Performance issue when using HostedAudioDeviceInterface

Hi!

I’m working on an application that uses the engine through HostedAudioDeviceInterface and I’m experiencing some performance issues when a larger number of channels (up to 128) is used.

Most of the time is spent in DeviceManager::rebuildWaveDeviceList (see callstack below) and calling
WaveInputDevice::setStereoPair on all inputs can take up to 3-4 seconds even in a release build.

Function Name	                                                       Total CPU [unit, %]	Self CPU [unit, %]
| + Application::initialise	                                           82480 (55.30%)      0 (0.00%)	
|| + juce::AudioSourcePlayer::setSource	                               80820 (54.18%)      0 (0.00%)	
||| + Application::prepareToPlay	                                   80820 (54.18%)      0 (0.00%)	
|||| + lama::EngineWrapper::prepareToPlay                              80820 (54.18%)      1 (0.00%)	
||||| + tracktion::engine::HostedAudioDeviceInterface::initialise      55002 (36.87%)      0 (0.00%)	
|||||| + tracktion::engine::WaveInputDevice::setStereoPair             54874 (36.79%)      0 (0.00%)	
||||||| + tracktion::engine::DeviceManager::setDeviceInChannelStereo   54874 (36.79%)      0 (0.00%)	
|||||||| - tracktion::engine::DeviceManager::rebuildWaveDeviceList     54874 (36.79%)     32 (0.02%)	     // [1]
|||||| - tracktion::engine::DeviceManager::initialise                    127 (0.09%)       0 (0.00%)	
|||||| + juce::AudioDeviceManager::setCurrentAudioDeviceType               1 (0.00%)       0 (0.00%)	
||||||| - juce::AudioDeviceManager::setAudioDeviceSetup                    1 (0.00%)       1 (0.00%)	

A workaround I tried

Deferring the rebuild so it happens only once (after HostedAudioDevice::initialise(), before
HostedAudioDevice::prepareToPlay()) seems to help with the performance issue, and so far it does not seem to break
anything.

This required two changes. One in the engine to bypass the device list rebuild and and one in my application to call it
once the engine is initialized:

diff --git a/Source/Engine.cpp b/Source/Engine.cpp
index cfdcca9..e485e69 100644
@@ -1325,6 +1325,8 @@ void EngineWrapper::prepareToPlay(const te::HostedAudioDeviceInterface::Parameters)
             waveInputDevice->setStereoPair(true);
         }
     }
+    engine_.getDeviceManager().rebuildWaveDeviceListIfNeeded();

     hostedAudioDeviceInterface_.prepareToPlay(parameters.sampleRate, parameters.blockSize);
}


Submodule tracktion_engine contains modified content
diff --git a/tracktion_engine/modules/tracktion_engine/playback/tracktion_DeviceManager.cpp b/tracktion_engine/modules/tracktion_engine/playback/tracktion_DeviceManager>index b64e85eb99..eec4800de9 100644
@@ -923,7 +923,7 @@ void DeviceManager::setDeviceInChannelStereo (int chan, bool isStereoPair)
         }
     }

-    rebuildWaveDeviceList();
+    // rebuildWaveDeviceList();
}

Questions

  • Would an optimization like this make sense or is it necessary to rebuild the device list every time?
  • If this can work, is there a chance that the engine could handle this, without the application having to trigger the rebuild?
  • If that cannot work for any reason, is there anything else I could do to work around the issue?
  • Am I using HostedAudioDeviceInterface::initialise correctly? the EngineInPlugin example only calls it once, but it takes parameters that might change after that, so I think I do need to call it in prepareToPlay

Thanks,
Daniel

DeviceManager::setDeviceInChannelStereo should probably only need to call rebuildWaveDeviceList(); if it changes.

Can you change the function to this and see if it helps?

void DeviceManager::setDeviceInChannelStereo (int chan, bool isStereoPair)
{
    chan &= ~1;

    if (inStereoChans[chan / 2] == isStereoPair)
        return;
    

Can you see how many times rebuildWaveDeviceList() is called in your case then? It really only needs to be called once for all of these changes. I can probably add a way to just disabled this whilst multiple calls are made.


If a single call is still taking on the order of half a second or so, can you let me know what inside that function is taking the time? I might be able to optimise that a bit for hosted audio devices.

1 Like

The change you suggested seems to work.

rebuildWaveDeviceList() is still called once or twice when I select a different device or change the enablement on one of the channels, but I think that is expected. After that therebuildWaveDeviceList() call is skipped when setDeviceInChannelStereo is called for each channel.

A single rebuild does not take too long so not doing it repeatedly for each channel seems to be enough to fix the performance issue.

1 Like

Hey!

I think I found a commit (on the feature/launcher branch) that addresses the
issue and I was wondering if we can expect this to be merged into develop anytime soon?

If not, then I guess I can still cherry-pick this one commit

Thanks

Sorry, I meant to cherry pick that on to develop.
Here now:

1 Like

looks good Thank you!