void MP3Stream::readHeader() { if (_state != MP3_STATE_READY) return; // If necessary, load more data into the stream decoder if (_stream.error == MAD_ERROR_BUFLEN) readMP3Data(); while (_state != MP3_STATE_EOS) { _stream.error = MAD_ERROR_NONE; // Decode the next header. Note: mad_frame_decode would do this for us, too. // However, for seeking we don't want to decode the full frame (else it would // be far too slow). Hence we perform this explicitly in a separate step. if (mad_header_decode(&_frame.header, &_stream) == -1) { if (_stream.error == MAD_ERROR_BUFLEN) { readMP3Data(); // Read more data continue; } else if (MAD_RECOVERABLE(_stream.error)) { //status("MP3Stream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); continue; } else { warning("MP3Stream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); break; } } // Sum up the total playback time so far mad_timer_add(&_totalTime, _frame.header.duration); break; } if (_stream.error != MAD_ERROR_NONE) _state = MP3_STATE_EOS; }
// Seek in the stream, finding the next valid header void Mp3PspStream::findValidHeader() { DEBUG_ENTER_FUNC(); if (_state != MP3_STATE_READY) return; // If necessary, load more data into the stream decoder if (_stream.error == MAD_ERROR_BUFLEN) readMP3DataIntoBuffer(); while (_state != MP3_STATE_EOS) { _stream.error = MAD_ERROR_NONE; // Decode the next header. if (mad_header_decode(&_header, &_stream) == -1) { if (_stream.error == MAD_ERROR_BUFLEN) { readMP3DataIntoBuffer(); // Read more data continue; } else if (MAD_RECOVERABLE(_stream.error)) { debug(6, "MP3PSPStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); continue; } else { warning("MP3PSPStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); break; } } // Sum up the total playback time so far mad_timer_add(&_totalTime, _header.duration); break; } if (_stream.error != MAD_ERROR_NONE) _state = MP3_STATE_EOS; }
int TrackDuration::duration(const QFileInfo &fileinfo) { QString fn = fileinfo.absoluteFilePath(); if (fn.isEmpty()) return 0; QFile file(fn); if (!file.open(QFile::ReadOnly)) return 0; mad_stream infostream; mad_header infoheader; mad_timer_t infotimer; mad_stream_init(&infostream); mad_header_init(&infoheader); mad_timer_reset(&infotimer); qint64 r; qint64 l = 0; unsigned char* buf = new unsigned char[INPUT_BUFFER_SIZE]; while (!file.atEnd()) { if (l < INPUT_BUFFER_SIZE) { r = file.read(reinterpret_cast<char*>(buf) + l, INPUT_BUFFER_SIZE - l); l += r; } mad_stream_buffer(&infostream, buf, l); for (;;) { if (mad_header_decode(&infoheader, &infostream)) { if (!MAD_RECOVERABLE(infostream.error)) break; if (infostream.error == MAD_ERROR_LOSTSYNC) { TagLib::ID3v2::Header header; uint size = (uint)(infostream.bufend - infostream.this_frame); if (size >= header.size()) { header.setData(TagLib::ByteVector(reinterpret_cast<const char*>(infostream.this_frame), size)); uint tagsize = header.tagSize(); if (tagsize > 0) { mad_stream_skip(&infostream, qMin(tagsize, size)); continue; } } } qDebug() << "header decode error while getting file info" << infostream.error; continue; } mad_timer_add(&infotimer, infoheader.duration); } if (infostream.error != MAD_ERROR_BUFLEN && infostream.error != MAD_ERROR_BUFPTR) break; memmove(buf, infostream.next_frame, &(buf[l]) - infostream.next_frame); l -= (infostream.next_frame - buf); } mad_stream_finish(&infostream); mad_header_finish(&infoheader); delete[] buf; return timerToMs(&infotimer); }
static enum mp3_action decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag) { enum mad_layer layer; if ((data->stream).buffer == NULL || (data->stream).error == MAD_ERROR_BUFLEN) { if (!mp3_fill_buffer(data)) return DECODE_BREAK; } if (mad_header_decode(&data->frame.header, &data->stream)) { if ((data->stream).error == MAD_ERROR_LOSTSYNC && (data->stream).this_frame) { signed long tagsize = id3_tag_query((data->stream). this_frame, (data->stream). bufend - (data->stream). this_frame); if (tagsize > 0) { if (tag && !(*tag)) { mp3_parse_id3(data, (size_t)tagsize, tag); } else { mad_stream_skip(&(data->stream), tagsize); } return DECODE_CONT; } } if (MAD_RECOVERABLE((data->stream).error)) { return DECODE_SKIP; } else { if ((data->stream).error == MAD_ERROR_BUFLEN) return DECODE_CONT; else { g_warning("unrecoverable frame level error " "(%s).\n", mad_stream_errorstr(&data->stream)); return DECODE_BREAK; } } } layer = data->frame.header.layer; if (!data->layer) { if (layer != MAD_LAYER_II && layer != MAD_LAYER_III) { /* Only layer 2 and 3 have been tested to work */ return DECODE_SKIP; } data->layer = layer; } else if (layer != data->layer) { /* Don't decode frames with a different layer than the first */ return DECODE_SKIP; } return DECODE_OK; }
static void scan_file (FILE * fd, int *length, int *bitrate) { struct mad_stream stream; struct mad_header header; mad_timer_t timer; unsigned char buffer[8192]; unsigned int buflen = 0; mad_stream_init (&stream); mad_header_init (&header); timer = mad_timer_zero; while (1) { if (buflen < 8192) { int bytes = 0; bytes = fread (buffer + buflen, 1, 8192 - buflen, fd); if (bytes <= 0) break; buflen += bytes; } mad_stream_buffer (&stream, buffer, buflen); while (1) { if (mad_header_decode (&header, &stream) == -1) { if (!MAD_RECOVERABLE (stream.error)) break; continue; } if (length) mad_timer_add (&timer, header.duration); } if (stream.error != MAD_ERROR_BUFLEN) break; memmove (buffer, stream.next_frame, &buffer[buflen] - stream.next_frame); buflen -= stream.next_frame - &buffer[0]; SDL_Delay(1); } mad_header_finish (&header); mad_stream_finish (&stream); if (length) *length = mad_timer_count (timer, MAD_UNITS_MILLISECONDS); }
static const void *mp_registersong (music_player_t *music, const void *data, unsigned len) { mp_player_t *mp = (mp_player_t*)music; int i; int maxtry; int success = 0; // the MP3 standard doesn't include any global file header. the only way to tell filetype // is to start decoding stuff. you can't be too strict however because MP3 is resilient to // crap in the stream. // this routine is a bit slower than it could be, but apparently there are lots of files out // there with some dodgy stuff at the beginning. // if the stream begins with an ID3v2 magic, search hard and long for our first valid header if (memcmp (data, "ID3", 3) == 0) maxtry = 100; // otherwise, search for not so long else maxtry = 20; mad_stream_buffer (&mp->Stream, data, len); for (i = 0; i < maxtry; i++) { if (mad_header_decode (&mp->Header, &mp->Stream) != 0) { if (!MAD_RECOVERABLE (mp->Stream.error)) { lprintf (LO_WARN, "mad_registersong failed: %s\n", mad_stream_errorstr (&mp->Stream)); return NULL; } } else { success++; } } // 80% to pass if (success < maxtry * 8 / 10) { lprintf (LO_WARN, "mad_registersong failed\n"); return NULL; } lprintf (LO_INFO, "mad_registersong succeed. bitrate %lu samplerate %d\n", mp->Header.bitrate, mp->Header.samplerate); mp->mp_data = data; mp->mp_len = len; // handle not used return data; }
int LibMadWrapper::findValidHeader(struct mad_header &header) { int ret; while ((ret = mad_header_decode(&header, this->stream)) != 0 && MAD_RECOVERABLE(this->stream->error)) { if (this->stream->error == MAD_ERROR_LOSTSYNC) { long tagsize = id3_tag_query(this->stream->this_frame, this->stream->bufend - this->stream->this_frame); if (tagsize > 0) { mad_stream_skip(this->stream, tagsize); continue; } } } return ret; }
void LibMadWrapper::open() { // avoid multiple calls to open() if (this->infile != nullptr) { return; } this->infile = fopen(this->Filename.c_str(), "rb"); if (this->infile == nullptr) { THROW_RUNTIME_ERROR("fopen failed, errno: " << string(strerror(errno))); } int fd = fileno(this->infile); this->mpeglen = getFileSize(fd); this->mpegbuf = static_cast<unsigned char *>(mmap(nullptr, this->mpeglen, PROT_READ, MAP_SHARED, fd, 0)); if (this->mpegbuf == MAP_FAILED) { THROW_RUNTIME_ERROR("mmap failed for File \"" << this->Filename << ")\""); } this->stream = new struct mad_stream; mad_stream_init(this->stream); /* load buffer with MPEG audio data */ mad_stream_buffer(this->stream, this->mpegbuf, this->mpeglen); // we want to know how many pcm frames there are decoded in this file // therefore decode header of every mpeg frame // pretty expensive, so only to this once if (this->numFrames == 0) { struct mad_header header; mad_header_init(&header); // try to find a valid header int ret = this->findValidHeader(header); if (ret != 0) { // only free the locally used header here, this->stream and this->mpegbuf are freed in LibMadWrapper::close() mad_header_finish(&header); THROW_RUNTIME_ERROR("unable to find a valid frame-header for File \"" + this->Filename + "\""); } this->Format.SetVoices(1); // a first valid header is good, but it may contain garbage this->Format.VoiceChannels[0] = MAD_NCHANNELS(&header); this->Format.SampleRate = header.samplerate; CLOG(LogLevel_t::Debug, "found a first valid header within File \"" << this->Filename << "\"\n\tchannels: " << MAD_NCHANNELS(&header) << "\nsrate: " << header.samplerate); // no clue what this 32 does // stolen from mad_synth_frame() in synth.c this->numFrames += 32 * MAD_NSBSAMPLES(&header); // try to find a second valid header ret = this->findValidHeader(header); if (ret == 0) { // better use format infos from this header this->Format.VoiceChannels[0] = max<int>(MAD_NCHANNELS(&header), this->Format.VoiceChannels[0]); this->Format.SampleRate = header.samplerate; CLOG(LogLevel_t::Debug, "found a second valid header within File \"" << this->Filename << "\"\n\tchannels: " << MAD_NCHANNELS(&header) << "\nsrate: " << header.samplerate); this->numFrames += 32 * MAD_NSBSAMPLES(&header); // now lets go on and decode rest of file while (1) { if (mad_header_decode(&header, this->stream) != 0) { if (MAD_RECOVERABLE(this->stream->error)) { continue; } else { break; } } // sanity checks if (this->Format.Channels() != MAD_NCHANNELS(&header)) { CLOG(LogLevel_t::Warning, "channelcount varies (now: " << MAD_NCHANNELS(&header) << ") within File \"" << this->Filename << ")\""); if (!gConfig.MadPermissive) { THROW_RUNTIME_ERROR("invalid mp3: channelcount varies"); } } if (this->Format.SampleRate != header.samplerate) { CLOG(LogLevel_t::Warning, "samplerate varies (now: " << header.samplerate << ") within File \"" << this->Filename << ")\""); if (!gConfig.MadPermissive) { THROW_RUNTIME_ERROR("invalid mp3: samplerate varies"); } } this->numFrames += 32 * MAD_NSBSAMPLES(&header); } } else { CLOG(LogLevel_t::Warning, "only one valid header found, probably no valid mp3 File \"" << this->Filename << "\""); } // somehow reset libmad stream mad_stream_finish(this->stream); mad_stream_init(this->stream); /* load buffer with MPEG audio data */ mad_stream_buffer(this->stream, this->mpegbuf, this->mpeglen); mad_header_finish(&header); } this->frame.hasValue = true; mad_frame_init(&this->frame.Value); this->synth.hasValue = true; mad_synth_init(&this->synth.Value); }
/* Following two functions are adapted from mad_timer, from the libmad distribution */ void scan(void const *ptr, ssize_t len, buffer *buf) { struct mad_stream stream; struct mad_header header; struct xing xing; unsigned long bitrate = 0; int has_xing = 0; int is_vbr = 0; mad_stream_init(&stream); mad_header_init(&header); mad_stream_buffer(&stream, ptr, len); buf->num_frames = 0; /* There are three ways of calculating the length of an mp3: 1) Constant bitrate: One frame can provide the information needed: # of frames and duration. Just see how long it is and do the division. 2) Variable bitrate: Xing tag. It provides the number of frames. Each frame has the same number of samples, so just use that. 3) All: Count up the frames and duration of each frames by decoding each one. We do this if we've no other choice, i.e. if it's a VBR file with no Xing tag. */ while (1) { if (mad_header_decode(&header, &stream) == -1) { if (MAD_RECOVERABLE(stream.error)) continue; else break; } /* Limit xing testing to the first frame header */ if (!buf->num_frames++) { if(parse_xing(&xing, stream.anc_ptr, stream.anc_bitlen)) { is_vbr = 1; if (xing.flags & XING_FRAMES) { /* We use the Xing tag only for frames. If it doesn't have that information, it's useless to us and we have to treat it as a normal VBR file */ has_xing = 1; buf->num_frames = xing.frames; break; } } } /* Test the first n frames to see if this is a VBR file */ if (!is_vbr && !(buf->num_frames > 20)) { if (bitrate && header.bitrate != bitrate) { is_vbr = 1; } else { bitrate = header.bitrate; } } /* We have to assume it's not a VBR file if it hasn't already been marked as one and we've checked n frames for different bitrates */ else if (!is_vbr) { break; } mad_timer_add(&buf->duration, header.duration); } if (!is_vbr) { double time = (len * 8.0) / (header.bitrate); /* time in seconds */ double timefrac = (double)time - ((long)(time)); long nsamples = 32 * MAD_NSBSAMPLES(&header); /* samples per frame */ /* samplerate is a constant */ buf->num_frames = (long) (time * header.samplerate / nsamples); mad_timer_set(&buf->duration, (long)time, (long)(timefrac*100), 100); } else if (has_xing) { /* modify header.duration since we don't need it anymore */ mad_timer_multiply(&header.duration, buf->num_frames); buf->duration = header.duration; } else { /* the durations have been added up, and the number of frames counted. We do nothing here. */ } mad_header_finish(&header); mad_stream_finish(&stream); }
static int get_format(bgav_stream_t * s) { mad_priv_t * priv; const char * version_string; struct mad_header h; priv = s->decoder_priv; mad_header_init(&h); mad_header_decode(&h, &priv->stream); /* Get audio format and create frame */ s->data.audio.format.samplerate = h.samplerate; if(h.mode == MAD_MODE_SINGLE_CHANNEL) s->data.audio.format.num_channels = 1; else s->data.audio.format.num_channels = 2; s->data.audio.format.samplerate = h.samplerate; s->data.audio.format.sample_format = GAVL_SAMPLE_FLOAT; s->data.audio.format.interleave_mode = GAVL_INTERLEAVE_NONE; s->data.audio.format.samples_per_frame = MAD_NSBSAMPLES(&h) * 32; if(!s->codec_bitrate) { if(s->container_bitrate == GAVL_BITRATE_VBR) s->codec_bitrate = GAVL_BITRATE_VBR; else s->codec_bitrate = h.bitrate; } gavl_set_channel_setup(&s->data.audio.format); if(h.flags & MAD_FLAG_MPEG_2_5_EXT) { if(h.layer == 3) s->data.audio.preroll = s->data.audio.format.samples_per_frame * 30; else s->data.audio.preroll = s->data.audio.format.samples_per_frame; version_string = "2.5"; } else if(h.flags & MAD_FLAG_LSF_EXT) { if(h.layer == 3) s->data.audio.preroll = s->data.audio.format.samples_per_frame * 30; else s->data.audio.preroll = s->data.audio.format.samples_per_frame; version_string = "2"; } else { if(h.layer == 3) s->data.audio.preroll = s->data.audio.format.samples_per_frame * 10; else s->data.audio.preroll = s->data.audio.format.samples_per_frame; version_string = "1"; } gavl_metadata_set_nocpy(&s->m, GAVL_META_FORMAT, bgav_sprintf("MPEG-%s layer %d", version_string, h.layer)); priv->audio_frame = gavl_audio_frame_create(&s->data.audio.format); return 1; }
static int mad_frame_seek(input_object *obj, int frame) { struct mad_local_data *data; struct mad_header header; int skip; ssize_t byte_offset; if (!obj) return 0; data = (struct mad_local_data *)obj->local_data; if (!data || !data->seekable) return 0; //alsaplayer_error("frame_seek(..., %d)", frame); mad_header_init(&header); data->bytes_avail = 0; if (frame <= data->highest_frame) { skip = 0; if (frame > 4) { skip = 3; } byte_offset = data->frames[frame-skip]; /* Prepare the buffer for a read */ fill_buffer(data, byte_offset); mad_stream_buffer(&data->stream, data->mad_map, data->bytes_avail); skip++; while (skip != 0) { skip--; mad_frame_decode(&data->frame, &data->stream); if (skip == 0) mad_synth_frame (&data->synth, &data->frame); } data->bytes_avail = data->stream.bufend - data->stream.next_frame; data->current_frame = frame; data->seeking = 0; return data->current_frame; } data->seeking = 1; fill_buffer(data, data->frames[data->highest_frame]); mad_stream_buffer(&data->stream, data->mad_map, data->bytes_avail); while (data->highest_frame < frame) { if (data->bytes_avail < 3072) { fill_buffer(data, data->map_offset + MAD_BUFSIZE - data->bytes_avail); mad_stream_buffer(&data->stream, data->mad_map, data->bytes_avail); } if (mad_header_decode(&header, &data->stream) == -1) { if (!MAD_RECOVERABLE(data->stream.error)) { fill_buffer(data, 0); mad_stream_buffer(&data->stream, data->mad_map, data->bytes_avail); data->seeking = 0; return 0; } } data->frames[++data->highest_frame] = data->map_offset + data->stream.this_frame - data->mad_map; data->bytes_avail = data->stream.bufend - data->stream.next_frame; } data->current_frame = data->highest_frame; if (data->current_frame > 4) { skip = 3; fill_buffer(data, data->frames[data->current_frame-skip]); mad_stream_buffer(&data->stream, data->mad_map, data->bytes_avail); skip++; while (skip != 0) { skip--; mad_frame_decode(&data->frame, &data->stream); if (skip == 0) mad_synth_frame (&data->synth, &data->frame); data->bytes_avail = data->stream.bufend - data->stream.next_frame; } } data->seeking = 0; return data->current_frame; return 0; }
/* Handle first-stage decoding: extracting the MP3 frame data. */ int RageSoundReader_MP3::do_mad_frame_decode( bool headers_only ) { int bytes_read = 0; while(1) { int ret; /* Always actually decode the first packet, so we cleanly parse Xing tags. */ if( headers_only && !mad->first_frame ) ret=mad_header_decode( &mad->Frame.header,&mad->Stream ); else ret=mad_frame_decode( &mad->Frame,&mad->Stream ); if( ret == -1 && (mad->Stream.error == MAD_ERROR_BUFLEN || mad->Stream.error == MAD_ERROR_BUFPTR) ) { if( bytes_read > 25000 ) { /* We've read this much without actually getting a frame; error. */ SetError( "Can't find data" ); return -1; } ret = fill_buffer(); if( ret <= 0 ) return ret; bytes_read += ret; continue; } if( ret == -1 && mad->Stream.error == MAD_ERROR_LOSTSYNC ) { /* This might be an ID3V2 tag. */ const int tagsize = id3_tag_query(mad->Stream.this_frame, mad->Stream.bufend - mad->Stream.this_frame); if( tagsize ) { mad_stream_skip(&mad->Stream, tagsize); /* Don't count the tagsize against the max-read-per-call figure. */ bytes_read -= tagsize; continue; } } if( ret == -1 && mad->Stream.error == MAD_ERROR_BADDATAPTR ) { /* * Something's corrupt. One cause of this is cutting an MP3 in the middle * without reencoding; the first two frames will reference data from previous * frames that have been removed. The frame is valid--we can get a header from * it, we just can't synth useful data. * * BASS pretends the bad frames are silent. Emulate that, for compatibility. */ ret = 0; /* pretend success */ } if( !ret ) { /* OK. */ if( mad->first_frame ) { /* We're at the beginning. Is this a Xing tag? */ if(handle_first_frame()) { /* The first frame contained a header. Continue searching. */ continue; } /* We've decoded the first frame of data. * * We want mad->Timer to represent the timestamp of the first sample of the * currently decoded frame. Don't increment mad->Timer on the first frame, * or it'll be the time of the *next* frame. (All frames have the same * duration.) */ mad->first_frame = false; mad->Timer = mad_timer_zero; mad->header_bytes = get_this_frame_byte(mad); } else { mad_timer_add( &mad->Timer,mad->Frame.header.duration ); } fill_frame_index_cache( mad ); return 1; } if( mad->Stream.error == MAD_ERROR_BADCRC ) { /* XXX untested */ mad_frame_mute(&mad->Frame); mad_synth_mute(&mad->Synth); continue; } if( !MAD_RECOVERABLE(mad->Stream.error) ) { /* We've received an unrecoverable error. */ SetError( mad_stream_errorstr(&mad->Stream) ); return -1; } } }
static void MP3_getInfo() { int FrameCount = 0; struct mad_stream stream; struct mad_header header; mad_stream_init (&stream); mad_header_init (&header); MP3_info.fileSize = size; mad_timer_reset(&MP3_info.length); mad_stream_buffer (&stream, mp3_data, size); while (1){ if (mad_header_decode (&header, &stream) == -1){ if (MAD_RECOVERABLE(stream.error)){ continue; }else{ break; } } //Informazioni solo dal primo frame: if (FrameCount == 0){ switch (header.layer) { case MAD_LAYER_I: strcpy(MP3_info.layer,"I"); break; case MAD_LAYER_II: strcpy(MP3_info.layer,"II"); break; case MAD_LAYER_III: strcpy(MP3_info.layer,"III"); break; default: strcpy(MP3_info.layer,"unknown"); break; } MP3_info.kbit = header.bitrate / 1000; MP3_info.hz = header.samplerate; switch (header.mode) { case MAD_MODE_SINGLE_CHANNEL: strcpy(MP3_info.mode, "single channel"); break; case MAD_MODE_DUAL_CHANNEL: strcpy(MP3_info.mode, "dual channel"); break; case MAD_MODE_JOINT_STEREO: strcpy(MP3_info.mode, "joint (MS/intensity) stereo"); break; case MAD_MODE_STEREO: strcpy(MP3_info.mode, "normal LR stereo"); break; default: strcpy(MP3_info.mode, "unknown"); break; } switch (header.emphasis) { case MAD_EMPHASIS_NONE: strcpy(MP3_info.emphasis,"no"); break; case MAD_EMPHASIS_50_15_US: strcpy(MP3_info.emphasis,"50/15 us"); break; case MAD_EMPHASIS_CCITT_J_17: strcpy(MP3_info.emphasis,"CCITT J.17"); break; case MAD_EMPHASIS_RESERVED: strcpy(MP3_info.emphasis,"reserved(!)"); break; default: strcpy(MP3_info.emphasis,"unknown"); break; } } FrameCount++; mad_timer_add (&MP3_info.length, header.duration); } mad_header_finish (&header); mad_stream_finish (&stream); MP3_info.frames = FrameCount; mad_timer_string(MP3_info.length, MP3_info.strLength, "%02lu:%02u:%02u", MAD_UNITS_HOURS, MAD_UNITS_MILLISECONDS, 0); }
static int run_sync(struct mad_decoder *decoder) { /* if (decoder->input_func == 0) return 0; if (decoder->error_func) { error_func = decoder->error_func; //error_data = decoder->cb_data; } else { error_func = error_default; error_data = &bad_last_frame; } stream = &decoder->sync->stream; frame = &decoder->sync->frame; synth = &decoder->sync->synth; mad_stream_init(stream); mad_frame_init(frame); mad_synth_init(synth); mad_stream_options(stream, decoder->options); */ do { switch (decoder->input_func(decoder->cb_data, stream)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: continue; case MAD_FLOW_CONTINUE: break; } while (1) { # if defined(USE_ASYNC) if (decoder->mode == MAD_DECODER_MODE_ASYNC) { switch (check_message(decoder)) { case MAD_FLOW_IGNORE: case MAD_FLOW_CONTINUE: break; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_STOP: goto done; } } # endif if (decoder->header_func) { if (mad_header_decode(&frame->header, stream) == -1) { if (!MAD_RECOVERABLE(stream->error)) break; switch (error_func(decoder->cb_data, stream, frame)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: case MAD_FLOW_CONTINUE: default: continue; } } switch (decoder->header_func(decoder->cb_data, &frame->header)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: continue; case MAD_FLOW_CONTINUE: break; } } if (mad_frame_decode(frame, stream) == -1) { if (!MAD_RECOVERABLE(stream->error)) break; switch (error_func(decoder->cb_data, stream, frame)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: break; case MAD_FLOW_CONTINUE: default: continue; } } else bad_last_frame = 0; if (decoder->filter_func) { switch (decoder->filter_func(decoder->cb_data, stream, frame)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: continue; case MAD_FLOW_CONTINUE: break; } } mad_synth_frame(synth, frame); if (decoder->output_func) { switch (decoder->output_func(decoder->cb_data, &frame->header, &synth->pcm)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: case MAD_FLOW_CONTINUE: break; } } } } while (stream->error == MAD_ERROR_BUFLEN); fail: result = -1; done: /* mad_synth_finish(synth); mad_frame_finish(frame); mad_stream_finish(stream); */ //return result; return stream->this_frame - stream->buffer; }
static int run_sync(struct mad_decoder *decoder) { enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *); void *error_data; int bad_last_frame = 0; struct mad_stream *stream; struct mad_frame *frame; struct mad_synth *synth; int result = 0; if (decoder->input_func == 0) return 0; if (decoder->error_func) { error_func = decoder->error_func; error_data = decoder->cb_data; } else { error_func = error_default; error_data = &bad_last_frame; } stream = &decoder->sync->stream; frame = &decoder->sync->frame; synth = &decoder->sync->synth; mad_stream_init(stream); mad_frame_init(frame); mad_synth_init(synth); mad_stream_options(stream, decoder->options); do { switch (decoder->input_func(decoder->cb_data, stream)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: continue; case MAD_FLOW_CONTINUE: break; } while (1) { if (decoder->header_func) { if (mad_header_decode(&frame->header, stream) == -1) { if (!MAD_RECOVERABLE(stream->error)) break; switch (error_func(error_data, stream, frame)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: case MAD_FLOW_CONTINUE: default: continue; } } switch (decoder->header_func(decoder->cb_data, &frame->header)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: continue; case MAD_FLOW_CONTINUE: break; } } if (mad_frame_decode(frame, stream) == -1) { if (!MAD_RECOVERABLE(stream->error)) break; switch (error_func(error_data, stream, frame)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: break; case MAD_FLOW_CONTINUE: default: continue; } } else bad_last_frame = 0; if (decoder->filter_func) { switch (decoder->filter_func(decoder->cb_data, stream, frame)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: continue; case MAD_FLOW_CONTINUE: break; } } mad_synth_frame(synth, frame); if (decoder->output_func) { switch (decoder->output_func(decoder->cb_data, &frame->header, &synth->pcm)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: case MAD_FLOW_CONTINUE: break; } } } } while (stream->error == MAD_ERROR_BUFLEN); fail: result = -1; done: mad_synth_finish(synth); mad_frame_finish(frame); mad_stream_finish(stream); return result; }
//Get info on file: //Uso LibMad per calcolare la durata del pezzo perché //altrimenti dovrei gestire il buffer anche nella seekNextFrame (senza è troppo lenta). //E' una porcheria ma è più semplice. :) int MP3MEgetInfo(){ unsigned long FrameCount = 0; int fd = -1; int bufferSize = 1024*496; u8 *localBuffer; long singleDataRed = 0; struct mad_stream stream; struct mad_header header; int timeFromID3 = 0; float mediumBitrate = 0.0f; int has_xing = 0; struct xing xing; memset(&xing, 0, sizeof(xing)); if (!MP3ME_tagRead) getMP3METagInfo(MP3ME_fileName, &MP3ME_info); mad_stream_init (&stream); mad_header_init (&header); fd = sceIoOpen(MP3ME_fileName, PSP_O_RDONLY, 0777); if (fd < 0) return -1; long size = sceIoLseek(fd, 0, PSP_SEEK_END); sceIoLseek(fd, 0, PSP_SEEK_SET); MP3ME_tagsize = ID3v2TagSize(MP3ME_fileName); double startPos = MP3ME_tagsize; sceIoLseek32(fd, startPos, PSP_SEEK_SET); //Check for xing frame: unsigned char *xing_buffer; xing_buffer = (unsigned char *)malloc(XING_BUFFER_SIZE); if (xing_buffer != NULL) { sceIoRead(fd, xing_buffer, XING_BUFFER_SIZE); if(parse_xing(xing_buffer, 0, &xing)) { if (xing.flags & XING_FRAMES && xing.frames){ has_xing = 1; bufferSize = 50 * 1024; } } free(xing_buffer); xing_buffer = NULL; } size -= startPos; if (size < bufferSize * 3) bufferSize = size; localBuffer = (unsigned char *) malloc(sizeof(unsigned char) * bufferSize); unsigned char *buff = localBuffer; MP3ME_info.fileType = MP3_TYPE; MP3ME_info.defaultCPUClock = MP3ME_defaultCPUClock; MP3ME_info.needsME = 1; MP3ME_info.fileSize = size; MP3ME_filesize = size; MP3ME_info.framesDecoded = 0; double totalBitrate = 0; int i = 0; for (i=0; i<3; i++){ memset(localBuffer, 0, bufferSize); singleDataRed = sceIoRead(fd, localBuffer, bufferSize); mad_stream_buffer (&stream, localBuffer, singleDataRed); while (1){ if (mad_header_decode (&header, &stream) == -1){ if (stream.buffer == NULL || stream.error == MAD_ERROR_BUFLEN) break; else if (MAD_RECOVERABLE(stream.error)){ continue; }else{ break; } } //Informazioni solo dal primo frame: if (FrameCount++ == 0){ switch (header.layer) { case MAD_LAYER_I: strcpy(MP3ME_info.layer,"I"); break; case MAD_LAYER_II: strcpy(MP3ME_info.layer,"II"); break; case MAD_LAYER_III: strcpy(MP3ME_info.layer,"III"); break; default: strcpy(MP3ME_info.layer,"unknown"); break; } MP3ME_info.kbit = header.bitrate / 1000; MP3ME_info.instantBitrate = header.bitrate; MP3ME_info.hz = header.samplerate; switch (header.mode) { case MAD_MODE_SINGLE_CHANNEL: strcpy(MP3ME_info.mode, "single channel"); break; case MAD_MODE_DUAL_CHANNEL: strcpy(MP3ME_info.mode, "dual channel"); break; case MAD_MODE_JOINT_STEREO: strcpy(MP3ME_info.mode, "joint (MS/intensity) stereo"); break; case MAD_MODE_STEREO: strcpy(MP3ME_info.mode, "normal LR stereo"); break; default: strcpy(MP3ME_info.mode, "unknown"); break; } switch (header.emphasis) { case MAD_EMPHASIS_NONE: strcpy(MP3ME_info.emphasis,"no"); break; case MAD_EMPHASIS_50_15_US: strcpy(MP3ME_info.emphasis,"50/15 us"); break; case MAD_EMPHASIS_CCITT_J_17: strcpy(MP3ME_info.emphasis,"CCITT J.17"); break; case MAD_EMPHASIS_RESERVED: strcpy(MP3ME_info.emphasis,"reserved(!)"); break; default: strcpy(MP3ME_info.emphasis,"unknown"); break; } //Check if lenght found in tag info: if (MP3ME_info.length > 0){ timeFromID3 = 1; break; } if (has_xing) break; } totalBitrate += header.bitrate; } if (size == bufferSize) break; else if (i==0) sceIoLseek(fd, startPos + size/3, PSP_SEEK_SET); else if (i==1) sceIoLseek(fd, startPos + 2 * size/3, PSP_SEEK_SET); if (timeFromID3 || has_xing) break; } mad_header_finish (&header); mad_stream_finish (&stream); if (buff){ free(buff); buff = NULL; } sceIoClose(fd); int secs = 0; if (has_xing) { /* modify header.duration since we don't need it anymore */ mad_timer_multiply(&header.duration, xing.frames); secs = mad_timer_count(header.duration, MAD_UNITS_SECONDS); MP3ME_info.length = secs; } else if (!MP3ME_info.length){ mediumBitrate = totalBitrate / (float)FrameCount; secs = size * 8 / mediumBitrate; MP3ME_info.length = secs; }else{ secs = MP3ME_info.length; } //Formatto in stringa la durata totale: int h = secs / 3600; int m = (secs - h * 3600) / 60; int s = secs - h * 3600 - m * 60; snprintf(MP3ME_info.strLength, sizeof(MP3ME_info.strLength), "%2.2i:%2.2i:%2.2i", h, m, s); return 0; }
static int himd_mp3stream_split_frames(struct himd_mp3stream * stream, unsigned int databytes, unsigned int firstframe, unsigned int lastframe, struct himderrinfo * status) { int gotdata = 1; unsigned int i; struct mad_stream madstream; struct mad_header madheader; /* stream->frameptrs is NULL if the current frame has not been splitted yet */ g_warn_if_fail(stream->frameptrs == NULL); stream->frameptrs = malloc((lastframe - firstframe + 2) * sizeof stream->frameptrs[0]); if(!stream->frameptrs) { set_status_printf(status, HIMD_ERROR_OUT_OF_MEMORY, _("Can't allocate memory for %u frame pointers"), lastframe-firstframe+2); return -1; } /* parse block */ mad_stream_init(&madstream); mad_header_init(&madheader); mad_stream_buffer(&madstream, &stream->blockbuf[0x20], databytes+MAD_BUFFER_GUARD); /* drop unneeded frames in front */ while(firstframe > 0) { if(mad_header_decode(&madheader, &madstream) < 0) { set_status_printf(status, HIMD_ERROR_BAD_DATA_FORMAT, _("Still %u frames to skip: %s"), firstframe, mad_stream_errorstr(&madstream)); gotdata = 0; goto cleanup_decoder; } firstframe--; lastframe--; } /* store needed frames */ for(i = 0;i <= lastframe;i++) { if(mad_header_decode(&madheader, &madstream) < 0 && (madstream.error != MAD_ERROR_LOSTSYNC || i != lastframe)) { set_status_printf(status, HIMD_ERROR_BAD_DATA_FORMAT, _("Frame %u of %u to store: %s"), i+1, lastframe, mad_stream_errorstr(&madstream)); gotdata = 0; goto cleanup_decoder; } stream->frameptrs[i] = madstream.this_frame; } stream->frameptrs[i] = madstream.next_frame; stream->frames = lastframe+1; stream->curframe = 0; cleanup_decoder: mad_header_finish(&madheader); mad_stream_finish(&madstream); if(!gotdata) return -1; return 0; }
// This is a shorter and faster version of scanFile(), that only scans the samplerate and number of Channels of the stream. void mpgDecoder::readMetaFromFile( playListItem * pli ) { #ifdef HAVE_MAD // use QFile as input Buffer, which is needed by libMad, cause it cannot detect EOF.... QFile madFile(pli->getFile()); madFile.open( QIODevice::ReadOnly ); unsigned char buffer[65536]; mad_stream scanStream; mad_header scanHeader; mad_stream_init (&scanStream); mad_header_init (&scanHeader); // get some more Byte from File... int readCnt = 0; while( !madFile.atEnd() && readCnt < 65536 ) { buffer[readCnt] = madFile.read(1).at(0); readCnt++; } if (madFile.atEnd()) return; mad_stream_buffer (&scanStream, buffer, readCnt ); while (1) { if (mad_header_decode (&scanHeader, &scanStream) == -1) { if (scanStream.error == MAD_ERROR_BUFLEN) break; if (!MAD_RECOVERABLE (scanStream.error)) break; continue; } } pli->setSamplerate( scanHeader.samplerate ); pli->setChannels( MAD_NCHANNELS(&scanHeader) ); mad_header_finish (&scanHeader); mad_stream_finish (&scanStream); #endif // read ID3 Metadata #ifdef HAVE_ID3TAG id3_file* id3f = id3_file_open( pli->getFile().toAscii(), ID3_FILE_MODE_READONLY ); id3_tag* id3 = id3_file_tag( id3f ); // only overwrite metadata of playlist-item if files contains own ones... QString temp = getID3String(id3, ID3_FRAME_ARTIST); if(!temp.isEmpty()) pli->setArtist( temp ); temp = getID3String(id3, ID3_FRAME_TITLE); if(!temp.isEmpty()) pli->setTitle( temp ); temp = getID3String(id3, ID3_FRAME_GENRE); if(!temp.isEmpty()) pli->setGenre( temp ); id3_file_close( id3f ); #endif }
// the following function has been copied from XMMS' mad input plugin // Copyright (C) 2001-2002 Sam Clegg void mpgDecoder::scanFile( ) { #ifdef HAVE_MAD mad_stream scanStream; mad_header scanHeader; int remainder = 0; int data_used = 0; int pos = 0; unsigned char buffer[4096]; // reset file, so we can read from the beginning madFile->reset(); mad_timer_reset( endPositionTimer ); mad_stream_init (&scanStream); mad_header_init (&scanHeader); while (1) { remainder = scanStream.bufend - scanStream.next_frame; memcpy (buffer, scanStream.this_frame, remainder); // get some more Byte from File... int readCnt = 0; unsigned char* readStart; readStart = buffer+remainder; while( !madFile->atEnd() && readCnt < (4096 - remainder) ) { readStart[readCnt] = madFile->read(1).at(0); readCnt++; } if (madFile->atEnd()) break; mad_stream_buffer (&scanStream, buffer, readCnt + remainder); while (1) { if (mad_header_decode (&scanHeader, &scanStream) == -1) { if (scanStream.error == MAD_ERROR_BUFLEN) { break; } if (!MAD_RECOVERABLE (scanStream.error)) { break; } continue; } pos++; data_used += scanStream.next_frame - scanStream.this_frame; mad_timer_add( endPositionTimer, scanHeader.duration ); if (pos == 1) { sampleRate = scanHeader.samplerate; channels = MAD_NCHANNELS(&scanHeader); } } if (scanStream.error != MAD_ERROR_BUFLEN) break; } mad_header_finish (&scanHeader); mad_stream_finish (&scanStream); // reset the file again, so we can read from the beginning when playing madFile->reset(); #endif }
int64 MadDecoder::getDurationMs(unsigned char* buffer, size_t bufferSize) { struct mad_stream madStream; struct mad_frame madFrame; struct mad_header madHeader; mad_timer_t time = mad_timer_zero; bool depadded = false; bool vbr = false; size_t tagsize = 0; size_t consumed = 0; size_t numFrames = 0; size_t initialBitrate = 0; mad_stream_init(&madStream); mad_header_init(&madHeader); mad_frame_init(&madFrame); do // Read data from the MP3 file { int padding = 0; size_t leftover = madStream.bufend - madStream.next_frame; memcpy(buffer, madStream.this_frame, leftover); int bytesRead = fread(buffer + leftover, (size_t)1, bufferSize - leftover, handle_); if (bytesRead <= 0) { break; } for (; !depadded && padding < bytesRead && !buffer[padding]; ++padding); depadded = true; mad_stream_buffer(&madStream, buffer + padding, leftover + bytesRead - padding); while (true) // decode frame headers { madStream.error = MAD_ERROR_NONE; if (mad_header_decode(&madHeader, &madStream) == -1) { if (madStream.error == MAD_ERROR_BUFLEN) // Normal behaviour; get some more data from the file break; if (MAD_RECOVERABLE(madStream.error) == 0) break; if (madStream.error == MAD_ERROR_LOSTSYNC) { unsigned available = (madStream.bufend - madStream.this_frame); tagsize = getId3TagSize(madStream.this_frame, (size_t)available); if (tagsize) // It's some ID3 tags, so just skip { if (tagsize >= available) { _fseeki64(handle_, (int64)(tagsize - available), SEEK_CUR); depadded = false; } mad_stream_skip(&madStream, std::min(tagsize, available)); } } continue; // not an audio frame } mad_timer_add(&time, madHeader.duration); consumed += madStream.next_frame - madStream.this_frame; if (numFrames == 0) { initialBitrate = madHeader.bitrate; // Get the precise frame count from the XING header if present madFrame.header = madHeader; if (mad_frame_decode(&madFrame, &madStream) == -1) { if (MAD_RECOVERABLE(madStream.error) == 0) { break; } } if ((numFrames = xingFrames(madStream.anc_ptr, madStream.anc_bitlen))) { mad_timer_multiply(&time, (signed long)numFrames); break; } } else { vbr |= madHeader.bitrate != initialBitrate; } // If not VBR, we can time just a few frames then extrapolate (not exact!) if (++numFrames == 25 && !vbr) { struct stat st; fstat(fileno(handle_), &st); timerMultiply(&time, (double)(st.st_size - tagsize) / consumed); break; } } // while(true) } while (madStream.error == MAD_ERROR_BUFLEN); mad_frame_finish(&madFrame); mad_header_finish(&madHeader); mad_stream_finish(&madStream); rewind(handle_); return mad_timer_count(time, MAD_UNITS_MILLISECONDS); }
/** * 搜索下一个有效MP3 frame * * @return * - <0 失败 * - 0 成功 */ static int seek_valid_frame(void) { int cnt = 0; int ret; mad_stream_finish(&stream); mad_stream_init(&stream); do { cnt++; if (stream.buffer == NULL || stream.error == MAD_ERROR_BUFLEN) { size_t read_size, remaining = 0; uint8_t *read_start; int bufsize; if (stream.next_frame != NULL) { remaining = stream.bufend - stream.next_frame; memmove(g_input_buff, stream.next_frame, remaining); read_start = g_input_buff + remaining; read_size = BUFF_SIZE - remaining; } else { read_size = BUFF_SIZE; read_start = g_input_buff; remaining = 0; } if (mp3_data.use_buffer) bufsize = buffered_reader_read(mp3_data.r, read_start, read_size); else bufsize = xrIoRead(mp3_data.fd, read_start, read_size); if (bufsize <= 0) { return -1; } if (bufsize < read_size) { uint8_t *guard = read_start + read_size; memset(guard, 0, MAD_BUFFER_GUARD); read_size += MAD_BUFFER_GUARD; } mad_stream_buffer(&stream, g_input_buff, read_size + remaining); stream.error = 0; } if ((ret = mad_header_decode(&frame.header, &stream)) == -1) { /* * MAD_ERROR_BUFLEN should be ignored * * We haven't reached the EOF as long as xrIoRead returned positive. */ if (!MAD_RECOVERABLE(stream.error) && stream.error != MAD_ERROR_BUFLEN) { return -1; } } else { ret = 0; stream.error = 0; } } while (!(ret == 0 && stream.sync == 1)); dbg_printf(d, "%s: tried %d times", __func__, cnt); return 0; }
static int mad_open(input_object *obj, const char *path) { struct mad_local_data *data; char *p; int mode; if (!obj) return 0; obj->local_data = malloc(sizeof(struct mad_local_data)); if (!obj->local_data) { puts("failed to allocate local data"); return 0; } data = (struct mad_local_data *)obj->local_data; memset(data, 0, sizeof(struct mad_local_data)); if ((data->mad_fd = reader_open(path, &reader_status, obj)) == NULL) { fprintf(stderr, "mad_open(obj, %s) failed\n", path); free(obj->local_data); obj->local_data = NULL; return 0; } obj->flags = 0; if (strncasecmp(path, "http://", 7) == 0) { obj->flags |= P_STREAMBASED; strcpy(data->sinfo.status, "Prebuffering"); } else { obj->flags |= P_FILEBASED; } if (!reader_seekable(data->mad_fd)) { data->seekable = 0; } else { obj->flags |= P_SEEK; obj->flags |= P_PERFECTSEEK; data->seekable = 1; } obj->flags |= P_REENTRANT; mad_init_decoder(data); memset(&data->xing, 0, sizeof(struct xing)); xing_init (&data->xing); data->mad_init = 1; fill_buffer(data, -1); //alsaplayer_error("initial bytes_avail = %d", data->bytes_avail); if (obj->flags & P_PERFECTSEEK) { data->offset = find_initial_frame(data->mad_map, data->bytes_avail < STREAM_BUFFER_SIZE ? data->bytes_avail : STREAM_BUFFER_SIZE); } else { data->offset = 0; } data->highest_frame = 0; if (data->offset < 0) { //fprintf(stderr, "mad_open() couldn't find valid MPEG header\n"); data->offset = 0; } //alsaplayer_error("data->offset = %d", data->offset); if (data->offset > data->bytes_avail) { data->seekable = 1; //alsaplayer_error("Need to refill buffer (data->offset = %d)", data->offset); fill_buffer(data, 0); mad_stream_buffer(&data->stream, data->mad_map, data->bytes_avail); } else { mad_stream_buffer(&data->stream, data->mad_map + data->offset, data->bytes_avail - data->offset); data->bytes_avail -= data->offset; } first_frame: if ((mad_header_decode(&data->frame.header, &data->stream) == -1)) { switch (data->stream.error) { case MAD_ERROR_BUFLEN: return 0; case MAD_ERROR_LOSTSYNC: case MAD_ERROR_BADEMPHASIS: case MAD_ERROR_BADBITRATE: case MAD_ERROR_BADLAYER: case MAD_ERROR_BADSAMPLERATE: //alsaplayer_error("Error %x (frame %d)", data->stream.error, data->current_frame); data->bytes_avail-=(data->stream.next_frame - data->stream.this_frame); goto first_frame; break; case MAD_ERROR_BADBITALLOC: return 0; case MAD_ERROR_BADCRC: alsaplayer_error("MAD_ERROR_BADCRC: %s", error_str(data->stream.error, data->str)); case MAD_ERROR_BADBIGVALUES: case MAD_ERROR_BADDATAPTR: break; default: alsaplayer_error("ERROR: %s", error_str(data->stream.error, data->str)); alsaplayer_error("No valid frame found at start (pos: %d, error: 0x%x --> %x %x %x %x) (%s)", data->offset, data->stream.error, data->stream.this_frame[0], data->stream.this_frame[1], data->stream.this_frame[2], data->stream.this_frame[3],path); return 0; } } mad_frame_decode(&data->frame, &data->stream); /* alsaplayer_error("xing parsing...%x %x %x %x (%x %d)", data->stream.this_frame[0], data->stream.this_frame[1], data->stream.this_frame[2], data->stream.this_frame[3], data->stream.anc_ptr, data->stream.anc_bitlen); */ if (xing_parse(&data->xing, data->stream.anc_ptr, data->stream.anc_bitlen) == 0) { // We use the xing data later on } mode = (data->frame.header.mode == MAD_MODE_SINGLE_CHANNEL) ? 1 : 2; data->samplerate = data->frame.header.samplerate; data->bitrate = data->frame.header.bitrate; mad_synth_frame (&data->synth, &data->frame); { struct mad_pcm *pcm = &data->synth.pcm; obj->nr_channels = pcm->channels; //alsaplayer_error("nr_channels = %d", obj->nr_channels); } //alsaplayer_error("Initial: %d, %d, %d", data->samplerate, data->bitrate, obj->nr_channels); /* Calculate some values */ data->bytes_avail = data->stream.bufend - data->stream.next_frame; { int64_t time; int64_t samples; int64_t frames; long oldpos = reader_tell(data->mad_fd); reader_seek(data->mad_fd, 0, SEEK_END); data->filesize = reader_tell(data->mad_fd); data->filesize -= data->offset; reader_seek(data->mad_fd, oldpos, SEEK_SET); if (data->bitrate) time = (data->filesize * 8) / (data->bitrate); else time = 0; samples = 32 * MAD_NSBSAMPLES(&data->frame.header); obj->frame_size = (int) samples << 2; /* Assume 16-bit stereo */ frames = data->samplerate * (time+1) / samples; if (data->xing.flags & XING_FRAMES) { obj->nr_frames = data->xing.frames; } else { obj->nr_frames = (int) frames; } obj->nr_tracks = 1; } /* Determine if nr_frames makes sense */ if (!(obj->flags & P_SEEK) && (obj->flags & P_STREAMBASED)) { obj->nr_frames = -1; } /* Allocate frame index */ if (!data->seekable || obj->nr_frames > 1000000 || (data->frames = (ssize_t *)malloc((obj->nr_frames + FRAME_RESERVE) * sizeof(ssize_t))) == NULL) { data->seekable = 0; // Given really } else { data->seekable = 1; data->frames[0] = 0; } data->mad_init = 1; p = strrchr(path, '/'); if (p) { strcpy(data->filename, ++p); } else { strcpy(data->filename, path); } strcpy(data->path, path); data->parse_id3 = prefs_get_bool(ap_prefs, "mad", "parse_id3", 1); return 1; }
/** * MP3音乐播放回调函数, * 负责将解码数据填充声音缓存区 * * @note 声音缓存区的格式为双声道,16位低字序 * * @param buf 声音缓冲区指针 * @param reqn 缓冲区帧大小 * @param pdata 用户数据,无用 */ static int mp3_audiocallback(void *buf, unsigned int reqn, void *pdata) { int avail_frame; int snd_buf_frame_size = (int) reqn; int ret; double incr; signed short *audio_buf = buf; unsigned i; uint16_t *output; UNUSED(pdata); if (g_status != ST_PLAYING) { if (handle_seek() == -1) { __end(); return -1; } xAudioClearSndBuf(buf, snd_buf_frame_size); xrKernelDelayThread(100000); return 0; } while (snd_buf_frame_size > 0) { avail_frame = g_buff_frame_size - g_buff_frame_start; if (avail_frame >= snd_buf_frame_size) { send_to_sndbuf(audio_buf, &g_buff[g_buff_frame_start * 2], snd_buf_frame_size, 2); g_buff_frame_start += snd_buf_frame_size; audio_buf += snd_buf_frame_size * 2; snd_buf_frame_size = 0; } else { send_to_sndbuf(audio_buf, &g_buff[g_buff_frame_start * 2], avail_frame, 2); snd_buf_frame_size -= avail_frame; audio_buf += avail_frame * 2; if (stream.buffer == NULL || stream.error == MAD_ERROR_BUFLEN) { size_t read_size, remaining = 0; uint8_t *read_start; int bufsize; if (stream.next_frame != NULL) { remaining = stream.bufend - stream.next_frame; memmove(g_input_buff, stream.next_frame, remaining); read_start = g_input_buff + remaining; read_size = BUFF_SIZE - remaining; } else { read_size = BUFF_SIZE; read_start = g_input_buff; remaining = 0; } if (mp3_data.use_buffer) bufsize = buffered_reader_read(mp3_data.r, read_start, read_size); else bufsize = xrIoRead(mp3_data.fd, read_start, read_size); if (bufsize <= 0) { __end(); return -1; } if (bufsize < read_size) { uint8_t *guard = read_start + read_size; memset(guard, 0, MAD_BUFFER_GUARD); read_size += MAD_BUFFER_GUARD; } mad_stream_buffer(&stream, g_input_buff, read_size + remaining); stream.error = 0; } ret = mad_frame_decode(&frame, &stream); if (ret == -1) { if (MAD_RECOVERABLE(stream.error) || stream.error == MAD_ERROR_BUFLEN) { if (stream.error == MAD_ERROR_LOSTSYNC) { long tagsize = id3_tag_query(stream.this_frame, stream.bufend - stream.this_frame); if (tagsize > 0) { mad_stream_skip(&stream, tagsize); } if (mad_header_decode(&frame.header, &stream) == -1) { if (stream.error != MAD_ERROR_BUFLEN) { if (!MAD_RECOVERABLE(stream.error)) { __end(); return -1; } } } else { stream.error = MAD_ERROR_NONE; } } g_buff_frame_size = 0; g_buff_frame_start = 0; continue; } else { __end(); return -1; } } output = &g_buff[0]; if (stream.error != MAD_ERROR_NONE) { continue; } mad_synth_frame(&synth, &frame); for (i = 0; i < synth.pcm.length; i++) { signed short sample; if (MAD_NCHANNELS(&frame.header) == 2) { /* Left channel */ sample = MadFixedToSshort(synth.pcm.samples[0][i]); *(output++) = sample; sample = MadFixedToSshort(synth.pcm.samples[1][i]); *(output++) = sample; } else { sample = MadFixedToSshort(synth.pcm.samples[0][i]); *(output++) = sample; *(output++) = sample; } } g_buff_frame_size = synth.pcm.length; g_buff_frame_start = 0; incr = frame.header.duration.seconds; incr += mad_timer_fraction(frame.header.duration, MAD_UNITS_MILLISECONDS) / 1000.0; g_play_time += incr; add_bitrate(&g_inst_br, frame.header.bitrate, incr); } } return 0; }
static int count_time_internal (struct mp3_data *data) { struct xing xing; unsigned long bitrate = 0; int has_xing = 0; int is_vbr = 0; int num_frames = 0; mad_timer_t duration = mad_timer_zero; struct mad_header header; int good_header = 0; /* Have we decoded any header? */ mad_header_init (&header); xing_init (&xing); /* There are three ways of calculating the length of an mp3: 1) Constant bitrate: One frame can provide the information needed: # of frames and duration. Just see how long it is and do the division. 2) Variable bitrate: Xing tag. It provides the number of frames. Each frame has the same number of samples, so just use that. 3) All: Count up the frames and duration of each frames by decoding each one. We do this if we've no other choice, i.e. if it's a VBR file with no Xing tag. */ while (1) { /* Fill the input buffer if needed */ if (data->stream.buffer == NULL || data->stream.error == MAD_ERROR_BUFLEN) { if (!fill_buff(data)) break; } if (mad_header_decode(&header, &data->stream) == -1) { if (MAD_RECOVERABLE(data->stream.error)) continue; else if (data->stream.error == MAD_ERROR_BUFLEN) continue; else { debug ("Can't decode header: %s", mad_stream_errorstr( &data->stream)); break; } } good_header = 1; /* Limit xing testing to the first frame header */ if (!num_frames++) { if (xing_parse(&xing, data->stream.anc_ptr, data->stream.anc_bitlen) != -1) { is_vbr = 1; debug ("Has XING header"); if (xing.flags & XING_FRAMES) { has_xing = 1; num_frames = xing.frames; break; } debug ("XING header doesn't contain number of " "frames."); } } /* Test the first n frames to see if this is a VBR file */ if (!is_vbr && !(num_frames > 20)) { if (bitrate && header.bitrate != bitrate) { debug ("Detected VBR after %d frames", num_frames); is_vbr = 1; } else bitrate = header.bitrate; } /* We have to assume it's not a VBR file if it hasn't already * been marked as one and we've checked n frames for different * bitrates */ else if (!is_vbr) { debug ("Fixed rate MP3"); break; } mad_timer_add (&duration, header.duration); } if (!good_header) return -1; if (!is_vbr) { /* time in seconds */ double time = (data->size * 8.0) / (header.bitrate); double timefrac = (double)time - ((long)(time)); /* samples per frame */ long nsamples = 32 * MAD_NSBSAMPLES(&header); /* samplerate is a constant */ num_frames = (long) (time * header.samplerate / nsamples); /* the average bitrate is the constant bitrate */ data->avg_bitrate = bitrate; mad_timer_set(&duration, (long)time, (long)(timefrac*100), 100); } else if (has_xing) { mad_timer_multiply (&header.duration, num_frames); duration = header.duration; } else { /* the durations have been added up, and the number of frames counted. We do nothing here. */ debug ("Counted duration by counting frames durations in " "VBR file."); } if (data->avg_bitrate == -1 && mad_timer_count(duration, MAD_UNITS_SECONDS) > 0) { data->avg_bitrate = data->size / mad_timer_count(duration, MAD_UNITS_SECONDS) * 8; } mad_header_finish(&header); debug ("MP3 time: %ld", mad_timer_count (duration, MAD_UNITS_SECONDS)); return mad_timer_count (duration, MAD_UNITS_SECONDS); }