1

I want to measure the time delay of a signal. To do that the signal is put on a speaker an the delay when it gets captured by a microphone is estimated. The delay is expected to be in the range of milliseconds, so it is crucial to start the speaker signal and the measurement at the exact same time.

My question is if that can be achieved by using threads:

def play_sound():
    # play sound

def record():
    # start recording


if __name__ == '__main__':
    t1 = threading.Thread(target=play_sound())
    t2 = threading.Thread(target=record())

    t1.start()
    t2.start()

or is there a better way to d it?

Stanissse
  • 41
  • 3

2 Answers2

2

I would start the recording thread first and look for the first peak in the signal captured by the mic. This will tell you how many ms after recording started the first sound was detected. For this you probably need to know the sampling rate of the mic etc- here is a good starting point.

The timeline is something like this

---- recording start ------- playback start -------- sound first detected ----

You want to find out how many ms after you start recording a sound was picked up ((first_peak - recording_start) in the code below), and then subtract the time it took to start the playback ((playback_start - recording_start) below)

Here's a rough code outline

from datetime import datetime

recording_start, playback_start, first_peak = None, None, None

def play_sound():
    nonlocal playback_start
    playback_start = datetime.now()

def record():
    nonlocal recording_start, first_peak
    recording_start = datetime.now()
    first_peak = find_peak_location_in_ms()  # implement this


Thread(target=record()).start()  # note recording starts first
Thread(target=play_sound()).start()

# once the threads are finished
delay = (first_peak - recording_start) - (playback_start - recording_start)

PS one of the other answers correctly points out that you need to worry about the global interpreter lock. You can likely bypass it by using c-level APIs to record/play the sound without blocking other threads, but you may find Python's not the right tool for that job

mbatchkarov
  • 15,487
  • 9
  • 60
  • 79
  • Thanks for the answer! Yes I've read about the global interpreter lock, but if I understand your solution correctly that would not be a problem? – Stanissse Oct 07 '20 at 09:19
  • 1
    This is by far the best answer. I don't think you **strictly** need the recorder and the sound to be started at the same time. You can have an active recorder in the background and compare the timestamp of when the sound is first detected by the recorder and the ts of when it was emitted by the speaker. – anddt Oct 07 '20 at 09:53
1

It won't be 100% concurrent real-time, but no solution for desktop will ever be. The question then becomes if it is accurate enough for your application. To know this you should simply run a few tests with known delays and see if it works.

You should know about the global interpreter lock: https://docs.python.org/3.3/glossary.html#term-global-interpreter-lock. This means that even on a multicore pc you code won't run truly concurrent.

If this solution is not accurate enough, you should look into the multiprocessing package. https://docs.python.org/3.3/library/multiprocessing.html

Edit: Well, in order to truly get them to start simultaneously you can't start them sequentially after each other like that. You need to use multiprocessing, create the two threads, and then create some kind of interrupt that will start the two threads at the same time. And I think even then you can't be truly sure they will start at the same time because the OS can switch in other stuff (multitasking), and even if that goes fine in the processors itself things might be reordered differently, different code might be cached, etc. On a desktop you can never have the guarantuee that two programs start simultaneously. So the question then becomes if they are consistently simultaneous enough for your purpose. To answer that you will need to find someone with experience in this, or just run a few tests.

Tigris
  • 133
  • 8
  • Thanks for your answer. Do I get it right, that by using the multiprocessing package I can assure that the processes start simultaneously? – Stanissse Oct 07 '20 at 08:46