7

I am looking to have a python script run in the background and use pyaudio to record sound files when the threshold of the microphone has reached a certain point. This is for a monitor on a two way radio network. So hence we only want to record transmitted audio.

Tasks in mind:

  • Record audio input on a n% gate threshold

  • stop recording after so many seconds of silence

  • keep recording for so many seconds after audio

  • Phase 2: input data into MySQL database to search the recordings

I am looking at a file structure of the similar

/home/Recodings/2013/8/23/12-33.wav would be a recording of the transmision on 23/08/2013 @ 12:33.wav

I have used the code from

Detect and record a sound with python

I am at a bit of a loss where to go from here now and a little guidance would be greatly appreciated

thank you

Community
  • 1
  • 1
ZeroG
  • 137
  • 1
  • 3
  • 10

4 Answers4

17

The current top answer is a bit outdated and only works for python 2. Here is a version updated for python 3. It wraps the functions into classes and packages everything into one simple easy-to-use version. Note that there is one key difference between the top answer and my script:

The script at the top records for one file and then stops, while my script keeps recording whenever noise is detected and dumps the recordings into a directory as it goes.

The main idea for both scripts are pretty similar:

Step 1: 'Listen' until rms becomes greater than the threshold

Step 2: Start recording, set a timer for when to stop recording, == TIMEOUT_LENGTH

Step 3: If the rms breaks threshold again before the timer times out reset the timer

Step 4: Now that the timer is expired, write the recording to a directory and go back to step 1

import pyaudio
import math
import struct
import wave
import time
import os

Threshold = 10

SHORT_NORMALIZE = (1.0/32768.0)
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
swidth = 2

TIMEOUT_LENGTH = 5

f_name_directory = r'C:\Users\Jason\PyCharmProjects\AutoRecorder\records'

class Recorder:

    @staticmethod
    def rms(frame):
        count = len(frame) / swidth
        format = "%dh" % (count)
        shorts = struct.unpack(format, frame)

        sum_squares = 0.0
        for sample in shorts:
            n = sample * SHORT_NORMALIZE
            sum_squares += n * n
        rms = math.pow(sum_squares / count, 0.5)

        return rms * 1000

    def __init__(self):
        self.p = pyaudio.PyAudio()
        self.stream = self.p.open(format=FORMAT,
                                  channels=CHANNELS,
                                  rate=RATE,
                                  input=True,
                                  output=True,
                                  frames_per_buffer=chunk)

    def record(self):
        print('Noise detected, recording beginning')
        rec = []
        current = time.time()
        end = time.time() + TIMEOUT_LENGTH

        while current <= end:

            data = self.stream.read(chunk)
            if self.rms(data) >= Threshold: end = time.time() + TIMEOUT_LENGTH

            current = time.time()
            rec.append(data)
        self.write(b''.join(rec))

    def write(self, recording):
        n_files = len(os.listdir(f_name_directory))

        filename = os.path.join(f_name_directory, '{}.wav'.format(n_files))

        wf = wave.open(filename, 'wb')
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(self.p.get_sample_size(FORMAT))
        wf.setframerate(RATE)
        wf.writeframes(recording)
        wf.close()
        print('Written to file: {}'.format(filename))
        print('Returning to listening')



    def listen(self):
        print('Listening beginning')
        while True:
            input = self.stream.read(chunk)
            rms_val = self.rms(input)
            if rms_val > Threshold:
                self.record()

a = Recorder()

a.listen()
Primusa
  • 12,476
  • 3
  • 31
  • 50
  • @primusa How would I be able to just save the sound that the script picks up without the pause from the timeout? – Juliette May 11 '21 at 15:35
11

Some time ago I wrote some of the steps

  • Record audio input on a n% gate threshold

A: Start a Boolean variable type for "Silence" and you can calculate RMS to decide if Silence is true or False, Set one RMS Threshold

  • stop recording after so many seconds of silence

A: Do you need calculate one timeout, for it get the Frame Rate, Chunk Size and how many seconds do you want, to calculate your timeout make (FrameRate / chunk * Max_Seconds)

  • keep recording for so many seconds after audio

A: If Silence is false == (RMS > Threshold) get the last chunk of data of audio (LastBlock) and just keep record :-)

  • Phase 2: input data into MySQL database to search the recordings

A: This step is up to you

Source code:

import pyaudio
import math
import struct
import wave

#Assuming Energy threshold upper than 30 dB
Threshold = 30

SHORT_NORMALIZE = (1.0/32768.0)
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
swidth = 2
Max_Seconds = 10
TimeoutSignal=((RATE / chunk * Max_Seconds) + 2)
silence = True
FileNameTmp = '/home/Recodings/2013/8/23/12-33.wav'
Time=0
all =[]

def GetStream(chunk):
    return stream.read(chunk)
def rms(frame):
    count = len(frame)/swidth
    format = "%dh"%(count)
    # short is 16 bit int
    shorts = struct.unpack( format, frame )

    sum_squares = 0.0
    for sample in shorts:
        n = sample * SHORT_NORMALIZE
        sum_squares += n*n
    # compute the rms 
    rms = math.pow(sum_squares/count,0.5);
    return rms * 1000

def WriteSpeech(WriteData):
    stream.stop_stream()
    stream.close()
    p.terminate()
    wf = wave.open(FileNameTmp, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(p.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(WriteData)
    wf.close()

def KeepRecord(TimeoutSignal, LastBlock):
    all.append(LastBlock)
    for i in range(0, TimeoutSignal):
        try:
            data = GetStream(chunk)
        except:
            continue
        #I chage here (new Ident)
        all.append(data)

    print "end record after timeout";
    data = ''.join(all)
    print "write to File";
    WriteSpeech(data)
    silence = True
    Time=0
    listen(silence,Time)     

def listen(silence,Time):
    print "waiting for Speech"
    while silence:
        try:
            input = GetStream(chunk)
        except:
            continue
        rms_value = rms(input)
        if (rms_value > Threshold):
            silence=False
            LastBlock=input
            print "hello ederwander I'm Recording...."
            KeepRecord(TimeoutSignal, LastBlock)
        Time = Time + 1
        if (Time > TimeoutSignal):
            print "Time Out No Speech Detected"
            sys.exit()

p = pyaudio.PyAudio()

stream = p.open(format = FORMAT,
    channels = CHANNELS,
    rate = RATE,
    input = True,
    output = True,
    frames_per_buffer = chunk)

listen(silence,Time)
Alex H
  • 3,013
  • 3
  • 15
  • 27
ederwander
  • 3,280
  • 17
  • 22
  • Ok I am getting somewhere, but I need the program to run indefenantly and create the files it's self at the moment I am getting a file not found on ederwander's code. the program is detecting the sound correctly..... do we have to have a time out for the program or can we just time out that recording and then start listening again. Sorry getting a bit tied up in a knot here – ZeroG Aug 31 '13 at 12:21
  • Ok I'm getting no errors now and have a file output working correctly. the problem is I only get a 1 sec recording of the trigger audio, I need it to record until silence again and then write to file and then start listening again. – ZeroG Sep 03 '13 at 13:22
  • Make one function "listen" and call it in KeepRecord function, it is so easy to do ! – ederwander Sep 03 '13 at 15:45
  • sorry I am just new to all this and trying hard, the function listen is where in the code yes so I understand how to make a function but I am really struggling on where to put this as I think technically the whole code is the listen function??? – ZeroG Sep 03 '13 at 16:18
  • New UPDATE. PS: I have not tested – ederwander Sep 03 '13 at 16:24
0

So you just need the getLevel(data) function? A quick hack would be:

def getLevel(data):
   sqrsum = 0
   for b in data:
      b = ord(b)
      sqrsum+=b*b
   return sqrsum

That should increase with volume. Set your threshold appropriately through trial and error.

ejk314
  • 381
  • 1
  • 11
  • Thank You ejk314, where do you reckon I put that in the code. also is getLevel a pyaudio function? – ZeroG Aug 24 '13 at 08:04
0

For those who have problems installing pyaudio because of the missing portaudio.h, you can do that:

sudo apt-get install portaudio19-dev python-pyaudio python3-pyaudio

the answer is from: portaudio.h: No such file or directory

Rodrigo
  • 105
  • 1
  • 6