1

I'm using PortAudio's callback API for designing a signal processing loopback library.

I'd like to add a branch that depends on a flag inside the callback, so like

int pa_callback(const void *inputuffer,
                 void *outputBuffer,
                 unsigned long frameCount,
                 const PaStreamCallbackTimeInfo *timeInfo,
                 PaStreamCallbackFlags statusFlags,
                 void *userData)
{
  if (do_something_flag) {
    do_something(inputBuffer, outputBuffer, frameCount);
  } else {
    do_something_else(inputBuffer, outputBuffer, frameCount);
  }

  return paContinue;
}

Where do_something_flag is set elsewhere in my program at regular intervals.

The PortAudio callback documentation states:

Before we begin, it's important to realize that the callback is a delicate place. This is because some systems perform the callback in a special thread, or interrupt handler, and it is rarely treated the same as the rest of your code. For most modern systems, you won't be able to cause crashes by making disallowed calls in the callback, but if you want your code to produce glitch-free audio, you will have to make sure you avoid function calls that may take an unbounded amount of time to execute. Exactly what these are depend on your platform but almost certainly include the following: 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. If you think short critical sections are safe please go read about priority inversion.

I don't care about the atomicity of the do_something_flag. That is, I don't care how many cycles it takes to get a correct value (within reason). According to the documentation, it looks like I can't use mutexes for setting/reading that variable.

1) What are my options?

2) If I make it global and set it in another part of my program (another thread), what is the absolute worst that will happen? Again, I mean in terms of corrupting data to the point of program failure/etc.

Is there a right way to do this?

justynnuff
  • 461
  • 1
  • 6
  • 20

1 Answers1

1

I'm not totally sure what you're exactly trying to do but I'm guessing it's what your title is asking about - "Changing a variable elsewhere".

Let's take this example: you have a variable frequency that changes over time. How do you access this? Well you have a generic pointer in the callback called userData. This can point to anything - a data structure, array, etc. I don't really remember how often the callback function gets called (it's pretty often... I wouldn't worry about speed) but the userData allows you to have variables that can be changed in your main thread while the pointer in the audio thread allows you to access it directly in the memory... My knowledge on thread safety isn't the best and sorry if that isn't the best explanation but I can at least show you how to do it through code (below).

This is how i usually do it but you don't need to do it yourself; I set a structure at the top of my file like so:

typedef struct {
    float freq;
    float vol;
}paData;

Obviously you'll initialize this somewhere in your code (probably in your main function call) and open the audio stream as such (data is of type paData):

/* Open audio stream */
err = Pa_OpenStream(&(*stream),
        &inputParameters,
        &outputParameters,
        SAMPLE_RATE, bufSize, paNoFlag, 
        paCallback, &data);

After opening it you can have your callback like this:

static int pa_callback(const void *inputBffer,
             void *outputBuffer,
             unsigned long frameCount,
             const PaStreamCallbackTimeInfo *timeInfo,
             PaStreamCallbackFlags statusFlags,
             void *userData)
{
    // cast data so we can use it
    paData *data = (paData *)userData;

    // what's our frequency?
    printf("%f\n", data->freq);

    /* Do something with your code here */

    return paContinue;
}

Hope that helps.

yun
  • 1,243
  • 11
  • 30
  • Yeah, that's perfect. I got so concerned with thread safety, atomicity, and global variables, I didn't see the simple answer: that PA gives you this mechanic. – justynnuff Jul 15 '16 at 21:01
  • Ya no worries it's hard to learn a new API by yourself... I fortunately had a great teacher when I learned PortAudio so it made things a lot easier. Good luck with what you're doing! – yun Jul 15 '16 at 21:09
  • HI @yun.cloud , I have recently started with speech acquisition and processing using portaudio and I am facing a bit of issues while implementing it. I have posted the question here on stackoverflow here (https://stackoverflow.com/questions/44645466/portaudio-real-time-audio-processing-for-continuous-input-stream). Any kind of help is greatly appreciated. – Ashish K Jun 22 '17 at 11:50