portaudio 只取一个样本?

portaudio only taking one sample?

我目前正在使用 portaudio 和一个记录应用程序,我似乎在收集样本时遇到了一些问题。据我所知,只有一个样本被存储,回调只被调用一次,就是这样,即使变量 NUM_OF_SECONDS 设置为 30 秒。

我目前 运行 不知道我可以测试什么,也不知道如何调试它,所以我来到这里,关于如何调试我的问题有什么建议吗?

代码如下:

main.cpp:

#include <record.h>

int main()
{
    record  somethis;
    somethis.start_record();
    return 0;
}

record.h

#pragma once
#include <iostream> // Functionality: COUT
#include "portaudio.h"
#include <stdio.h>
#include <stdlib.h>
#include <chrono>  //Functionality: Sleep
#include <thread>   //Functionality: Sleep
#include <algorithm> //Functionality: fill_n
#define SAMPLE_RATE (44100)

typedef float SAMPLE;

#define NUM_SECONDS 30
#define NUM_CHANNELS  2
#define SAMPLE_SILENCE 0.0f
#define PA_SAMPLE_TYPE  paFloat32
#define FRAMES_PER_BUFFER (512)
#define TRUE (1==1)
#define FALSE (!TRUE)
#define WRITE_TO_FILE   TRUE


typedef struct
{
    int     frameIndex;
    int     maxFrameindex;
    SAMPLE  *recordedSamples;
}
paTestData;

class record {
public:
    record();
    void start_record();
private:
    PaStreamParameters  inputParameters,
                        outputParameters;
    PaStream*           stream;
    PaError             err = paNoError;
    paTestData          data;
    int                 totalFrames;
    int                 numSamples;
    int                 numBytes;
    SAMPLE              max, val;
    double              average;
    int recordCallback(const void *inputBuffer, void *outputBuffer,
                       unsigned long framesPerBuffer,
                       const PaStreamCallbackTimeInfo* timeInfo,
                       PaStreamCallbackFlags statusFlags, void *userData);

    static int recordCallbackSub(const void *inputBuffer, void *outputBuffer,
                       unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
                       PaStreamCallbackFlags statusFlags, void *userData)
    {
        auto pThis = reinterpret_cast<record*>(userData);  // get back the this pointer.
        return pThis->recordCallback( inputBuffer, outputBuffer,framesPerBuffer, timeInfo,statusFlags, nullptr);

    }
};

record.cpp

#include "record.h"


record::record()
{
    std::cout << "Record object made" << std::endl;
    std::cout << "Portaudio Version: " << Pa_GetVersion() << std::endl;
    this->data.maxFrameindex = this->totalFrames = NUM_SECONDS * SAMPLE_RATE;
    this->data.frameIndex = 0;
    this->numSamples = this->totalFrames * NUM_CHANNELS;
    numBytes = numSamples * sizeof(SAMPLE);
    this->data.recordedSamples = new SAMPLE[numSamples]; /* From now on, recordedSamples is initialised. */
    if( this->data.recordedSamples == NULL )
    {
        std::cout << "Could not allocate record array" << std::endl;
        exit(1);
    }

    for(int i=0; i<numSamples; i++ )
    {
        this->data.recordedSamples[i] = 0;

    }

    int err = Pa_Initialize();

    if( err == paNoError )
    {
        std::cout << "No error in init" << std::endl;
        std::cout << "PortAudio init: "<< Pa_GetErrorText( err ) << std::endl;
    }
    else
    {
       printf(  "PortAudio error: %s\n", Pa_GetErrorText( err ) );
       exit(1);
    }

    this->inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */


    if (this->inputParameters.device == paNoDevice) {
        std::cout << "Error: No default input device" << std::endl;
        exit(1);
    }

    this->inputParameters.channelCount = 1;                    /* stereo input */
    this->inputParameters.sampleFormat = PA_SAMPLE_TYPE;
    this->inputParameters.suggestedLatency = Pa_GetDeviceInfo( this->inputParameters.device )->defaultLowInputLatency;
    this->inputParameters.hostApiSpecificStreamInfo = NULL;

    std::cout << "Device name: "  <<Pa_GetDeviceInfo(this->inputParameters.device)->name << std::endl;
    std::cout << "Max inputChannels: "  <<Pa_GetDeviceInfo(this->inputParameters.device)->maxInputChannels << std::endl;

}

int record::recordCallback(const void *inputBuffer, void *outputBuffer,
                           unsigned long framesPerBuffer,
                           const PaStreamCallbackTimeInfo* timeInfo,
                           PaStreamCallbackFlags statusFlags, void *userData)
{
    std::cout << "Callback called" << std::endl;
    this->data = (paTestData&) userData;
    const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
    SAMPLE *wptr = &this->data.recordedSamples[this->data.frameIndex * NUM_CHANNELS];
    long framesToCalc;
    long i;
    int finished;
    unsigned long framesLeft = this->data.maxFrameindex - this->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(int i=0; i<framesToCalc; i++ )
        {
            *wptr++ = SAMPLE_SILENCE;  /* left */
            if( NUM_CHANNELS == 2 ) *wptr++ = SAMPLE_SILENCE;  /* right */
        }
    }
    else
    {
        for(int i=0; i<framesToCalc; i++ )
        {
            *wptr++ = *rptr++;  /* left */
            if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;  /* right */
        }
    }
    this->data.frameIndex += framesToCalc;
    return finished;
}

void record::start_record()
{

    err = Pa_OpenStream(
              &this->stream,
              &this->inputParameters,
              NULL,                  /* &outputParameters, */
              SAMPLE_RATE,
              FRAMES_PER_BUFFER,
              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
              &record::recordCallbackSub,
              this );
    if( err != paNoError )
    {
        std::cout << "Something wrong  - open_stream check" << std::endl;
        std::cout << "PortAudio error: "<< Pa_GetErrorText( err ) << std::endl;
        exit(1);
    }

    this->err = Pa_StartStream( this->stream );

    if( err != paNoError )
    {
        std::cout << "Something wrong in stream check" << std::endl;
        std::cout << "PortAudio error: "<< Pa_GetErrorText( err ) << std::endl;
        exit(1);
    }

    std::cout << "Waiting for playback to finish" << std::endl;

    while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
    {
        Pa_Sleep(1000);
        printf("index = %d\n", this->data.frameIndex ); fflush(stdout);

    }
    if( err < 0 )
    {
        std::cout << "error check with isStreamActive - something wrong" << std::endl;
        std::cout << "PortAudio error: "<< Pa_GetErrorText( err ) << std::endl;
        exit(1);
    }

    err = Pa_CloseStream( stream );
    if( err != paNoError )
    {
        std::cout << "error check with close_stream- something wrong" << std::endl;
        std::cout << "PortAudio error: "<< Pa_GetErrorText( err ) << std::endl;
        exit(1);
    }

    std::cout << "Number of entries: " <<  sizeof(this->data.recordedSamples)/sizeof(this->data.recordedSamples[0]) << std::endl;

    /* Measure maximum peak amplitude. */
    max = 0;
    average = 0.0;
    for(int i=0; i<numSamples; i++ )
    {

        val = this->data.recordedSamples[i];
        std::cout << "i: " << i << " : "<< val << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

        if( val < 0 ) val = -val; /* ABS */
        if( val > max )
        {
            max = val;
        }
        average += val;
    }

    average = average / (double)numSamples;

    std::cout<<"sample max amplitude = " << max << std::endl;
    std::cout<<"sample average = " <<  average << std::endl;

    if (WRITE_TO_FILE)
    {
        FILE  *fid;
        fid = fopen("recorded.wav", "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");
        }
    }



    std::cout << "Everythin done!" << std::endl;



}

更新:

我从一些调试通知中得知,回调仅被调用一次,在它 returns 之后,流变为非活动状态,因此无法调用回调函数。为什么流变得不活跃?

你的第一次回电

static int recordCallbackSub(const void *inputBuffer, void *outputBuffer,
                   unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
                   PaStreamCallbackFlags statusFlags, void *userData)
{
    auto pThis = reinterpret_cast<record*>(userData);  // get back the this pointer.
    return pThis->recordCallback( inputBuffer, outputBuffer,framesPerBuffer, timeInfo,statusFlags, nullptr);

}

调用您的第二次回调

int record::recordCallback(const void *inputBuffer, void *outputBuffer,
                           unsigned long framesPerBuffer,
                           const PaStreamCallbackTimeInfo* timeInfo,
                           PaStreamCallbackFlags statusFlags, void *userData)
{
    std::cout << "Callback called" << std::endl;
    this->data = (paTestData&) userData;

    ....
}

userData 参数设置为 nullptr。然后,您将 nullptr 转换为 paTestData& 并将 data 成员变量设置为结果,我怀疑您不打算这样做。

删除 this->data = (paTestData&) userData; 行。