Search code examples
pythonpython-sounddevice

How to play a sound file using an OutputStream in python sounddevice?


The goal is to play two sound files simultaneously so the sounddevice.play function is not an option. I believe that I should make an OutputStream for each file. Right now I'm stuck in trying to get one OutputStream to work. Please help.

My code so far:

import soundfile as sf
import sounddevice as sd

data, fs = sf.read('sound_file.wav', dtype='float32')

def callback(outdata, frames, time, status):
    outdata[:] = data

with sd.OutputStream(callback=callback):
               pass

Edit: switched to RawOutputStream

import soundfile as sf
import sounddevice as sd

wf = sf.SoundFile('sound_file.wav')

def callback(outdata, frames, time, status):
    data = wf.buffer_read(frames, dtype='float32')
    if len(data) <= 0:
        raise sd.CallbackAbort
    if len(outdata) > len(data):
        raise sd.CallbackAbort #wrong obviously
    outdata[:] = data

with sd.RawOutputStream(channels=wf.channels,
                        callback=callback) as stream:
    while stream.active:
        continue

Solution

  • import soundfile as sf
    import sounddevice as sd
    import threading
    
    def _play(sound):
        event =threading.Event()
    
        def callback(outdata, frames, time, status):
            data = wf.buffer_read(frames, dtype='float32')
            if len(outdata) > len(data):
                outdata[:len(data)] = data
                outdata[len(data):] = b'\x00' * (len(outdata) - len(data))
                raise sd.CallbackStop
            else:
                outdata[:] = data
    
        with sf.SoundFile(sound) as wf:
            stream = sd.RawOutputStream(samplerate=wf.samplerate,
                                        channels=wf.channels,
                                        callback=callback,
                                        blocksize=1024,
                                        finished_callback=event.set)
            with stream:
                event.wait()
    
    def _playsound(sound):
        new_thread = threading.Thread(target=_play, args=(sound,))
        new_thread.start()
    
    _playsound('sounds_file.wav')