Multiprocessing?
When I run sd.play in a flask app or ipython terminal, I get some sonic artifacts when there is additional load on Python. Have you tried running SD in multiprocessing, any advice on how to do that?
FWIW, I hammered out a test class in multiprocessing. I haven't tested it on my other machine yet, so I can't say if it helped with the blips and bops. For those who need MP, it might help:
import numpy as np
from multiprocessing import Process, Queue
fs = 48000
rand_data = np.random.rand(48000 * 5) * 0.1
class AudioMan(Process):
"""Audio Manager
The multiprocessing.Process class causes two
instances of the class to exist. The first is in the parent
process. When .start is called, a second process and instance
are created. Sounddevice is imported in .run to avoid importing is
twice, which causes some kind of PortAudio crash."""
def __init__(self):
self.state = 'stopped' # Set to quit to exit.
self.q = Queue()
self.sd = None
super(AudioMan, self).__init__()
def run(self):
# Sounddevice can't handle being imported in multiple processes.
# importing it in `run` causes it to only be imported in the
# spawned process.
import sounddevice as sd
self.sd = sd
while self.state != 'quit':
cmd = self.q.get()
if cmd == 'quit':
self.state = 'quit'
else:
# Example: pass ('play', 'foo.wav')
if isinstance(cmd, tuple):
method, args = cmd
getattr(self, method)(args)
def play(self):
self.q.put(('_play', 'foo.wav'))
def _play(self, fn):
self.sd.play(rand_data, 48000, device=1)
print(fn)
def stop(self):
self.q.put(('_stop', None))
def _stop(self, _):
self.sd.stop()
def quit(self):
self.q.put('quit')
I don't really have any experience with multiprocessing, but I would have suggested what you have already done in your code example:
- import
sounddeviceonly in one process - communicate with a queue
In general, I would advise to avoid multiprocessing unless it is absolutely necessary.
There are already a few issues related to multiprocessing: #120, #147, #154, #302, ...
I get some sonic artifacts when there is additional load on Python.
Did you manage to reduce/avoid those with multiprocessing?
Another possible approach would be to use a callback function implemented in C (or another compiled language) and make sure that the callback function never takes the GIL. I've experimented with that approach at https://github.com/spatialaudio/python-rtmixer. A similar thing might even work with Cython, but I've not tried this.