PortAudio улавяне на микрофон, отделни стойности на канала

Използвам микрофонен масив (playstation eye) с 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, трябва да картографира канали като напр

ПРИМЕР [ {Channel1} {Channel2} {Channel3} {Channel4} ]

Сега проблемът е, че когато духна в един микрофон на audacity (който използва основни аудио драйвери), получавам това. Очевидно един микрофон има пик от 1, а другите са почти безшумни.

Нива на Audacity

Така че не разбирам какво правя погрешно, някакви насоки?


person benjgorman    schedule 20.03.2014    source източник
comment
Изглежда, че сте намерили решение, но мисля, че открих проблем с вашата тема за запис. Ако прочетох вашата логика правилно, вие изхвърляте нови данни, ако няма достатъчно място в буфера ви. Като се има предвид вашият случай на употреба, бих предложил да изчистите старите данни от вашия буфер, вместо да изпускате данни от portaudio буфера. Също така изглежда, че не четете всички проби. Мисля, че трябва да прочетете NUM_CHANNELS * кадъра на проби на буфер, за да получите всички проби, но проверете отново документацията за тази.   -  person trukvl    schedule 24.03.2014
comment
Приех решението по-долу, тъй като поправи грешката по отношение на това, че не виждам 2 стойности на канала, но да, все още имам проблеми с тази програма. Все пак не съм сигурен какво имате предвид, бихте ли обяснили по-подробно? Следвах примера, който 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 */

Това би обяснило защо ch 1 и 3, и ch 2 и 4 измерват едно и също ниво, защото просто пропускате всяка друга семпла от същия канал.

person jaket    schedule 21.03.2014