83 lines
2.8 KiB
Python
83 lines
2.8 KiB
Python
from threading import Thread, Event
|
|
import sounddevice as sd
|
|
import soundfile as sf
|
|
from config import socketio
|
|
|
|
import queue
|
|
|
|
|
|
def play_wav_async(wav_file, stop_string):
|
|
wavThread = WavPlayer(wav_file, socketio=socketio, stop_string=stop_string)
|
|
wavThread.start()
|
|
return wavThread
|
|
|
|
def play_wav(wav_file, stop_string):
|
|
wavThread = WavPlayer(wav_file, socketio=socketio, stop_string=stop_string)
|
|
wavThread.run()
|
|
return wavThread
|
|
|
|
class WavPlayer(Thread):
|
|
'''
|
|
Thread for running server side matrix test operations
|
|
'''
|
|
def __init__(self, wav_file, socketio=None, stop_string="stop"):
|
|
super(WavPlayer, self).__init__()
|
|
self.socketio = socketio
|
|
self._stopevent = Event()
|
|
self.wav_file = wav_file
|
|
|
|
def join(self, timeout=None):
|
|
""" Stop the thread. """
|
|
self._stopevent.set()
|
|
try:
|
|
Thread.join(self, timeout)
|
|
except RuntimeError:
|
|
pass
|
|
|
|
|
|
def play_wav_async(self, wav_file, buffersize=1024, blocksize=1024, socketio=None):
|
|
q = queue.Queue(maxsize=buffersize)
|
|
|
|
def callback(outdata, frames, time, status):
|
|
assert frames == blocksize
|
|
if status.output_underflow:
|
|
print('Output underflow: increase blocksize?', file=sys.stderr)
|
|
raise sd.CallbackAbort
|
|
assert not status
|
|
try:
|
|
data = q.get_nowait()
|
|
except queue.Empty:
|
|
print('Buffer is empty: increase buffersize?', file=sys.stderr)
|
|
raise sd.CallbackAbort
|
|
if len(data) < len(outdata):
|
|
outdata[:len(data)] = data
|
|
outdata[len(data):] = b'\x00' * (len(outdata) - len(data))
|
|
raise sd.CallbackStop
|
|
else:
|
|
outdata[:] = data
|
|
|
|
with sf.SoundFile(wav_file) as f:
|
|
for _ in range(buffersize):
|
|
data = f.buffer_read(blocksize, dtype='float32')
|
|
if not data:
|
|
break
|
|
q.put_nowait(data) # Pre-fill queue
|
|
|
|
stream = sd.RawOutputStream(
|
|
samplerate=f.samplerate, blocksize=blocksize,
|
|
channels=f.channels, dtype='float32',
|
|
callback=callback, finished_callback=self._stopevent.set)
|
|
with stream:
|
|
timeout = blocksize * buffersize / f.samplerate
|
|
while data and not self._stopevent.isSet():
|
|
data = f.buffer_read(blocksize, dtype='float32')
|
|
q.put(data, timeout=timeout)
|
|
self._stopevent.wait() # Wait until playback is finished
|
|
return stream
|
|
|
|
def run(self):
|
|
'''
|
|
This function is called when the thread starts
|
|
'''
|
|
return self.play_wav_async(self.wav_file)
|