Using MAD with small buffers

"Daniel Kraft" <[email protected]> Sat, 14 Jan 2006 14:48:56 +0100
Newsgroups gmane.comp.audio.mad.devel
Message-ID <[email protected]>
Hi!

I want to use MAD for audio playback, and looked at the minimad.c example. My
problem is that I can't map the whole input to memory at once (as is done there)
but I have to use fixed size blocks (size of my input buffer), which I feed to
MAD and refill in my input callback.

I've adapted the minimad.c code (my version is included below) to use fixed size
blocks of input; if that buffer size is greater then my file size it works quite
fine; but when it is smaller, I get loads of errors (mostly lost
synchronization) after a few blocks are read.

As there's not much documentation about that, I hope you can help me: When I use
stream_buffer(...), how long is that buffer data needed to be available? Can I
clear (or reuse) that buffer the next time my input callback is called?

Thank you very much for your help!
Yours,
Daniel





/* ************************************************************* */

# include <stdio.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/mman.h>
#include <assert.h>
#include <malloc.h>

# include "mad.h"

#define BUFFER_SIZE	(1<<10)

typedef struct {
  unsigned char* start;
  unsigned long length;
  FILE* in;
} mydata;

static int decode(mydata*);

int main(int argc, char *argv[])
{
  mydata buffer;

  buffer.in=fopen(argv[1], "rb");
  assert(buffer.in);

  buffer.start=malloc(BUFFER_SIZE);
  buffer.length=0;

  decode(&buffer);

  free(buffer.start);
  fclose(buffer.in);

  return 0;
}

static
enum mad_flow input(void *data,
		    struct mad_stream *stream)
{
  mydata *buffer = data;

  if(!buffer->length)
  {
   buffer->length=fread(buffer->start, sizeof(*buffer->start), BUFFER_SIZE,
buffer->in);
   if(!buffer->length)
    return MAD_FLOW_STOP;
  }

  mad_stream_buffer(stream, buffer->start, buffer->length);
  buffer->length=0;

  return MAD_FLOW_CONTINUE;
}

static inline
signed int scale(mad_fixed_t sample)
{
  /* round */
  sample += (1L << (MAD_F_FRACBITS - 16));

  /* clip */
  if (sample >= MAD_F_ONE)
    sample = MAD_F_ONE - 1;
  else if (sample < -MAD_F_ONE)
    sample = -MAD_F_ONE;

  /* quantize */
  return sample >> (MAD_F_FRACBITS + 1 - 16);
}

static
enum mad_flow output(void *data,
		     struct mad_header const *header,
		     struct mad_pcm *pcm)
{
  unsigned int nchannels, nsamples;
  mad_fixed_t const *left_ch, *right_ch;

  /* pcm->samplerate contains the sampling frequency */

  nchannels = pcm->channels;
  nsamples  = pcm->length;
  left_ch   = pcm->samples[0];
  right_ch  = pcm->samples[1];

  while (nsamples--) {
    signed int sample;

    /* output sample(s) in 16-bit signed little-endian PCM */

    sample = scale(*left_ch++);
    putchar((sample >> 0) & 0xff);
    putchar((sample >> 8) & 0xff);

    if (nchannels == 2) {
      sample = scale(*right_ch++);
      putchar((sample >> 0) & 0xff);
      putchar((sample >> 8) & 0xff);
    }
  }

  return MAD_FLOW_CONTINUE;
}

static
enum mad_flow error(void *data,
		    struct mad_stream *stream,
		    struct mad_frame *frame)
{
  mydata *buffer = data;

  fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
	  stream->error, mad_stream_errorstr(stream),
	  stream->this_frame - buffer->start);

  /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */

  return MAD_FLOW_CONTINUE;
}

static
int decode(mydata* buffer)
{
  struct mad_decoder decoder;
  int result;

  /* configure input, output, and error functions */

  mad_decoder_init(&decoder, buffer,
		   input, 0 /* header */, 0 /* filter */, output,
		   error, 0 /* message */);

  /* start decoding */

  result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

  /* release the decoder */

  mad_decoder_finish(&decoder);

  return result;
}