I am writing a program that uses PortAudio to get audio input from the computer into my program. PortAudio, in their Writing a Callback tutorial, says that the callback is triggered as an Interrupt Handler
, and explains that code written in the callback needs to not do:
memory allocation/deallocation, I/O (including file I/O as well as console I/O, such as printf()), context switching (such as exec() or yield()), mutex operations, or anything else that might rely on the OS
The problem I'm having is figuring out how would I go about processing the audio coming from the callback without being able to use malloc
. My current (and working) callback looks like this.
int Audio::paCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) {
// Cast data passed through stream to our structure.
auto *in = (uint16_t *) inputBuffer;
// Get number of audio channels, for instance stereo would be two
int numberOfChannels = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice())->maxInputChannels;
for (int i = 0; i < numberOfChannels; i ) {
auto storedStream = (uint16_t *) malloc(sizeof(uint16_t) * (framesPerBuffer 2));
storedStream[0] = uint16_t(i);
storedStream[1] = uint16_t(framesPerBuffer);
for (int j = 0; j < storedStream[1]; j ) {
storedStream[j 2] = in[numberOfChannels*j i];
}
audioQueue->push(&storedStream);
}
return 0;
}
I know I shouldn't be using malloc
, so how would I go about fixing this?
While looking for examples of code, I found that Audacity, a free audio editing software, uses PortAudio. I have looked at Audacity's PortAudio callback (line 2391 in AudioIO.cpp
), and all they do is call two functions. In the second function, they call alloca
, which allocates memory.
Is it ok to call functions from an interrupt handler that then do memory allocation, or would the functions also be executed within the interrupt context?
CodePudding user response:
Generally to avoid dynamically-allocated memory, we'll employ the use of various 'static containers.' Things like a circular buffer of pre-allocated and reserved data, or a blit buffer (two static buffers, where new data is added to one buffer, while previously-added data is processed from a second buffer. Periodically their roles are 'swapped' when all the data in the 'processing' buffer is empty). In both cases, it helps to know the 'maximum' amount of data you're likely to need, and pre-allocate that much data.
Is it ok to call functions from an interrupt handler that then do memory allocation, or would the functions also be executed within the interrupt context?
This really depends on your target platform. Sometimes avoiding dynamic memory allocation is very important, and sometimes on modern embedded platforms, the concern is quite a bit overblown.