python-sounddevice icon indicating copy to clipboard operation
python-sounddevice copied to clipboard

delay at beginning of input signal

Open toonpeters opened this issue 2 years ago • 1 comments

In this function, we simultaneously play a testsound (ts) multiple times with a pause in between and record its total response sound. The problem is that the recorded signal sound contains unwanted zeros in the beginning. This acts as a delay because the beginning of the recording is indeed the beginning of the useful sound after the delay. Furthermore, because of the delay in the beginning, the end of the sound is also not recorded (not in the put in the queue. This is clarified in the plot (in time domain (s), the blue stripes are non important here)

How can I get rid of the delay in the beginning?

image

import numpy as np 
import queue
import sounddevice as sd
import threading

N_ts = 20
blocksize = 8

#make quick sine signal
seconds = float(50)/1000  # put to ms
t_arr = np.linspace(0, seconds, int(44100*seconds))
ts = np.sin(2 * np.pi * 440 * t_arr)
ts = ts * (2**15 - 1) / np.max(np.abs(ts))
ts = ts.astype(np.int16)

def callback(indata, outdata, frames, time, status):

        global current_frame, prerec, postrec, totalcount, cuts, total_frames
        total_frames += frames
        if not postrec:  # time between
            if status:
                print(status)

            #take one block of 8
            chunksize = min(len(ts) - current_frame, frames)
            # for different channels
            outdata[:chunksize,0] = ts[current_frame:current_frame + chunksize]
            outdata[:chunksize,1] = ts[current_frame:current_frame + chunksize]

            if chunksize < frames:
                outdata[chunksize:] = 0
                postrec = True
                prerec = 0
            current_frame += chunksize

        else:
            # play nothing (zeros) (for a period of ts), then enable postrec
            # to play ts again (len(self.ts) is the pause)
            if prerec > len(ts): 
                current_frame = 0
                postrec = False
                prerec = 0
                totalcount += 1
            else:
                prerec += len(indata) #mostly just the blocksize (=8)
            outdata[:] = 0

        #N_ts is the number of times the signal is repeated
        if totalcount == N_ts:
            raise sd.CallbackStop()
        q.put(indata.copy()[:])


                
current_frame = 0
prerec = 0
totalcount = 0
total_frames = 0
cuts = []
postrec = False
event = threading.Event()
q = queue.Queue()
                
with sd.Stream(channels=(2,2), blocksize=blocksize, callback=callback, finished_callback=event.set):
    event.wait()

sound = np.empty((total_frames, 2), dtype=np.float32)
b = 0
while not q.empty():
    sound[b:b+blocksize,:] = q.get()[:,:]
    b+= blocksize

toonpeters avatar Jun 08 '22 15:06 toonpeters

Does the delay also occur when using sd.playrec() (you will have to adapt the generation of your output signal a bit)? If not then its worth looking for the difference there, given that you want to use your own stream implementation regardless.

HaHeho avatar Jun 08 '22 19:06 HaHeho