MAD_ERROR_BADHUFFDATA before MAD_ERROR_BUFLEN
Radek Bartoň <[email protected]> Sun, 29 Apr 2007 20:01:58 +0200
| Newsgroups | gmane.comp.audio.mad.devel |
|---|---|
| Organization | FIT BUT |
| Message-ID | <[email protected]> |
Hello. I tried to incorporate Madlld low-level API demonstration code into my MP3 player, but I'm experiencing a weird problem. When data stored by mad_stream_buffer to MAD are going to run out MAD_ERROR_BADHUFFDATA is generated right before MAD_ERROR_BUFLEN. Then when new data are sent to MAD MAD_ERROR_BADCRC and MAD_ERROR_BADDATAPTR occur. Since then no other errors occur until next data are consumed. This produces slight skip in otherwise fluent playback. I checked twice that my code which I'm attaching is equivalent to Madlld's one. Please, if you'll notice a reasong of this behaviour in my code, let me know since I'm little desperate about this. Thank you. -- Bc. Radek Bartoň Faculty of Information Technology Brno University of Technology E-mail: [email protected] Web: http://blackhex.no-ip.org Jabber: [email protected]
mad_problem.cpp
(text/x-c++src, 5 KB)
Q_LONG Player::decodeFile(uchar * buffer, const Q_LONG size)
{
qDebug("decode: %ld", size);
const Q_LONG MPEG_BUFFER_SIZE = 40 * 1024;
uchar mpeg_buffer[MPEG_BUFFER_SIZE + MAD_BUFFER_GUARD];
Q_LONG to_decode = size;
Q_LONG decoded = 0;
qDebug("saved: %ld offset: %ld", this->int_saved_size, this->int_saved_start);
// Store previously buffered PCM samples to output buffer.
if (this->int_saved_size > 0)
{
Q_LONG stored = this->storeSynthToBuffer(buffer, this->int_synth,
this->int_saved_start, to_decode);
buffer += stored;
to_decode -= stored;
decoded += stored;
// Consume stored part of buffer.
this->int_saved_size -= stored;
this->int_saved_start = (this->int_saved_size > 0) ? this->int_saved_start
+ stored : 0;
qDebug("BUFFERED: stored: %ld saved: %ld offset: %ld", stored, this->int_saved_size, this->int_saved_start);
}
// Decode until desired size is reached.
while ((to_decode > 0) && !this->int_file.atEnd())
{
uchar * guard_start = NULL;
// It's needed to read next MPEG data from file?
if ((this->int_stream.buffer == NULL) || (this->int_stream.error ==
MAD_ERROR_BUFLEN))
{
Q_LONG remaining = 0;
Q_LONG read_size = MPEG_BUFFER_SIZE;
uchar * read_start = mpeg_buffer;
// Save undecoded rest of buffer to its beginning.
if (this->int_stream.next_frame != NULL)
{
remaining = this->int_stream.bufend - this->int_stream.next_frame;
memmove(mpeg_buffer, this->int_stream.next_frame, remaining);
read_start += remaining;
read_size -= remaining;
}
qDebug("read_size: %ld read_start: %d", read_size, read_start - mpeg_buffer);
// Read next frame of MPEG data.
if ((read_size = this->int_file.readBlock((char *) read_start,
read_size)) < 0)
{
qWarning("Error reading from file %s!", this->int_file_name.operator
const char *());
return -1;
}
// If end of file reached append a few zeros to MPEG data buffer.
if (this->int_file.atEnd())
{
guard_start = read_start + read_size;
memset(guard_start, 0, MAD_BUFFER_GUARD);
read_size += MAD_BUFFER_GUARD;
}
// Send MPEG data to MAD.
mad_stream_buffer(&this->int_stream, mpeg_buffer, remaining + read_size);
this->int_stream.error = MAD_ERROR_NONE;
}
// Decode next frame.
if (mad_frame_decode(&this->int_frame, &this->int_stream) != 0)
{
// Is occured error recoverable?
if (MAD_RECOVERABLE(this->int_stream.error))
{
if (this->int_stream.error != MAD_ERROR_LOSTSYNC ||
this->int_stream.this_frame != guard_start)
{
qWarning("Recoverable error during MPEG stream decoding: %x!",
this->int_stream.error);
}
// Decode next frame.
continue;
}
else
{
// Out of MPEG frame data?
if (this->int_stream.error == MAD_ERROR_BUFLEN)
{
qDebug("need data");
// Read next data from file and try to decode aggain.
continue;
}
else
{
qWarning("Error decoding MPEG stream: %x!", this->int_stream.error);
return -1;
}
}
}
// Synthetize decoded frame to PCM samples.
mad_synth_frame(&this->int_synth, &this->int_frame);
qDebug("decoded: %d to_decode: %ld", this->int_synth.pcm.length * 2 * sizeof(short), to_decode);
// Store decoded PCM samples to output buffer.
Q_LONG stored = this->storeSynthToBuffer(buffer, this->int_synth, 0,
to_decode);
buffer += stored;
to_decode -= stored;
decoded += stored;
// Save rest of a buffer for next call.
this->int_saved_size = (this->int_synth.pcm.length * 2 * sizeof(short)) -
stored;
this->int_saved_start = (this->int_saved_size > 0) ? stored : 0;
qDebug("NEW: stored: %ld saved: %ld offset: %ld", stored, this->int_saved_size, this->int_saved_start);
}
return decoded;
}
Q_LONG Player::storeSynthToBuffer(uchar * buffer, const mad_synth & synth,
const Q_LONG offset, const Q_LONG size)
{
Q_LONG stored = 0;
Q_LONG samples = size / (2 * sizeof(short));
Q_LONG start = offset / (2 * sizeof(short));
Q_LONG end = ((start + samples) > synth.pcm.length) ? synth.pcm.length :
(start + samples);
// Copy decoded PCM samples to output buffer.
for (Q_LONG I = start; I < end; ++I)
{
// Copy left and right channel.
for (int J = 0; J < 2; ++J)
{
mad_fixed_t mad_sample;
short sample;
// Convert MAD fixed point sample to short.
mad_sample = synth.pcm.samples[J][I];
sample = (short) (mad_sample >> (MAD_F_FRACBITS - 15));
if (mad_sample >= MAD_F_ONE)
{
sample = SHRT_MAX;
}
if (mad_sample <= -MAD_F_ONE)
{
sample = -SHRT_MAX;
}
// Copy left/right channel sample to output
memcpy(buffer, &sample, sizeof(sample));
buffer += sizeof(sample);
stored += sizeof(sample);
}
}
return stored;
}