在PortAudio中连续录制(来自麦克风或输出)

我正在尝试在PortAudio中创建一个音乐可视化应用程序,我做了一些基础研究,并找到了一些关于如何从麦克风录制到(临时)文件的示例。 但是没有示例在录制期间数据未用于运行时。

那么如何启动连续的音频流,我可以从当前的“帧”中捕获数据?

这就是我尝试这样做的方式:

#include  #include  #include  #include  #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 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 

更新

这段代码的目的是什么?

  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 ? 那真是太傻了……你能想到更合适的标识符吗?

其次,您将&datavoid ** )传递给Pa_OpenStream。 据推测,Pa_OpenStream将相同的值传递给您的回调函数,在该函数中,您将该指针视为void *就像它指向testData * 。 这是未定义的行为。

删除static void* data; 。 这不是必要的。 声明一个新的testData data = { 0 }; 在主要内部,在最顶部。 现在您将testData * (转换为void * )传递给Pa_OpenStream,Pa_OpenStream会将其传递给您的回调,您可以安全地将其转换回testData * 。 您可能希望在调用Pa_OpenStream之前设置data成员…

要实时与数据流交互,您需要一种机制,在帧周期内hibernate(在Windows上忙等待)(采样率/每帧采样)或使用线程同步原语来触发您的线程int main是准备好处理的音频。 这将使您可以访问PortAudio在其回调期间提供的每个dataframe(从PortAudio线程调用)。 以下是使用boost::conditionboost::mutex的概念。

 //CAUTION THIS SNIPPET IS ONLY INTENDED TO DEMONSTRATE HOW ONE MIGHT //SYNCHRONIZE WITH THE PORTAUDIO THREAD #include  #include  #include  #include  #include  #include  #include  #include  #include "portaudio.h" boost::condition waitForAudio; boost::mutex waitForAudioMutex; boost::mutex audioBufferMutex; bool trigger = false; std::deque 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 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)

你关闭了流err = Pa_CloseStream(stream); 在第一次迭代中。 在第二次迭代中,频道已经关闭。 尝试操作err = Pa_CloseStream(stream); 在所有迭代之后。

我这样解决了:

 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= 100) //if you delete this condition continuously recorded this audio { strec = false; arch.close(); } }