How to pass auxiliary data to audioDeviceIOCallbackWithContext()?

Hello, recently I faced a problem with providing some auxiliary data to audio callback. audioDeviceIOCallbackWithContext() accepts AudioIODeviceCallbackContext as the last parameter, but AudioIODeviceCallbackContext struct is not extensible. The question is how can I pass some custom data to audioDeviceIOCallbackWithContext()? I thought that context parameter was intended for this purpose but does not look like.

There’s nothing built in to accomplish this. Perhaps there’s some other way of achieving a similar result. How are you calling audioDeviceIOCallbackWithContext? What extra data are you trying to include?

Basically, I need to associate a metadata with each block of audio data. Currently it’s just 2 64-bit integers but possibility to include any data is preferred.

There’s nothing built in to accomplish this

Any strong reason to apply such restriction on interface which is supposed to be generic?

Do you definitely need to use AudioIODeviceCallback? i.e. are you writing a custom AudioIODevice that has to work with the AudioDeviceManager, or similar? If not, it might make more sense to just write your own audio callback interface that can accept the extra metadata.

The purpose of the interface is to get audio data in and out of an AudioIODevice. I don’t think it’s supposed to be ‘generic’, in the sense that it’s not intended to deal with arbitrary non-audio data, or interacting with devices other than AudioIODevices.

I’m also not really sure there’s a reasonable way of safely bundling arbitrary data into the audio device callback. The first option would be to add something like a std::any parameter to audioDeviceIOCallbackWithContext, which would require runtime checks when extracting the additional data. This is quite a heavyweight (and awkward to use!) solution to a fairly rare problem, so difficult to justify. A second option would be to add some sort of template parameter to the interface, so it becomes AudioIODeviceCallback<T>. The disadvantage of this approach is that now, all the types that interact with AudioIODeviceCallback need to be templated, and need to agree on the type of T, which is not viable in practice.

For all of the AudioIODevices included with JUCE, the AudioIODeviceCallback allows retrieving all of the data produced by those devices. If you want to write a custom device type that produces other sorts of data, then perhaps AudioIODevice is not the correct abstraction. Similarly, if you’re writing some sort of audio processor that requires additional metadata, then maybe AudioIODeviceCallback is not the best interface to use to receive the additional information.


I’m sure there’s a good solution to the problem posed by the original poster, but at the moment there’s not enough information about the requirements to make a good suggestion. I’m not sure whether the goal is to write a custom AudioIODevice that produces additional information, or to write some new kind of audio processor that requires additional metadata during processing, or something else. I also don’t know whether AudioDeviceManager interop is necessary. Depending on the problem, “passing auxiliary data to audioDeviceIOCallbackWithContext” is likely just one potential solution out of many.

1 Like

The context parameter is not for handling your own additional data. So it’s not like what is done with C based callback interfaces where they often have something like a “context” parameter because of the limitations of the language. Here with the Juce case, the context is for additional data that Juce may want to provide for the callback. Currently this only seems to contain a pointer that will point to a timestamp, if available from the system.

Unless I am misunderstanding something, you’d simply do the normal C++ thing and inherit from juce::AudioIODeviceCallback and deal with your additional needed data as member variables in any way you like.

So, something like :

class MyAudioEngine : public juce::AudioIODeviceCallback
{
public:
  int mycustomdata = 0;
  void audioDeviceIOCallbackWithContext (const float* const* inputChannelData, int numInputChannels,
                                           float* const* outputChannelData, int numOutputChannels,
                                           int numberOfSamples, const juce::AudioIODeviceCallbackContext& context) override
{
  // generate/process your audio here and the "mycustomdata" variable is accessible
}
void audioDeviceAboutToStart (juce::AudioIODevice* device) override {}
virtual void audioDeviceStopped() override {}
};
1 Like

are you writing a custom AudioIODevice that has to work with the AudioDeviceManager, or similar?

yes, this is the case

In that case, a simple workaround would be to derive your own callback type from AudioIODeviceCallback, and then to attempt a dynamic_cast to the derived type in your custom AudioIODevice’s callback.