spatialaudio / python-sounddevice

:sound: Play and Record Sound with Python :snake:
https://python-sounddevice.readthedocs.io/
MIT License
1.02k stars 149 forks source link

Is it possible to mock a device for offline use? #431

Open bmcfee opened 2 years ago

bmcfee commented 2 years ago

This is a bit of an oddball question, but I haven't been able to figure it out from the docs or a (brief) source-dive, so I thought I'd ask here.

Background: I'd like to develop a demo for the librosa example gallery to demonstrate how to use sounddevice to do stream processing. The challenge here is that our doc build runs in a github workflow, and will not have access to an audio input device. It would be best if the example script can run out of the box without modification, and generate real output to be captured in the doc page (built by sphinx-gallery).

The solution I have in mind is to somehow provide a mock or virtual portaudio device that connects to an on-disk file or otherwise accessible and reproducible data stream. This way, a user could take the demo and get reasonable results with only changing the input device (which they'd probably have to do no matter what). Does this seem reasonable, or is there a simpler way that I'm not seeing?

If it does seem reasonable, any pointers for how best to set this up to work with sounddevice would be much appreciated. Thanks!

mgeier commented 2 years ago

I don't think this can be reasonably done within the Python module sounddevice.

I think it might be possible ....

You could try to implement a new PortAudio host API, or a new dummy device for an existing host API, but both of those don't sound easy.

To me, the most promising approach seems to be to use JACK. You can use my Python wrapper https://github.com/spatialaudio/jackclient-python to generate the desired dummy signal(s) and it also has an example for how to run jackd on Github Actions: https://github.com/spatialaudio/jackclient-python/blob/master/.github/workflows/main.yml (however, this currently doesn't work on Windows).

It's possible to run JACK in the background of a Github Action with jackd --no-realtime -d dummy &, but I haven't tried yet whether it's possible to run a Python instance with a JACK client in the background as well. But I would hope so!

Let me know what you find out!

bmcfee commented 2 years ago

Thanks for the thorough set of recommendations! Indeed, I didn't expect a solution within the python module, so some clever hackery on the environment side would seem necessary.

To me, the most promising approach seems to be to use JACK. You can use my Python wrapper https://github.com/spatialaudio/jackclient-python to generate the desired dummy signal(s) and it also has an example for how to run jackd on Github Actions: https://github.com/spatialaudio/jackclient-python/blob/master/.github/workflows/main.yml (however, this currently doesn't work on Windows).

It's possible to run JACK in the background of a Github Action with jackd --no-realtime -d dummy &, but I haven't tried yet whether it's possible to run a Python instance with a JACK client in the background as well. But I would hope so!

This sounds promising! Windows support isn't a huge problem, since our doc build only needs to happen once (on a linux host) and not across all platforms.

I might not have time to hack on this right away, but I'll report back with findings when I do. Thanks again!

mgeier commented 2 years ago

Windows support isn't a huge problem

It did actually work on Windows, but at some point there was an error during choco install, see e.g. https://github.com/spatialaudio/jackclient-python/runs/6739879410.

If you happen to know how to solve this (or how to use something other than choco), please let me know!