Search code examples
pythonaudioqueuepython-multiprocessingpython-sounddevice

Python: Eliminating gaps between segments of recorded audio


I am using Python sounddevice library to record audio, but I can't seem to eliminate ~0.25 to ~0.5 second gaps between what should be consecutive audio files. I think this is because the file writing takes up time, so I learned to use Multiprocessing and Queues to separate out the file writing but it hasn't helped. The most confusing thing is that the logs suggest that the iterations in Main()'s loop are near gapless (only 1-5 milliseconds) but mysteriously the audio_capture function is taking longer than expected even tho nothing else significant is being done. I tried to reduce the script as much as possible for this post. My research has all pointed to this threading/multiprocessing approach, so I am flummoxed.

Background: 3.7 on Raspbian Buster I am dividing the data into segments so that the files are not too big and I imagine programming tasks must deal with this challenge. I also have 4 other subprocesses doing various things after.

Log: The audio_capture part should only take 10:00

08:26:29.991 --- Start of segment #0
08:36:30.627 --- End of segment #0     <<<<< This is >0.6 later than it should be
08:36:30.629 --- Start of segment #1   <<<<< This is near gapless with the prior event

Script:

import logging
import sounddevice
from scipy.io.wavfile import write
import time
import os
from multiprocessing import Queue, Process

# this process is a near endless loop
def main():
    fileQueue = Queue()
    writerProcess = Process(target=writer, args=(fileQueue,))
    writerProcess.start()
    for i in range(9000):
        fileQueue.put(audio_capture(i)) 
    writerProcess.join()

# This func makes an audio data object from a sound source
def audio_capture(i): 
    cycleNumber = str(i)
    logging.debug('Start of segment #' + cycleNumber)
    # each cycle is 10 minutes at 32000Hz sample rate
    audio = sounddevice.rec(frames=600 * 32000, samplerate=32000, channels=2) 
    name = time.strftime("%H-%M-%S") + '.wav' 
    path = os.path.join('/audio', name)
    sounddevice.wait()
    logging.debug('End of segment #' + cycleNumber)
    return [audio, path]
    
# This function writes the files.
def writer(input_queue):
    while True:
        try:
            parameters = input_queue.get()
            audio = parameters[0]
            path = parameters[1]
            write(filename=path, rate=32000, data=audio)
            logging.debug('File is written')
        except:
            pass

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s.%(msecs)03d --- %(message)s', datefmt='%H:%M:%S',handlers=[logging.FileHandler('/audio/log.txt'), logging.StreamHandler()])
    main()  

Solution

  • The documentation tells us that sounddevice.rec() is not meant for gapless recording:

    If you need more control (e.g. block-wise gapless recording, overlapping recordings, …), you should explicitly create an InputStream yourself. If NumPy is not available, you can use a RawInputStream.

    There are multiple examples for gapless recording in the example programs.