Question

I'm using a microphone array (playstation eye) with PortAudio. I'm attempting microphone array processing where I can know the level of each microphone and specify the direction of a sound using beam forming or inter aural time delays. I'm having trouble determining which sound levels come from each channel.

Here is some code snippets, the first being the recordCallback.

static int recordCallback( const void *inputBuffer, void *outputBuffer,
                          unsigned long framesPerBuffer,
                          const PaStreamCallbackTimeInfo* timeInfo,
                          PaStreamCallbackFlags statusFlags,
                          void *userData )
{

    paTestData *data = (paTestData*)userData;


    const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
    SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
    long framesToCalc;
    long i;
    int finished;
    unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;

    (void) outputBuffer; /* Prevent unused variable warnings. */
    (void) timeInfo;
    (void) statusFlags;
    (void) userData;

    if( framesLeft < framesPerBuffer )
    {
        framesToCalc = framesLeft;
        finished = paComplete;
    }
    else
    {
        framesToCalc = framesPerBuffer;
        finished = paContinue;
    }

    if( inputBuffer == NULL )
    {
        for( i=0; i<framesToCalc; i++ )
        {
            *wptr++ = SAMPLE_SILENCE;  /* 1 */
            if( NUM_CHANNELS =>= 2 ) *wptr++ = SAMPLE_SILENCE;  /* 2 */
            if( NUM_CHANNELS =>= 3 ) *wptr++ = SAMPLE_SILENCE;  /* 3 */
            if( NUM_CHANNELS >= 4 ) *wptr++ = SAMPLE_SILENCE;  /* 4 */
        }
    }
    else
    {
        for( i=0; i<framesToCalc; i++ )
        {
            *wptr++ = *rptr++;  /* 1 */
            if( NUM_CHANNELS >= 2 ) *wptr++ = *rptr++;  /* 2 */
            if( NUM_CHANNELS >= 3 ) *wptr++ = *rptr++;  /* 3 */
            if( NUM_CHANNELS >= 4 ) *wptr++ = *rptr++;  /* 4 */
        }
    }
    data->frameIndex += framesToCalc;
    return finished;
}

Here is the main method where I'm playing back the audio and trying to show channel averages.

int main(void)
{
    PaStreamParameters  inputParameters,
    outputParameters;
    PaStream*           stream;
    PaError             err = paNoError;
    paTestData          data;
    int                 i;
    int                 totalFrames;
    int                 numSamples;
    int                 numBytes;
    SAMPLE              max, val;
    double              average;

    printf("patest_record.c\n"); fflush(stdout);

    data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
    data.frameIndex = 0;
    numSamples = totalFrames * NUM_CHANNELS;
    numBytes = numSamples * sizeof(SAMPLE);
    data.recordedSamples = (SAMPLE *) malloc( numBytes ); /* From now on, recordedSamples is initialised. */
    if( data.recordedSamples == NULL )
    {
        printf("Could not allocate record array.\n");
        goto done;
    }
    for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;

    err = Pa_Initialize();
    if( err != paNoError ) goto done;

    inputParameters.device = 2; /* default input device */
    if (inputParameters.device == paNoDevice) {
        fprintf(stderr,"Error: No default input device.\n");
        goto done;
    }
    inputParameters.channelCount = 2;                    /* stereo input */
    inputParameters.sampleFormat = PA_SAMPLE_TYPE;
    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo = NULL;

    /* Record some audio. -------------------------------------------- */
    err = Pa_OpenStream(
                        &stream,
                        &inputParameters,
                        NULL,                  /* &outputParameters, */
                        SAMPLE_RATE,
                        FRAMES_PER_BUFFER,
                        paClipOff,      /* we won't output out of range samples so don't bother clipping them */
                        recordCallback,
                        &data );
    if( err != paNoError ) goto done;

    err = Pa_StartStream( stream );
    if( err != paNoError ) goto done;
    printf("\n=== Now recording!! Please speak into the microphone. ===\n"); fflush(stdout);

    while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
    {
        Pa_Sleep(1000);

        printf("index = %d\n", data.frameIndex ); fflush(stdout);
        printf("Channel = %d\n", data.currentChannel ); fflush(stdout);
    }
    if( err < 0 ) goto done;

    err = Pa_CloseStream( stream );
    if( err != paNoError ) goto done;

    /* Measure maximum peak amplitude. */

    /*  average for each channel */

    SAMPLE channel1val =0;
    SAMPLE channel2val = 0;
    SAMPLE channel3val =0;
    SAMPLE channel4val = 0;

    long channel1avg = 0.0;
    long channel2avg =0.0;
    long channel3avg =0.0;
    long channel4avg =0.0;

    SAMPLE channel1max = 0;
    SAMPLE channel2max =0;
    SAMPLE channel3max =0;
    SAMPLE channel4max =0;

    i = 0;
    do
    {

        channel1val = data.recordedSamples[i];
        if (channel1val < 0)
        {
            channel1val = -channel1val;
        }

        if (channel1val > channel1max)
        {
            channel1max = channel1val;
        }
        channel1avg  += channel1val;

        i = i + 4;
    }
    while (i<numSamples);

    i = 1;
    do
    {
        channel2val = data.recordedSamples[i];
        if (channel2val < 0)
        {
            channel2val = -channel2val;
        }

        if (channel2val > channel2max)
        {
            channel2max = channel2val;
        }
        channel2avg  += channel2val;

        i = i + 4;
    }
    while (i<numSamples);

    i = 2;
    do
    {
        channel3val = data.recordedSamples[i];
        if (channel3val < 0)
        {
            channel3val = -channel3val;
        }

        if (channel3val > channel3max)
        {
            channel3max = channel3val;
        }
        channel3avg  += channel3val;

        i = i + 4;
    }
    while (i<numSamples);

    i = 3;
    do
    {
        channel4val = data.recordedSamples[i];
        if (channel4val < 0)
        {
            channel4val = -channel4val;
        }

        if (channel4val > channel4max)
        {
            channel4max = channel4val;
        }
        channel4avg  += channel4val;

        i = i + 4;
    }
    while (i<numSamples);






    channel1avg = channel1avg / (double)numSamples;
    channel2avg = channel2avg / (double)numSamples;
    channel3avg = channel3avg / (double)numSamples;
    channel4avg = channel4avg / (double)numSamples;

  // printf("sample max amplitude = "PRINTF_S_FORMAT"\n", max );
  //  printf("sample average = %lf\n", average );


    printf("channel1 max amplitude = "PRINTF_S_FORMAT"\n", channel1max);
    printf("sample average = %lf\n", channel1avg);

    printf("channel2 max amplitude = "PRINTF_S_FORMAT"\n", channel2max);
    printf("sample average = %lf\n", channel2avg);

    printf("channel3 max amplitude = "PRINTF_S_FORMAT"\n", channel3max);
    printf("sample average = %lf\n", channel3avg);

    printf("channel4 max amplitude = "PRINTF_S_FORMAT"\n", channel4max);
    printf("sample average = %lf\n", channel4avg);


    printf("/nPrinting out values/n");

    for (int j=0; j<8; j++)
    {
        printf("Value: %lf\n", data.recordedSamples[j]);
    }


    /* Write recorded data to a file. */
#if WRITE_TO_FILE
    {
        FILE  *fid;
        fid = fopen("recorded.raw", "wb");
        if( fid == NULL )
        {
            printf("Could not open file.");
        }
        else
        {
            fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid );
            fclose( fid );
            printf("Wrote data to 'recorded.raw'\n");
        }
    }
#endif

    /* Playback recorded data.  -------------------------------------------- */
    data.frameIndex = 0;

    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
    if (outputParameters.device == paNoDevice) {
        fprintf(stderr,"Error: No default output device.\n");
        goto done;
    }
    outputParameters.channelCount = 2;                     /* stereo output */
    outputParameters.sampleFormat =  PA_SAMPLE_TYPE;
    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
    outputParameters.hostApiSpecificStreamInfo = NULL;

    printf("\n=== Now playing back. ===\n"); fflush(stdout);
    err = Pa_OpenStream(
                        &stream,
                        NULL, /* no input */
                        &outputParameters,
                        SAMPLE_RATE,
                        FRAMES_PER_BUFFER,
                        paClipOff,      /* we won't output out of range samples so don't bother clipping them */
                        playCallback,
                        &data );
    if( err != paNoError ) goto done;

    if( stream )
    {
        err = Pa_StartStream( stream );
        if( err != paNoError ) goto done;

        printf("Waiting for playback to finish.\n"); fflush(stdout);

        while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
        if( err < 0 ) goto done;

        err = Pa_CloseStream( stream );
        if( err != paNoError ) goto done;

        printf("Done.\n"); fflush(stdout);
    }

done:
    Pa_Terminate();
    if( data.recordedSamples )       /* Sure it is NULL or valid. */
        free( data.recordedSamples );
    if( err != paNoError )
    {
        fprintf( stderr, "An error occured while using the portaudio stream\n" );
        fprintf( stderr, "Error number: %d\n", err );
        fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
        err = 1;          /* Always return 0 or 1, but no other return codes. */
    }
    return err;
}

If I run my code and blow into one microphone I get this. When I play the sound back using my code, it works fine and the sound is correct, but looking at the values outputted:

channel1 max amplitude = 1.00000000
sample average = 1.000000
channel2 max amplitude = 0.02542114
sample average = 0.025421
channel3 max amplitude = 1.00000000
sample average = 1.000000
channel4 max amplitude = 0.02627563
sample average = 0.026276

Which clearly isn't correct. As it's showing that two channels are almost identical. From what I understand, as it's capturing linear PCM, it should be mapping channels such as

SAMPLE [ {Channel1} {Channel2} {Channel3} {Channel4} ]

Now the problem is, when I blow into one microphone on audacity (which uses core-audio drivers) I get this. Clearly one microphone has peak of 1 and the others are near silent.

Audacity Levels

So I don't understand what I'm doing wrong, any pointers?

Was it helpful?

Solution

It looks like you are only recording 2 channels

inputParameters.channelCount = 2;                    /* stereo input */

This would explain why ch 1 and 3, and ch 2 and 4 are measuring the same level because you're just skipping every other sample from the same channel.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top