Continuous recording in PortAudio (from microphone or output)

I am trying to create a music visualization application in PortAudio, I did some basic research and found some examples of how to record from a microphone to a (temporary) file. But there was no example where data is not used during recording during recording.

So, how can I start a continuous audio stream, where can I catch data from the current β€œframe”?

Here is how I tried to do this:

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

But it gives the following result:

 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 
+6
source share
4 answers

Update:

What is the purpose of this code?

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

It seems to me that this is causing your (last) problem. Why do you need to restore (and then discard) the number of frames that can be read from the stream without expecting that, apparently, should be zero, since the stream is a callback stream?


Previous answer:

This seems very suspicious:

 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; /* ... */ } 

First, why are there two variables called data ? It's just stupid ... Can you come up with more appropriate identifiers?

Secondly, you pass &data (a void ** ) to Pa_OpenStream. Presumably, Pa_OpenStream passes the same value to your callback function, where you handle this pointer to void * as if it were pointing to testData * . This behavior is undefined.

Remove static void* data; . This is not necessary, here. Declare a new testData data = { 0 }; inside the main, right at the top. Now you pass testData * (converted to void * ) to Pa_OpenStream, Pa_OpenStream will pass this to your callback, where you can safely convert it back to testData * like you do. You might want to set the data elements before calling Pa_OpenStream ...

+4
source

To interact with a real-time data stream, you will need a mechanism that either sleeps (waiting on Windows) for the frame period (sample / sample rate per frame), or uses the stream synchronization primitive to start the stream int main , which is sound, ready to processing. This will give you access to every frame of data that PortAudio provides during its callback (called from the PortAudio stream). Here's how the concept works using boost::condition and boost::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; } 

Typically, this method is cross-platform, but this particular implementation can only work on Linux. On Windows, use SetEvent(eventVar) instead of condition::notify_one() and WaitForSingleObject(eventVar, duration) instead of condition::timed_wait(lock, duration) .

+1
source

In the first iteration, you closed the stream err = Pa_CloseStream(stream); . At the second iteration, the channel was already closed. Try the operation err = Pa_CloseStream(stream); after all iterations.

0
source

I solved it like this:

  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(); } } 
0
source

All Articles