在PortAudio连续记录(从麦克风或输出)(Continuous recording in Po

2019-08-18 03:27发布

我试图创造PortAudio音乐可视化应用程序,我做了一些基础性研究,发现如何从一个麦克风记录到(临时)文件中的一些例子。 但没有在记录期间的数据不使用运行时的例子。

所以,我怎么可以开始一个连续的音频流,我可以从当前的“帧”赶数据?

这是我想做到这一点:

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>

#include "portaudio.h"

#define SAMPLE_RATE (44100)

typedef struct{
    int frameIndex;
    int maxFrameIndex;
    char* recordedSamples;
}
testData;

PaStream* stream;

static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){
    testData* data = (testData*)userData;
    const char* buffer_ptr = (const char*)inputBuffer;
    char* index_ptr = &data->recordedSamples[data->frameIndex];

    long framesToCalc;
    long i;
    int finished;
    unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;

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

    if(inputBuffer == NULL){
        for(i = 0; i < framesToCalc; i++){
            *index_ptr++ = 0;
        }
    }else{
        for(i = 0; i < framesToCalc; i++){
            *index_ptr++ = *buffer_ptr++;
        }
    }

    data->frameIndex += framesToCalc;
    return finished;
}

int setup(testData streamData){
    PaError err;

    err = Pa_Initialize();
    if(err != paNoError){
        fprintf(stderr, "Pa_Initialize error: %s\n", Pa_GetErrorText(err));
        return 1;
    }

    PaStreamParameters inputParameters;
    inputParameters.device = Pa_GetDefaultInputDevice();
    if (inputParameters.device == paNoDevice) {
        fprintf(stderr, "Error: No default input device.\n");
        return 1;
    }

    inputParameters.channelCount = 1;
    inputParameters.sampleFormat = paInt8;
    inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo = NULL;

    err = Pa_OpenStream(&stream, &inputParameters, NULL, SAMPLE_RATE, 256, paClipOff, recordCallback, &streamData);
    if(err != paNoError){
        fprintf(stderr, "Pa_OpenDefaultStream error: %s\n", Pa_GetErrorText(err));
        return 1;
    }

    err = Pa_StartStream(stream);
    if(err != paNoError){
        fprintf(stderr, "Pa_StartStream error: %s\n", Pa_GetErrorText(err));
        return 1;
    }

    return 0;
}

void quit(testData streamData){
    PaError err;
    err = Pa_Terminate();
    if(err != paNoError){
        fprintf(stderr, "Pa_Terminate error: %s\n", Pa_GetErrorText(err));
    }

    if(streamData.recordedSamples)
        free(streamData.recordedSamples);
}

int main(){
    int i;
    PaError err;
    testData streamData = {0};

    streamData.frameIndex = 0;
    streamData.maxFrameIndex = SAMPLE_RATE;
    streamData.recordedSamples = (char*)malloc(SAMPLE_RATE * sizeof(char));
    if(streamData.recordedSamples == NULL)
        printf("Could not allocate record array.\n");

    for(i=0; i<SAMPLE_RATE; i++) 
        streamData.recordedSamples[i] = 0;

    //int totalFrames = SAMPLE_RATE;

    if(!setup(streamData)){
        printf("Opened\n");

        int i = 0;

        while(i++ < 500){

            if((err = Pa_GetStreamReadAvailable(stream)) != paNoError)
                break;

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

            err = Pa_CloseStream(stream);
            if(err != paNoError)
                break;

            streamData.frameIndex = 0;
            for(i=0; i<SAMPLE_RATE; i++) 
                streamData.recordedSamples[i] = 0;
        }

        if(err != paNoError){
            fprintf(stderr, "Active stream error: %s\n", Pa_GetErrorText(err));
        }

        quit(streamData);
    }else{
        puts("Couldn't open\n");
    }
    return 0;
}

但它提供了以下的输出:

ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_dmix.c:957:(snd_pcm_dmix_open) The dmix plugin supports only playback stream
Active stream error: Can't read from a callback stream

Answer 1:

更新

这是什么代码的目的是什么?

        if((err = Pa_GetStreamReadAvailable(stream)) != paNoError)
            break;

在我看来,这样是造成你的(最新的)问题。 为什么你需要检索(然后丢弃),可以从流,而无需等待被读取的帧数,这大概也是零,因为流是一个回调流?


以前的答案:

这似乎非常可疑:

static void* data;
/* ... */
static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){
    testData* data = (testData*)userData;
    /* ... */
}

首先,为什么叫有两个变量data ? 这只是愚蠢的......你能想到更合适的标识符?

其次,你传递一个&data (一void ** )到Pa_OpenStream。 据推测,Pa_OpenStream传递给你的回调函数,你把该指针相同的值void *好像它指向一个testData * 。 这是不确定的行为。

删除static void* data; 。 这是没有必要的,在这里。 声明一个新testData data = { 0 }; 里面主要的,就在最顶端。 现在,你传递一个testData * (转换为void * )到Pa_OpenStream,Pa_OpenStream将传递到您的回调,你可以安全地将其转换回testData *的你。 您可能需要设置的成员data调用Pa_OpenStream之前...



Answer 2:

要与数据进行交互流实时你需要,要么睡觉(在Windows上忙等待)的帧周期(采样率/每帧的样本),或使用一个线程同步原语来触发你的线程机制int main有是音频准备进行处理。 这会给你访问的每个数据帧中的回调(从PortAudio线程调用)期间PortAudio提供。 这里的概念是如何工作使用boost::conditionboost::mutex

//CAUTION THIS SNIPPET IS ONLY INTENDED TO DEMONSTRATE HOW ONE MIGHT
//SYNCHRONIZE WITH THE PORTAUDIO THREAD

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>

#include "portaudio.h"

boost::condition waitForAudio;
boost::mutex waitForAudioMutex;
boost::mutex audioBufferMutex;
bool trigger = false;

std::deque<char> audioBuffer;

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

    const char* buffer_ptr = (const char*)inputBuffer;

    //Lock mutex to block user thread from modifying data buffer
    audioBufferMutex.lock();

    //Copy data to user buffer
    for(i = 0; i < frameCount; ++i) {
       audioBuffer.push_back(buffer_ptr + i);
    }

    //Unlock mutex, allow user to manipulate buffer
    audioBufferMutex.unlock();

    //Signal user thread to process audio
    waitForAudioMutex.lock();
    trigger= true;
    waitForAudio.notify_one();
    waitForAudioMutex.unlock();

    return finished;
}

int main(){
        Pa_Initialize();
        //OPEN AND START PORTAUDIO STREAM
        while(true){ //Catch signal (Ctrl+C) or some other mechanism to interrupt this loop
            boost::xtime duration;
            boost::xtime_get(&duration, boost::TIME_UTC);
            boost::interprocess::scoped_lock<boost::mutex> lock(waitForAudioMutex);
            if(!trigger) {
                if(!waitForAudio.timed_wait(lock, duration)) {
                    //Condition timed out -- assume audio stream failed
                    break;
                }
            }
            trigger= false;

            audioBufferMutex.lock();
            //VISUALIZE AUDIO HERE
            //JUST MAKE SURE TO FINISH BEFORE PORTAUDIO MAKES ANOTHER CALLBACK
            audioBufferMutex.unlock();
        }
        //STOP AND CLOSE PORTAUDIO STEAM
        Pa_Terminate();
        return 0;
    }

一般来说,这种技术是跨平台的,但是这种具体实施可能只在Linux上运行。 在Windows上使用SetEvent(eventVar)代替condition::notify_one()WaitForSingleObject(eventVar, duration) ,而不是condition::timed_wait(lock, duration)



Answer 3:

您已关闭流err = Pa_CloseStream(stream); 在第一次迭代。 在第二次迭代中,信道已经关闭。 试该操作err = Pa_CloseStream(stream); 毕竟迭代。



Answer 4:

我解决了它这种方式:

    PaStreamParameters  inputParameters ,outputParameters;
    PaStream*           stream;
    PaError             err;  
    paTestData          data;
    int                 i;
    int                 totalFrames;
    int                 numSamples;
    int                 numBytes;

    err                     = paNoError;
    inputParameters.device  = 4;

    data.maxFrameIndex      = totalFrames = NUM_SECONDS * SAMPLE_RATE;
    data.frameIndex         = 0;
    numSamples              = totalFrames * NUM_CHANNELS;
    numBytes                = numSamples * sizeof(SAMPLE);
    data.recordedSamples    = (SAMPLE *) malloc( numBytes );

    std::ofstream arch;
    arch.open("signal.csv");

    err = Pa_Initialize();
    inputParameters.channelCount = 1;                    
    inputParameters.sampleFormat = PA_SAMPLE_TYPE;
    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo = NULL;

    int contador = 0;
    bool strec = true;
    while (strec)
    {
        err = Pa_OpenStream(
              &stream,
              &inputParameters,
              NULL,                  
              SAMPLE_RATE,
              FRAMES_PER_BUFFER,
              paClipOff,      
              recordCallback,
              &data );

        err = Pa_StartStream( stream );

        printf("\n===Grabando.... ===\n"); fflush(stdout);
        Pa_Sleep(3000);
        while( ( err = Pa_IsStreamActive(stream) ) == 1 )
        {

        }

        err = Pa_CloseStream( stream );

        for( i=0; i<numSamples; i++ )

        {

            val = data.recordedSamples[i];

            arch << val << std::endl;
            std::cout << std::endl << "valor  : " << val;
        }

        data.frameIndex = 0;
        contador++;
        if (contador >= 100) //if you delete this condition continuously recorded this audio
        {
            strec = false;
            arch.close();
        }
    }


文章来源: Continuous recording in PortAudio (from mic or output)