JUCE icon indicating copy to clipboard operation
JUCE copied to clipboard

[Bug]: Cumulating Xruns when changing soundcard settings (Reproduced in AudioPluginHost example)

Open Sidelobe opened this issue 9 months ago • 0 comments
trafficstars

Detailed steps on how to reproduce the bug

We've been struggling with a strange issue where suddenly there are lots of Xruns when changing the sound card settings. The Xruns keep adding up and there's no recovery.

I happens often but not always. The easiest way to reproduce it has been with Blackhole (virtual sound card https://github.com/ExistentialAudio/BlackHole) in macos. I thought it was a BlackHole issue, but since I was able to reproduce it without, I'm posting this here.

  • Tested/Reproduced with Juce 7.0.12on macos Sonoma and windows 11 (debug), macbook M1
  • Tested/Reproduced with Juce 8.0.6 on macos Sonoma (debug), macbook M1

IMPORTANT DETAILS: So far, I've only been able to reproduce this with virtual soundcards (e.g. Loopback, Parallels sound card,...). There needs to be a certain system/CPU load (e.g. >25%) to reproduce this somewhat consistently. That's why I modified the example reverb and burn some cycles in it with memcpys (see code below) --this gets me to roughly 60% CPU in debug build.

With certain sound card settings, the Xruns start increasing... and never stop, until I switch sound cards again: Image

Has anybody experienced somehting similar?

I'm able to reproduce it on AudioPluginHost example with minimal modifications:

in extras/AudioPluginHost/Source/UI/MainHostWindow.h

In MainHostWindow class, add this private class instance:

class XrunConsolerLogger : public juce::Timer
 {
 public:
     XrunConsolerLogger(AudioDeviceManager& deviceMgr) : m_deviceMgr(deviceMgr) { startTimer(500); }
     void timerCallback() override
     {
         std::cout << "Xruns: " << m_deviceMgr.getXRunCount() << std::endl;
     }
 private:
     AudioDeviceManager& m_deviceMgr;
     
 } xrunLogger;

in extras/AudioPluginHost/Source/UI/MainHostWindow.cpp

add xrunLogger(deviceManager) to MainHostWindow ctor intitializer list

Image

in extras/AudioPluginHost/Source/Plugins/InternalPlugins.cpp

Add this at the start of ReverbPlugin::processBlock()

        // burn cycles with some dummy memcpy
        const float* const* in = buffer.getArrayOfReadPointers();
        float* const* temp = buffer.getArrayOfWritePointers();
        float* const* out = buffer.getArrayOfWritePointers();
        
        // When CPU load is >100%, we get the same behaviour .. Xruns...
        int numIterations = 50000;
        for (int k=0; k<numIterations; ++k) {
            for (int i=0; i<buffer.getNumChannels(); ++i) {
                memcpy(temp[i], in[i], buffer.getNumSamples()*sizeof(float));
                temp[i][0] += 1e-9f;
                memcpy(out[i], temp[i], buffer.getNumSamples()*sizeof(float));
            }
        }

GIT Patch

Here's a Git Patch for all these changes. JUCE-AddXrunLogger_ReverbLoad.patch

What is the expected behaviour?

I would expect that - if Xruns happen - the system recovers, i.e. drops the frames and the Xruns don't keep increasing.

Operating systems

Windows, macOS

What versions of the operating systems?

This happens on macos 14.7.1 (Sonoma) and Windows 11 for ARM.

Architectures

Arm64/aarch64

Stacktrace


Plug-in formats (if applicable)

No response

Plug-in host applications (DAWs) (if applicable)

AudioPluginHost example with minimal modifications

Testing on the develop branch

I have not tested against the develop branch

Code of Conduct

  • [x] I agree to follow the Code of Conduct

Sidelobe avatar Feb 14 '25 13:02 Sidelobe