Syncronising audio outputs on python script

39 views Asked by At

I have developed a python script that takes the audio from a .wav file and plays it out of multiple audio sources (currently configured such that it is out of laptop speakers and a paired bluetooth device). I am dealing with some delay and synchronisation issues. The audio is constructed very well and comes out with good quality, however, it is starting at different times on the two devices and I can't seem to figure it out.

I have tried implementing event's, positions, time stamps, and code to maybe even detect the latency to see if I can get it to sync up but even then I couldn't figure it out. Some help would be appreciated. Here is the code:

import sounddevice as sd
import wave
import numpy as np

# Query available devices
devices = sd.query_devices()
for device in devices:
    print(device) #Find your device ID

audio_file = #Audio Path
speaker_ids = [3,5] #Device IDs to play out of, one is computer speakers the other is bluetooth
wf = wave.open(audio_file, 'rb')

samplerate = wf.getframerate()
channels = wf.getnchannels()

# Set volume (0.0 to 1.0)
volume = 1

# Reading and processing audio data
audio_data = wf.readframes(wf.getnframes())
audio_data = np.frombuffer(audio_data, dtype=np.int16)
audio_data = np.reshape(audio_data, (-1, channels))
audio_data = audio_data * volume  # Apply volume control

# Function to create a callback for each stream
def create_callback(device_id):
    position = 0  # Starting position for this stream

    def callback(outdata, frames, time, status):
        nonlocal position
        end_index = position + frames
        if end_index > len(audio_data):
            # If the end of the audio data is reached, fill the rest with zeros
            valid_frames = len(audio_data) - position
            outdata[:valid_frames] = (audio_data[position:] / 32768.0)
            outdata[valid_frames:] = 0
            position = 0  # Reset position for looping
        else:
            # Normal playback
            outdata[:] = (audio_data[position:end_index] / 32768.0)
            position = end_index

    return callback

streams = []
try:
    for dev_id in speaker_ids:
        stream_callback = create_callback(dev_id)
        stream = sd.OutputStream(device=dev_id,
                                 samplerate=samplerate,
                                 channels=channels,
                                 dtype='float32',
                                 callback=stream_callback)
        streams.append(stream)
        stream.start()

    input('Press Enter to stop playback\n')

finally:
    for stream in streams:
        stream.stop()
    wf.close()

0

There are 0 answers