eiz / SynchronousAudioRouter

Low latency application audio routing for Windows
http://sar.audio/
GNU General Public License v3.0
1.04k stars 138 forks source link

Configure button creates a second instance of ASIO driver in violation of ASIO spec #133

Open dechamps opened 1 year ago

dechamps commented 1 year ago

Hi, FlexASIO dev here.

In dechamps/FlexASIO#183 I observed that, if the "Configure" button in the SAR control panel is clicked while the ASIO driver is running, SAR will instantiate a second instance of the backend ASIO driver in the same process.

This is arguably a violation of the ASIO spec, given that the ASIO SDK really only supports a single global singleton instance of the ASIO driver running in the entire process, and ASIO drivers would therefore be justified in assuming that they will not be instantiated more than once in the same process at any given time.

Currently FlexASIO does make that assumption, and crashes as soon as SAR attempts to create the second instance. I'm going to make FlexASIO more resilient to this usage pattern, but I would recommend that SAR refrains from using ASIO drivers in ways that might not be compliant with the ASIO SDK, as that might cause problems with other backend drivers as well. (Especially since, in this particular case, this seems like an easy fix - if you already have an ASIO driver instance, then call controlPanel() on that instance instead of creating a new one.)

amurzeau commented 1 year ago

As I don't know that much about ASIO, is ASIO entirely single thread only across the ASIO API ? So I can assume that while the ASIO Host calls controlPanel(), the ASIO Host can't call anything else (like init()) ?

dechamps commented 1 year ago

That's a good question. The ASIO SDK is completely silent on the subject of thread safety (the only exception is that bufferSwitch() is called from the driver's own thread, which makes sense by necessity).

Given this, it would make sense to apply the robustness principle and for drivers to ensure their entry points are thread safe. At the same time, hosts should not assume drivers are thread safe.

In practice, FlexASIO doesn't really follow this principle (i.e. it's not thread safe). So far it has gotten away with it and I have not come across an ASIO host that would make concurrent calls to the driver. I hope no hosts do this, as that's really asking for trouble. Nonetheless, I filed dechamps/FlexASIO#185 to document this limitation.

Another interesting question is whether controlPanel() can be called before init(). Again the ASIO SDK is completely silent on this; however, one could argue that if init() is not called then the driver doesn't know which window handle to open the control panel under (that's provided as a parameter to init()), so such a sequence of calls would be at least somewhat awkward. Applying the robustness principle again, I would recommend that drivers be prepared to receive a controlPanel() call before init() (filed dechamps/FlexASIO#184 about that), while hosts should ideally call init() before controlPanel() - if only for drivers to have a window handle to create the control panel under.