pythonpython-sounddevice

Using Python sounddevice for multichannel acquisition on two devices


I'm trying to output upto 4 channels of audio from my RME Fireface UCX sound card and acquire 16 channels of audio data on my miniDSP 16-channel microphone array. At present if I use a combination of the listed devices from the query_devices command I can get a 16-in/2-out system working but I can't seem to increase the number of output channels.

From the list of devices:

0 name:Microsoft Sound Mapper - Input input ch:2 out ch:0
1 name:ADAT (7+8) (RME Fireface UCX) input ch:2 out ch:0
2 name:Line (miniDSP micArray Multi-ch input ch:2 out ch:0
...
46 name:ASIO Fireface USB input ch:18 out ch:18
47 name:ASIO4ALL v2 input ch:2 out ch:2
48 name:miniDSP ASIO Driver input ch:16 out ch:2
...
60 name:Line (miniDSP micArray Multi-channels) input ch:16 out ch:0
72 name:Line (nanoSHARC micArray16 UAC2.0) input ch:16 out ch:0
73 name:Analog (1+2) (Fireface Analog (1+2)) input ch:0 out ch:8
74 name:Analog (1+2) (Fireface Analog (1+2)) input ch:2 out ch:0
...

So if I have x (which is a numpy array with 44100*5 rows by three columns to represent 5 secs of 3-channels of data sampled at 44.1 kHz (=fs)) I can do:

sd.default.device = [46, 46]
rx_data = sd.playrec(x, samplerate=fs, channels=8)

and this simultaneously plays 3 channels of output to my speakers and acquires 8 channels on my RME soundcard input. Similarly, if I do:

duration = 5
sd.default.device = [48, 48]
sd.rec(int(duration * fs), samplerate=fs, channels=16)

I can get my miniDSP to record 16-channels of audio for 5 secs. Also, if I do:

sd.default.device = [60, 73]
rx_data = sd.playrec(x, samplerate=fs, channels=16)

I can get the miniDSP h/w to acquire 16 channels of audio whilst the RME outputs only 2-channels (yes only 2-channels despite the numpy array having 3 columns and the device list saying that device can output 8 channels ?). This is ok, but as I said I would like perhaps 3 or 4 output channels of audio.

I thought about combining the ASIO devices i.e.

sd.default.device = [48, 46]
rx_data = sd.playrec(x, samplerate=fs, channels=16)

But this (not unsurprisingly) results in the PortAudioError -9993 Illegal combination of I/O devices.

So I have considered using InputStreams and OutputStreams, but I didn't quite follow some of the example code in the sounddevice docs. But maybe this is the way forward on this problem ?


Solution

  • The PortAudio library (which is used in the sounddevice module) doesn't really allow a stream with different input and output (hardware) devices. It may work for some host APIs and some combination of devices, but it is not officially supported.

    See also https://github.com/spatialaudio/python-sounddevice/issues/154.

    You might be able to combine multiple devices into a "virtual device" on the operating system level, but I don't know if/how that works on Windows (it may work on Linux/ALSA with the configuration file .asoundrc and on macOS by defining "aggregate devices").

    If that's not an option, you can use different streams for input and output (as you mentioned in your question). In this case, the timing of the input and output blocks generally doesn't match, so you shouldn't try to process inputs and outputs in sync. You should rather use something like a queue.Queue to transport audio data from the input callback to the output callback.

    There are indeed no examples for that in the documentation. But you can try to combine rec_unlimited.py (for an input callback) with play_long_file.py (for an output callback).