Захват микрофона PortAudio, отдельные значения канала

Я использую массив микрофонов (игровой глаз) с PortAudio. Я пытаюсь обработать массив микрофонов, где я могу узнать уровень каждого микрофона и указать направление звука, используя формирование луча или интерауральные временные задержки. У меня возникли проблемы с определением того, какие уровни звука исходят от каждого канала.

Вот несколько фрагментов кода, первым из которых является 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;
}

Вот основной метод, в котором я воспроизводю звук и пытаюсь показать средние значения каналов.

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

Если я запускаю свой код и дую в один микрофон, я получаю это. Когда я воспроизвожу звук, используя свой код, он работает нормально, и звук правильный, но глядя на выведенные значения:

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

Что явно не правильно. Поскольку это показывает, что два канала почти идентичны. Насколько я понимаю, поскольку он захватывает линейный PCM, он должен отображать такие каналы, как

ОБРАЗЕЦ [ {Канал1} {Канал2} {Канал3} {Канал4} ]

Теперь проблема в том, что когда я дую в один микрофон на Audacity (который использует драйверы ядра аудио), я получаю это. Ясно, что один микрофон имеет пиковое значение 1, а другие почти бесшумны.

Уровни смелости

Так что я не понимаю, что я делаю неправильно, какие-либо указатели?


person benjgorman    schedule 20.03.2014    source источник
comment
Похоже, вы нашли решение, но я думаю, что нашел проблему с вашей веткой записей. Если я правильно понял вашу логику, вы отбрасываете новые данные, если в вашем буфере недостаточно места. Учитывая ваш вариант использования, я бы предложил сбросить старые данные из вашего буфера вместо удаления данных из буфера portaudio. Также похоже, что вы не читаете все образцы. Я думаю, вам нужно прочитать NUM_CHANNELS * кадров на буфер, чтобы получить все образцы, но дважды проверьте документацию по этому.   -  person trukvl    schedule 24.03.2014
comment
Я принял приведенное ниже решение, поскольку оно исправило ошибку, связанную с отсутствием значений двух каналов, но да, у меня все еще есть проблемы с этой программой. Я не уверен, что вы имеете в виду, не могли бы вы объяснить подробнее? Я следовал примеру, который дает portaudio portaudio.com/docs/v19-doxydocs/paex__record_8c_source.html.   -  person benjgorman    schedule 24.03.2014
comment
Одна секунда чтения выглядит так, как будто поток завершается, когда пользовательский буфер заполнен. Я предполагал, что вы захотите, чтобы это приложение работало бесконечно. Если это так, было бы лучше выбросить старые данные, которые стояли в очереди, а не удалять новые данные, только что прочитанные с микрофона.   -  person trukvl    schedule 25.03.2014
comment
В конечном итоге я хочу запускать приложение на неопределенный срок, в идеале я хочу записывать непрерывно или, по крайней мере, внутри цикла, а затем выполнять некоторую обработку, когда уровни превышают определенный порог. Но я не был уверен, как лучше поступить.   -  person benjgorman    schedule 25.03.2014


Ответы (1)


Похоже, вы записываете только 2 канала

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

Это объясняет, почему каналы 1 и 3, а также каналы 2 и 4 измеряют один и тот же уровень, потому что вы просто пропускаете все остальные сэмплы с одного и того же канала.

person jaket    schedule 21.03.2014