ASIO Issue

Hi
I discovered an issue opening ASIO drivers on windows.
If you load the same driver twise (re-loading)
Juce will try to close the driver first, around line 397 of juce_win32_ASIO.cpp

   if (isOpen())
        close();

When stopping it is asumed that asioObject will go to nullptr during several Thread::sleep(20) in the close() function.
This is not always the case it seems.
I get crashes using this. So i added

asioObject = nullptr;

after the last sleep. that seems to solve the issue.
I have no idea if this was a good solution or not? any ideas?

Can you provide some more information? Where is the crash occurring? If you can provide a minimal reproducible example or at least a stack trace of the crash this will help to track it down.

Hi
Sorry for not being clear.
It is not so easy to explain. I only get the issue with RME devices (as far as I can tell)
There is no need to find the bug, I believe I found it, it seems like a dangling pointer to asioObject. Let me try to explain again.
The issue is only present when I have the RME device open, and then call setAudioDeviceSetup with the same device as is already open, with (or without) the same device settings.

  1. Call AudioDeviceManager::setAudioDeviceSetup(newSetup,true) ← With the currently open RME device
  2. Now, ASIOAudioIODEvice::open() is called (in juce_win32_ASIO.cpp)
    First thing this function does is this: (line 397)
if (isOpen())
        close();

Since the device is open, it will try to close it and continue on.
Now, a bit further down on line 405, it only opens the device if asioObject == nullptr, and it seems that is not always the case.

    if (asioObject == nullptr)
    {
        auto openingError = openDevice();

        if (asioObject == nullptr)
            return openingError;
    } 

With the RME device we get a crash on the next line:

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

The problem is that asioObject is not nullptr, so the device is never opened before the call to asioObject->getChannels. since asioObject points to the closed device (or god knows what) you get an access violation.

The source of this, as far as I can tell, is in the close() function:
In the close() function you have this:

if (asioObject != nullptr)
{
Thread::sleep (20);
asioObject->stop();
Thread::sleep (10);
disposeBuffers();
}

        Thread::sleep (10);

Then it returns, but asioObjects is never set to nullptr, but is rather a dangling pointer, and thereby the device is not opened (as pointed to earlier).

if I add

asioObject = nullptr ;

after the last sleep(10) it all seems to work fine.
Problem is, I don’t know enough about the underlying logic here to know if it is a stupid fix or not.

1 Like

Thanks for the info. I’ve done a bit of testing, though I don’t have an RME device to test with unfortunately. I’ve got a simple audio app which just scans the available devices and then calls AudioDeviceManager::setAudioDeviceSetup() with the same device twice.

From what I can see, IASIO::stop() doesn’t release the asioObject and just stops the driver. I’ve tested with ASIO4ALL v2, Realtek ASIO, and Generic Low Latency ASIO Driver drivers and none of them are nullptr after the call to close(). I think this is the expected behaviour since we’re using the same driver and don’t want to fully destroy and re-open it, just stop it whilst we configure the new setup. All of these drivers cope fine with the following call to getChannels() so it seems to be a bug with the RME driver specifically, perhaps it is releasing itself after the call to stop()?

Can you enable JUCE’s ASIO logging by defining JUCE_ASIO_DEBUGGING and see if you get any useful information from the debug log? Other than that I’m not sure what to suggest I’m afraid as I don’t think we want to release the driver fully like in your fix.

Possibly related topic: ASIO issue - buffer sizes not being correctly updated