0
$\begingroup$

I am working on real time Room Impulse Response estimation using sine sweep.

  • $x(n)$ is my sine sweep signal.
  • $f(n)$ is its amplitude modulated inverse.
  • $beep$ consists of 3 sine sweeps with a delay of $1000ms$.

My concern is that when I simultaneously play and record three beeps with a time delay of 1000ms each, it should capture all three beeps in the recording and then it should show three corresponding peaks in the impulse response of that recording as well.

What I'm getting is that it is only capturing the first beep clearly in $myrecording$ variable and only one peak corresponding to that is prominent in the impulse response as well.

  • Why are the other two beeps not captured properly?
  • Why are they not showing up in the impulse response?

I've attached the code and corresponding graph as well.

Impulse Response

import threading
import time
from math import pi as pi
from scipy.signal import max_len_seq
import numpy as np
import matplotlib.pyplot as plt
import simpleaudio as sa
import sounddevice as sd
import scipy.signal as sig
from scipy.signal import chirp
from scipy.io.wavfile import write
from numpy import log as log
from numpy import exp as exp
from numpy import sin as sin
from concurrent.futures.thread import ThreadPoolExecutor

# ESS parameters
f1 = 200
f2 = 20000
T = 0.02
fs = 48000
t = np.arange(0, T * fs) / fs
R = np.log(f2 / f1)

# ESS generation
x = np.sin((2 * np.pi * f1 * T / R) * (np.exp(t * R / T) - 1)).astype(np.float32)

# BEEP VARIABLES
DELAY = 500
number_of_copies = 4
delay_between_the_beeps = np.zeros(48*DELAY)

# Making copies of beep with delay
one_delay_with_one_beep = np.concatenate((delay_between_the_beeps, x), axis=0)
b = np.tile(one_delay_with_one_beep,number_of_copies)
beep = np.concatenate((b,delay_between_the_beeps), axis=0)

#inverse filter of beep
k = np.exp(t * R / T)
f = x[::-1] / k

def play():
    sd.play(beep, 48000, blocking=False)

def record():
    global myrecording
    myrecording = sd.rec(int(7 * 48000), samplerate=48000, channels=1
                         , dtype=np.float32).squeeze()
    print(myrecording.dtype)
    sd.wait()

if __name__ == '__main__':
    tasks =[]
    with ThreadPoolExecutor(max_workers=3) as executor:
        tasks.append(executor.submit(record))
        tasks.append(executor.submit(play))
        concurrent.futures.wait(tasks)
    
    impulse_response= sig.fftconvolve(f, myrecording, mode='full')
    peak_delay = np.argmax(np.abs(impulse_response)) / 48
    print(peak_delay)
$\endgroup$

2 Answers 2

0
$\begingroup$

The problem lies here:

sd.play(beep, 48000, blocking=True)

According to the documentation for sounddevice, "blocking= True" means that code execution stops at that point until playback is finished.

All you are getting is an echo from the playback.

Remove just that bit (", blocking=True") and you should get better results.

I say "should" because when you start recording there may be a bit of a loss at the beginning while the sounddevice library is opening the sound device and opening the file to write to. That may cause you to miss the beginning of the playback.

I think you ought to start recording first then start the playback.

$\endgroup$
4
  • $\begingroup$ I tried enabling and disabling blocking of playing. Since I am playing and recording concurrently using threadpoolexecutor, I don't think so it will make any impact since, I am running both functions concurrently yet in individual threads. The beep I have created caters the initial warmup latency of sound device and original beep is being played after certain delay of time and I can hear the beep properly. $\endgroup$ Sep 2, 2021 at 12:00
  • $\begingroup$ Your code is hard to interpret with pieces commented out here and there. It really isn't clear how you mean for things to be done. $\endgroup$
    – JRE
    Sep 2, 2021 at 12:03
  • $\begingroup$ I've tried to clean the code bit and have updated it in the above question. Is it a bit interpretable now? $\endgroup$ Sep 2, 2021 at 12:31
  • $\begingroup$ I'd try it without the threads. Just use non-blocking for playback and blocking for record. I'm not sure just how well sounddevice (and the libraries behind it) handle threading. Worst case, you may need to switch to sounddevice.playrec and do it all with one call. $\endgroup$
    – JRE
    Sep 2, 2021 at 12:36
0
$\begingroup$

it should show corresponding three peaks in the impulse response

Yes.

Why are the other two beeps not captured properly and why are they not showing up in the impulse response

Sorry, I'm not going to debug your code for you. However, I can give you some debugging steps.

  1. Measure "a wire". Simply run your input signal directly into your impulse response. Verify that you get three impulse and that Fourier Transforms of the individual impulse response are "flat", i.e. equal magnitude and zero phase at all frequencies. Verify that the peak of the impulse response shows up at the time locations you expect.
  2. Create an artificial room impulse response with a direct component and a few reflections. Convolve your input signal with that response and run it through your impulse response calculation. Make sure it measures your artificial response exactly.
  3. If you are using HW then "bypass it" i.e. connect the audio output directly to the audio input. Repeat the verification as outlined in step 1. The frequency response should now represent the transfer function of your hardware system and at this point you can also tale a look at the noise floor and check whether this is sufficient for your application.
  4. Next measure a simple acoustic system: put a good microphone in front of a known good speaker. Repeat verification.
  5. Increase distance between microphone and speaker to includes the full room. Repeat verification.
  6. Next switch to your actual microphone and speaker. Put them in the same location as the "good stuff" was in step 4 and compare results. These should give a sense of how good you hardware is and how the noise performance is

The next level of debugging will depend on where in the step-by-step process you fail first and what exactly the failure is.

Don't go to the next step until the step you are working on is 100% clean and verified.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.