0
votes

I'm currently building an mp3 streamer/receiver with the use of live555, mpg123 and portaudio, and just starting the concepts of streaming, decoding and playing mp3.

My problem is when I need to play the sound with portaudio. I can't figure how to write a callback function that will play my decoded mp3.

Here's the callback function that I found somewhere and tried, but that isn't giving me good results (nothing but noises and buzz).

static int patestCallback( const void *inputBuffer, void *outputBuffer,
                           unsigned long framesPerBuffer,
                           const PaStreamCallbackTimeInfo* timeInfo,
                           PaStreamCallbackFlags statusFlags,
                           void *userData )
{
    /* Cast data passed through stream to our structure. */
    paTestData *data_struct = (paTestData*)userData; 
    float *out = (float*)outputBuffer;
    unsigned int i;
    (void) inputBuffer; /* Prevent unused variable warning. */

    for( i=0; i<framesPerBuffer; i++ )
    {
        if(data_struct->cursor < data_struct->num_frames)
        {
            out[i] = data_struct->data[data_struct->cursor];
            data_struct->cursor++;
        }
        else
        {
            out[i] = 0; // if you've used up all the data, just fill the rest with silence.
            //finished = paComplete;
        }
    }
    return 0;
}

The callback function receives a struct containing the decoded data in an unsigned char array and the number of bytes decoded:

typedef struct
{
    unsigned char* data;
    unsigned long cursor;
    unsigned long num_frames;
}
paTestData;

The codec function looks like this:

mpg123_decode(m,fBuffer,frameSize,out,OUTBUFF,&size);

So it returns the data in an unsigned char (out), with the bytes decoded in the size variable. fBuffer is the encoded data and frameSize the number of bytes encoded.

I've configured the portaudio stream the same way as in the portaudio tutorials:

err = Pa_OpenDefaultStream( &stream,
                                        0,          /* no input channels */
                                        2,          /* stereo output */
                                        paFloat32,  /* 32 bit floating point output */
                                        SAMPLE_RATE,
                                        paFramesPerBufferUnspecified,        /* frames per buffer, i.e. the number
                                                            of sample frames that PortAudio will
                                                            request from the callback. Many apps
                                                            may want to use
                                                            paFramesPerBufferUnspecified, which
                                                            tells PortAudio to pick the best,
                                                            possibly changing, buffer size.*/
                                        patestCallback, /* this is your callback function */
                                        &paData ); /*This is a pointer that will be passed to
                                                            your callback*/

An example of a good callback function would be very useful, but of course any help is appreciated,

Thanks

1
You need to be clear about the format and channel count of the data coming out of your codec, and the format and channel count of the data that PortAudio was configured to expect. Is the output of the codec unsigned chars? or something else? is it stereo or mono?Ross Bencina
Thanks Ross, I've added the missing parts. Thank you for your help!louis
I've added the missing parts in my explanation, of course, not my program ;).louis
Using lame instead of mpg123 might be a better decoding solution.Dominic Cerisano

1 Answers

0
votes

I think you can be fairly confident that the actual audio data format of the output from mpg123_decode is not unsigned chars. Most likely it is declared that way as a generic pointer. You should research what the actual type is. It may be something that you can configure.

My first guess would be that the output of mpg123_decode is stereo 16 bit ints (assuming a stereo source). If that is the case the following code may work.

Note that I have made the minimal changes to your code to get it to work. This is not a good example of how to do it. Problems with my code:

  • The stream sample format is paFloat even though you should probably just output paInt16 if the source is shorts. The scale variable is there to convert to the proper range for paFloat [-1,1]
  • Nasty casting to keep your data_struct->data as char* even though it should probably be a short* if the source is shorts.
  • Loop covers framesPerBuffer*2 samples (since 1 frame is all channels, and I'm assuming stereo). This doesn't make the code very clear (check other PA examples to see how stereo is usually handled).

    static int patestCallback( const void *inputBuffer, void *outputBuffer,
                               unsigned long framesPerBuffer,
                               const PaStreamCallbackTimeInfo* timeInfo,
                               PaStreamCallbackFlags statusFlags,
                               void *userData )
    {
        /* Cast data passed through stream to our structure. */
        paTestData *data_struct = (paTestData*)userData; 
        float *out = (float*)outputBuffer;
        unsigned int i;
        (void) inputBuffer; /* Prevent unused variable warning. */
        static const float scale = 1./32768.;
    
        for( i=0; i<framesPerBuffer*2; i++ ) // source and dest are interleaved
        {
            if(data_struct->cursor < data_struct->num_frames)
            {
                out[i] = *((short*)(&data_struct->data[data_struct->cursor])) * scale;
                data_struct->cursor += sizeof(short);
            }
            else
            {
                out[i] = 0; // if you've used up all the data, just fill the rest with silence.
                //finished = paComplete;
            }
        }
        return 0;
    }