Sunday, 28 May 2023

PyAudio distorted recording when while loop too busy

I have a Python script that monitors an audio stream in real time and uses a moving average to determine when there is audio and set start and stop points based on when it is above or below a given threshold. Because the script runs 24/7, I avoid excessive memory usage by removing part of the audio stream after about 4 hours of audio.

def record(Jn):
  global current_levels
  global current_lens
  device_name = j_lookup[Jn]['device']
  device_index = get_index_by_name(device_name)
  audio = pyaudio.PyAudio()
  stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, input_device_index=device_index, frames_per_buffer=CHUNK)

  recorded_frames = []
  quantized_history = []
  long_window = int(LONG_MOV_AVG_SECS*RATE/CHUNK) # converting seconds to while loop counter

  avg_counter_to_activate_long_threshold = LONG_THRESH*long_window
  safety_window = 1.5*avg_counter_to_activate_long_threshold
  long_thresh_met = 0

  long_start_selection = 0
  while True:
    data = stream.read(CHUNK, exception_on_overflow=False)
    recorded_frames.append(data)
    frame_data = struct.unpack(str(CHUNK) + 'h', data)
    frame_data = np.array(frame_data)
    sum_abs_frame = np.sum(np.abs(frame_data))
    quantized_history.append(0 if sum_abs_frame < j_lookup[Jn]['NOISE_FLOOR'] else 1)
    current_levels[Jn] = sum_abs_frame

    counter = len(recorded_frames)
    current_lens[Jn] = counter
    if counter >= long_window:
      long_movavg = sum(quantized_history[counter-long_window:counter])/long_window
      if long_movavg >= LONG_THRESH and long_thresh_met != 1:
        long_start_selection = int(max(counter - safety_window, 0))
        long_thresh_met = 1
      if long_movavg < LONG_THRESH and long_thresh_met == 1:
        long_end = int(counter)
        long_thresh_met = 2
        save_to_disk(recorded_frames[long_start_selection:long_end], audio, Jn)

    if counter > MAX_LOOKBACK_PERIOD: # don't keep endless audio history to avoid excessive memory usage
      del recorded_frames[0]
      del quantized_history[0]
      long_start_selection = max(0, long_start_selection - 1) # since you deleted first element, the recording start index is now one less

What I have above works, but what I noticed is that once I hit the four hour mark (the if counter > MAX_LOOKBACK_PERIOD statement at the very end becomes true), any audio saved after that point starts to sound distorted. For example, before the four hour point, the audio looks like:

enter image description here

after the four hour mark, it looks like:

enter image description here

You can see the distortion appearing as these vertical spikes on the spectrogram. I assume the del function is just taking so long that the while loop can't keep up with the audio stream and this is somehow causing the distortion, but I'm not sure. It has to be related to del somehow because the distortion only appears once the if counter > MAX_LOOKBACK_PERIOD becomes true.

Any idea how to address this?



from PyAudio distorted recording when while loop too busy

No comments:

Post a Comment