Error handling in juce::ASIOAudioIODevice

Hi!

I’m running into some problems when changing settings in the control panel of the ASIO driver.

The problem

Errors are detected in ASIOAudioIODevice::open, triggering assertions:

auto err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans);
jassert (err == ASE_OK);

Apparently, this (and other asio calls, like initDriver()) can fail with the driver I’m using (Merging Audio Device (MAD) by MERGING Technologies).

Here is the log of the errors (with JUCE_ASIO_DEBUGGING=1)

ASIO: kAsioResetRequest
ASIO: restart request!
ASIO: stopping
The thread 0x2ed8 has exited with code 0 (0x0).
ASIO: error: getSampleRate - Hardware Malfunction
ASIO: No sample rates supported - current rate: 0
ASIO: Rates: 
ASIO: clock: Ravenna Slave clock (cur)
ASIO: error: getSampleRate - Hardware Malfunction
ASIO: rate change: 0 to 44100
ASIO: error: setSampleRate - Invalid Parameter
ASIO:  Resetting
The thread 0x306c has exited with code 0 (0x0).
[ CMTAL_WorkingThread Thread is terminated ]
ASIO: ASIOInit: Driver failed to initialise
ASIO: error: getSampleRate - Hardware Malfunction
ASIO: disposing buffers
ASIO: creating buffers: 0, 192                               // totalBuffers, currentBlockSizeSamples
ASIO: error: create buffers 2 - Invalid Parameter
ASIO: error: Can't create i/o buffers - Invalid Parameter
JUCE Assertion failure in juce_MidiMessageCollector.cpp:39   // newSampleRate == 0

call stack for context:

 	LAMAConnect.exe!juce::MidiMessageCollector::reset(double newSampleRate) Line 39	C++
___ LAMAConnect.exe!juce::AudioProcessorPlayer::audioDeviceAboutToStart(juce::AudioIODevice * device) Line 370	C++
 	LAMAConnect.exe!juce::AudioDeviceManager::audioDeviceAboutToStartInt(juce::AudioIODevice * device) Line 1065	C++
 	LAMAConnect.exe!juce::AudioDeviceManager::CallbackHandler::audioDeviceAboutToStart(juce::AudioIODevice * device) Line 85	C++
 	LAMAConnect.exe!juce::ASIOAudioIODevice::start(juce::AudioIODeviceCallback * callback) Line 652	C++
 	LAMAConnect.exe!juce::ASIOAudioIODevice::timerCallback() Line 722	C++
 	LAMAConnect.exe!juce::Timer::TimerThread::callTimers() Line 120	C++

The calling ASIOAudioIODevice::timerCallback() also does not check for errors and proceeds to call theaudioDeviceAboutToStart callbacks, reporting sampleRate, and the input and output channels all to be 0.

I’m worried about this because some implementations (e.g. juce::AudioProcessorPlayer::audioDeviceAboutToStart) will
pass parameters on to juce::AudioProcessor::prepareToPlay and some processors might not handle such parameters gracefully (many juce classes have jassert(sampleRate > 0)).

Expected behaviour

I would expect such errors to be handled when they are detected in ASIOAudioIODevice::open, and the device to be
closed gracefully, without calling callbacks like audioDeviceAboutToStart with obviously invalid parameters.

Is it reasonable to expect such errors to be handled by juce (and to be reported through
AudioIODeviceCallback::audioDeviceError (const String&))?

How to fix this?

In my particular case, retrying the failed asio calls (asioObject->getChannels() and initDriver()) after some time seems eventually succeed:

// JUCE\modules\juce_audio_devices\native\juce_win32_ASIO.cpp : 410 open()
#if 1
int tryCount = 500;
while(tryCount > 0 && asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans) != ASE_OK)
 {
    --tryCount;
    Thread::sleep(10);
 }
 #endif
auto err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans);
jassert (err == ASE_OK);

I’m not sure if this is a good idea or not, so just a reliable way to detect the error and report it to the user (telling them to retry, which also seems to help) would also be helpful.

And if there is anything I can do on the application side to handle this gracefully, any advice is welcome

Thanks,
Daniel