dechamps / FlexASIO

A flexible universal ASIO driver that uses the PortAudio sound I/O library. Supports WASAPI (shared and exclusive), KS, DirectSound and MME.
Other
1.32k stars 71 forks source link

ASIO backend #67

Open kathampy opened 4 years ago

kathampy commented 4 years ago

I want to combine a WASAPI input device with an ASIO output device as a single FlexASIO device because most recording & monitoring applications only support opening a single ASIO device for both input and output. This seems technically possible if you support multiple backends simultaneously. It looks like PortAudio already supports ASIO backends.

Additionally, my output device supports simultaneous ASIO and WDM playback, so I can still use it in WASAPI applications while FlexASIO uses the ASIO interface.

I can work around it right now by using WASAPI shared mode in FlexASIO for both input and output, but that adds a lot of latency.

dechamps commented 4 years ago

This seems technically possible if you support multiple backends simultaneously.

Sadly, PortAudio does not support opening a full-duplex stream where the input and output devices are on different backends (Host APIs). Adding such support in PortAudio, or implementing it directly in FlexASIO, is not trivial. I wouldn't expect this feature to be implemented any time soon (or ever).

There's another potential problem with regard to using ASIO as a FlexASIO backend. ASIO was never designed for multiple drivers to be used in the same process - it uses global process state which gets in the way. I suspect that's a problem that can be worked around with some trickery, but it makes things more difficult.

kathampy commented 4 years ago

Another approach would be to create a virtual WDM playback device like VoiceMeeter / Hi-Fi CABLE & ASIO Bridge / Virtual Audio Cable (all by the same developer) which mixes WASAPI + ASIO audio into the backend running in WASAPI Exclusive / WDM-KS mode.

I could use VoiceMeeter for this, but I'm unable to achieve < 10 ms round-trip latency with its virtual ASIO & WDM devices.

dechamps commented 4 years ago

Another approach would be to create a virtual WDM playback device

That's way, waaaaay harder than anything else I mentioned so far. Writing a kernel-mode WDM audio driver is not for the faint of heart. I have neither the skills, nor the time, nor the motivation to embark on such a project.

VoiceMeeter / Hi-Fi CABLE & ASIO Bridge / Virtual Audio Cable (all by the same developer)

Virtual Audio Cable is not from the same developer as the other three.

kathampy commented 4 years ago

The SysVAD sample from the WDK seems to build a fully installable driver and virtual playback device. It's still a complicated task of course. I assumed the ASIO driver was similarly complex.

All those applications seem to be hosted on the same VB-Audio website.

In any case, thanks for building FlexASIO - it's great!

dechamps commented 4 years ago

I assumed the ASIO driver was similarly complex.

It's absolutely not. An ASIO driver is just a DLL that implements a fairly simple user-space API, nothing more. (It's really more of a "plug-in" than a "driver" from an OS perspective.)

A kernel-space driver is a vastly more complicated piece of machinery with a much wider interface, different development workflows, different challenges, many more moving parts, and way more opportunities for things to go wrong.

All those applications seem to be hosted on the same VB-Audio website.

Ah, right, I was confused because there are two things called "Virtual Audio Cable", and I was thinking of the other one.

nguyelc commented 3 years ago

Usage question: I'm currently using PortAudio and ASIO4ALL for my custom audio (Windows 10) app. How do I use FlexASIO in place of ASIO4ALL? Do I need to uninstall ASIO4ALL tp try it? Is there any code change require in how I set up the device using PortAudio? This seems like a great tool! Thanks in advance!

dechamps commented 3 years ago

Usage question: I'm currently using PortAudio and ASIO4ALL for my custom audio (Windows 10) app. How do I use FlexASIO in place of ASIO4ALL?

I'm confused by the question. No matter how I look at it, your use case doesn't make sense to me. Do you mean that you use ASIO4ALL through PortAudio, or that you're using ASIO and PortAudio side-by-side? And what would be the point of using FlexASIO, which is a wrapper around PortAudio, if you are already using PortAudio directly?

nguyelc commented 3 years ago

Sorry for the confusion. My intention is to take advantage of your Shared mode which ASIO4ALL doesn't support. Another purpose is to see if I can get better latency and performance in a remote setup (via Windows RDP). Currently, for remote setup, I use WMME as the backend which is bad with latency. I was hoping FlexASIO going through WASAPI may work better.

I'm using ASIO4ALL through PortAudio by defining a compile flag in PortAudio DLL build and in my project if that makes sense. Thanks!

dechamps commented 3 years ago

Either you're confused and don't understand what FlexASIO is, or I'm confused and still don't understand what you're trying to do.

FlexASIO is a thin wrapper around PortAudio. If you use FlexASIO as a PortAudio ASIO backend, what you'll end up doing is PortAudio → FlexASIO → PortAudio. In other words you're back where you're started, just with more layers (and thus more complexity, more latency and more places for things to go wrong). It's pointless.

Because FlexASIO is just an ASIO driver gateway to PortAudio, there is nothing you can do with FlexASIO that you can't already do in a simpler, more direct way using PortAudio itself.

You don't need FlexASIO to implement FlexASIO features in your application, because all FlexASIO features are PortAudio features behind the scenes, and you can just use these PortAudio features directly.

I'm not sure how I can make this clearer because I don't understand your reasoning for wanting to do this in the first place.

nguyelc commented 3 years ago

I admitted I was confused. Thanks for your explanation!

EvanBalster commented 2 years ago

I gather that an ASIO backend could enable FlexASIO to be used as a multi-client wrapper around some other ASIO driver, so that two applications can share low-latency access to IO.

Interestingly, it appears to be possible to replace the PortAudio DLL shipped with FlexASIO with one that supports ASIO. I'm going to see if this is enough to get a "multi-client adapter" working.

(in case anyone here is scrounging for a suitable PortAudio DLL, I happen to provide some here: https://imitone.com/support/portaudio/asio-wdmks-wasapi-mme/)

EvanBalster commented 2 years ago

Sadly it looks like my trick causes ASIO client applications to crash on startup. Presumably this has to do with (a) FlexASIO attempting to interact with the ASIO SDK as both a frontend and backend or (b) FlexASIO attempting to scan its own ASIO device when it initializes PortAudio.

If it's (b), this problem might be solved by compiling a modified PortAudio DLL that blocks FlexASIO from being loaded as part of its ASIO device enumeration.

If it's (a), then we need another process so that we have another instance of the ASIO SDK global state... That might be achieved by spawning another process and communicating with it through pipes.

dechamps commented 2 years ago

I gather that an ASIO backend could enable FlexASIO to be used as a multi-client wrapper around some other ASIO driver, so that two applications can share low-latency access to IO. Interestingly, it appears to be possible to replace the PortAudio DLL shipped with FlexASIO with one that supports ASIO. I'm going to see if this is enough to get a "multi-client adapter" working.

No, that won't be "enough". Just enabling the PortAudio ASIO backend is not sufficient to have multiple processes share the same ASIO driver instance. For that to work you'd need some kind of IPC for the client processes to coordinate with each other and mediate access to the ASIO driver. That's not going to happen magically.

Instead, what's going to happen is that the multiple PortAudio instances are going to act independently and each of them will try to initialize the backend ASIO driver for themselves. The behaviour is identical to using the backend ASIO driver directly… just with more steps, more complexity, and more opportunities for things to go wrong. As I said earlier in this thread, it's pointless.

Sadly it looks like my trick causes ASIO client applications to crash on startup.

Yes, you'll end up with a situation similar to #47 - FlexASIO will reenter init() in that case, and it's not designed to handle that. (Though you might also hit other issues such as PortAudio DLL ABI incompatibilities, depending on how your PortAudio DLL was built. If you want to make changes to the PortAudio DLL bundled with FlexASIO, I would recommend doing them inside the FlexASIO CMake superbuild system instead.)

There are probably ways to make a FlexASIO ASIO backend work (e.g. by having FlexASIO detect when it's re-entering init() and refuse to initialize in that case) but, again, just because you can, doesn't mean it makes sense. I still don't see how that could possibly be useful.

If you want to build some kind of multi-client ASIO driver wrapper, then it would make more sense to write a new ASIO driver that does just that (e.g. by coordinating around some kind of IPC mechanism). This doesn't really have anything to do with FlexASIO or PortAudio.

dechamps commented 2 months ago

In #239 @josephernest wrote:

In my case, it would help, because I could handle all my devices from a single config file. Laptop case : WASAPI2ASIO Studio case : ASIO2ASIO (!) with channel_count=16 Everything without changing the device in Ableton Live, just by automating the changes in the .TOML file (and your excellent feature watches the .toml filechange and restarts the driver!)

This is an interesting use case and the first time I see someone mention a good reason to want to use an ASIO backend behind FlexASIO.

However, I'm afraid I would still be unwilling to enable the ASIO PortAudio Host API in FlexASIO. The reason for this is PortAudio/portaudio#696 - the PortAudio ASIO support suffers from a significant design flaw that can seriously compromise the stability and performance of the driver, and it's not a flaw that can easily be fixed. I am not willing to compromise FlexASIO in that way.

There is one alternative: bypass PortAudio and have FlexASIO act as a thin wrapper between the host application and the underlying driver. This would be cleaner and vastly more reliable and performant. It wouldn't be too hard to implement, either (although there may be some subtleties around "injecting" reset requests behind the back of the driver when the config file changes). However I am not sure I would want to commit to implementing and maintaining that code for such a niche use case (i.e. just to make it possible to switch to a different ASIO driver through a FlexASIO config file). Especially since choosing a driver is supposed to be the host application's job - if the application makes that inconvenient then that seems like something the application should fix.

josephernest commented 2 months ago

Thanks @dechamps for the report.

However, I'm afraid I would still be unwilling to enable the ASIO PortAudio Host API in FlexASIO. The reason for this is https://github.com/PortAudio/portaudio/issues/696 - the PortAudio ASIO support suffers from a significant design flaw that can seriously compromise the stability and performance of the driver, and it's not a flaw that can easily be fixed. I am not willing to compromise FlexASIO in that way.

Do you think we could do the test in a branch? I'll document all the findings here for future reference. I can imagine it's just one or two lines of code to modify, when you scan all the PortAudio devices, there is surely a for (...) { if (backend == "ASIO") continue; , that we could just remove for the time of the test, in a separate branch?

Thanks in advance if we can try this little test :)

dechamps commented 2 months ago

I can imagine it's just one or two lines of code to modify

It is indeed a one-line change: abb8f98a3425dc9b3faf902b29f088ca6514cb53 (installer, workflow) I don't have any reason to believe this wouldn't work, but to be clear, I have not tested this at all, nor do I plan to. Assuming it works, the backend name is ASIO and you can use PortAudioDevices.exe as usual to list the "device" names (one device = one ASIO driver). You'll even see FlexASIO listing itself there which is hilarious if you're a fan of infinite recursion (needless to say that particular device won't work).

Feel free to give this a try if you like, but to be clear this will remain an experiment - due to PortAudio/portaudio#696 I will never accept this in FlexASIO proper. The only reason I made this build is because it took me literally 30 seconds to do it. If you want to keep using this and keep up with FlexASIO updates you'll essentially have to maintain your own fork.

josephernest commented 2 months ago

Thanks a million @dechamps for the build, I'll be happy to play with this :) BTW, your build process is amazing, fully automated. I have never used Github Actions build system (CI/CD), I have quickly looked, as I understand it, for each build, a Windows Server 2022 VM is started, and then it automatically compiles and builds the installer, and then makes it available for download. May I ask three small questions (and then I'll stop, because it's out of topic and I don't want to take too much of your time!):
(1) does it run on your own server or Github's servers? (2) if Github servers, do you have to pay for this service? (I see the servers ran during 15 minutes to do the build so I guess it's not free?) (3) Is 100% of the build config done in this single file continuous-integration.yml or did it require many other files to config the CI/CD? Anyway, brilliant! :tada:


Edit: Report after trying this test version: I tried FlexASIO-1.10b-asiobackend.zip.

No support asked of course, I know that I'm on my own if I want to continue this route, but I just wanted to post this for future reference :smile:

dechamps commented 2 months ago

(1) does it run on your own server or Github's servers?

It runs on GitHub's servers. See GitHub-hosted runners.

(2) if Github servers, do you have to pay for this service? (I see the servers ran during 15 minutes to do the build so I guess it's not free?)

It is free of charge for public (i.e. open source) repositories. FlexASIO is a public repository. See their billing docs. (And yes, that does sound too good to be true, but it is!)

(3) Is 100% of the build config done in this single file continuous-integration.yml or did it require many other files to config the CI/CD?

Nope, all the config is in that file.

If you want to try this yourself, you can for example fork FlexASIO, enable GitHub Actions on your fork, and then just push any commit. You will see in the Actions tab that the workflow immediately springs into life and a few minutes later your very own FlexASIO installer will be ready for you.

sadly it makes Ableton crash

That's surprising, but yeah you're on your own for that one.