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;
}