Search code examples
pythonaudiopython-sounddevicesurroundsoundcard

How to create a surround effect using python by controlling the volume of speakers channels?


I have bought a soundcard: Focusrite Scarlett 4i4 3rd Gen, with 4 outputs channels. I also have 4 speakers and I will link each speaker with the soundcard. I would like to be able to set separately the volume of each speaker, with maybe a tkinter interface (ultimately, but that is not the point).

I have seen that we could have plenty of different librairies (I'm using windows 10 for this project): the ones that seems to be interesting are sounddevice and soundcard.

I would though like to select the soundcard as my output device, and to specify which channel(s) must play sound right now. A good usage would be to have a .wav file in mono to implement in 1,2,3 or 4 speakers ; or a .wav file in stereo to implement in the same way, but with the first channel of the stereo in 2 speakers and the second channel of the stereo in the 2 other speakers. The perfect usage would be to create a surround 4.0 effect, making a square of speakers and being able to "turn around" with the sound: you can imagine that I put a sound of a train, and that this sound is turning around as if the train was turning around you.

sounddevice.AsioSettings() seems to allow us to control which output to use to play something, right? (https://python-sounddevice.readthedocs.io/en/0.3.15/api/platform-specific-settings.html) But when I see the doc in details, I also note that sounddevice.play() allows us to specify the mapping argument, which I don't really understand. (https://python-sounddevice.readthedocs.io/en/0.3.15/api/convenience-functions.html#sounddevice.play) I suppose that I will have to install Asio in all cases, which is not a problem (I hope!).

As my purpose is to control each speaker, what could I specify and how could I achieve that using the souddevice library or another one? Also, Is it possible to control the volume of each speakers, using those libraires or other ones (ex: pycaw)?

Thank you very much!

Elyurn

PS: If no solution exists with python, it would be a pleasure if you have ideas to achieve this goal another way (like a software able to do that for example).


Solution

  • Both the AsioSettings and the mapping argument are for statically selecting channels. You cannot use either to mix signals or change their volume.

    If you want to use the first few channels of your sound card in ascending order (e.g. channels 1, 2, 3 and 4), you don't need them at all. For example, you can simply use channels=4, which will select the first 4 channels. Even simpler, if you use sounddevice.play(), the number of channels will be determined by the given NumPy array, and you don't have to explicitly specify the channels parameter.

    If you know the desired movement (of the train in your example) in advance, you can pre-compute the 2- or 4-channel signal. Then you can simply play the multi-channel signal with sounddevice.play() (using AsioSettings or the mapping argument if needed).

    If you don't know the movement in advance (e.g. if it's computed in real-time), you can use a sounddevice.OutputStream and implement a custom callback function which does the weighting of signals.

    As for how exactly to mix the signal into the output channels, this doesn't really have anything to do with the sounddevice or soundcard modules. You can probably find signal processing libraries to do that, or you can implement it on your own. The appropriate search term for this is "panning". For two channels you can use "stereo panning", for more channels there are other methods, like "vector base amplitude panning (VBAP)", "Ambisonics amplitude panning", ...