Audio stops working after changing samplerate externally

Hello JUCE,

When changing the sample rate from an external source such as Audio Midi Setup, the audio functionality in JUCE (DemoRunner) stops working. This issue is fixed by forcing JUCE to re-initialize the audio device, for instance by adjusting settings like sample rate, buffer size, or channels within the DemoRunner settings.

This problem occurs when using a matching pair device where both input and output use the same internal sample rate. There is no problem when using the mac default microphone and speaker, JUCE audio will keep working when the samplerate of one of these two changes externally.

To reproduce the problem:

  1. Launch JUCE DemoRunner.
  2. Choose a matching pair audio device for both input and output (e.g., Scarlett Solo 4th gen) within the DemoRunner audio settings.
  3. Verify AudioSynthesiserDemo.h works.
  4. Change the sample rate in Audio Midi Setup.
  5. Note that the sample rate in DemoRunner Settings remains unchanged.
  6. Note that AudioSynthesiserDemo.h no longer makes any sound.

Test setup:

  • JUCE DemoRunner 7.0.11
  • macOS 14.3.1 (23D60)
  • M1 chip
  • Tested with Scarlett Solo 4th gen and Audient ID22

Best,

Jelle

Hi Jelle,
I usually work around this using a Timer.
It basically boils down to:

  • Have a Timer running with a non expensive speed, e.g. 1 second
  • In the timerCallback implementation, check that the app is the currently focused one (juce::TopLevelWindow::getActiveTopLevelWindow() comes to the rescue)
  • If the app is the currently focused one, check the value of your desired sample rate against the one that’s currently being set by the device
  • If they don’t match (and the device supports the desired sample rate, this is important as it can happen also on Windows where WASAPI is a bit obtuse), reconfigure the AudioDeviceManager accordingly

Please note that in my case I want to “force” the sample rate that I want in my app.
If you instead would like to “adapt” to the current sample rate set by CoreAudio then you could maybe use the same workaround but with some adjustments at the end.

Hope this helps :wink:

@masshacker your timer approach looks useful when a specific sample rate is required. However, in my application, the specific sample rate isn’t a requirement, I simply want JUCE audio functionality to remain working. I presumed that this would be handled automatically by JUCE.

I assumed the issue I reported was a bug but after the comment by @masshacker I realized I might be wrong. Could someone from the JUCE team reply?

The AudioDeviceManager is a ChangeBroadcaster. Have you tried to add a listener and call restartLastAudioDevice() there?

I could… but how do I know if the device is not running? Also, isn’t it the responsibility of device manager to take care of it automatically?

If the sampleRate changes, the audio pipeline i.e. your code has to be prepared fresh by calling AudioIODeviceCallback::audioDeviceAboutToStart() so you can prepare your buffers etc. according to the new settings.

Some code might want to do other things as well and not blindly restarting.

Hence it is an opt-in feature.

If this is the reasoning then there is some inconsistencies in behaviour:

If I select in DemoRunner “MacBook Air Microphone” as input and “MacBook Air Speakers” as output and change the samplerate of either of these in Audio Midi Setup, then the DemoRunner will automatically change the samplerate to follow that new samplerate.

However, if I set input or output to << none >> OR use a matching input/output device like in my initial description then the DemoRunner doesn’t automatically change the samplerate.

I am second guessing the API, I haven’t written it.
Maybe you get an answer from the authors

1 Like