I'm making a Raspberry Pi bat detector using a USB-powered ultrasonic microphone. I want to be able to record bats while excluding insects and other non-bat noises. Recording needs to be sound-triggered to avoid filling the SD card too quickly and to aid with analysis. This website explains how to do this with SoX:
rec - c1 -r 192000 record.wav sinc 10k silence 1 0.1 1% trim 0 5
This records for 5 seconds after a trigger sound of at least 0.1 seconds and includes a 10kHz high pass filter. This is a good start, but what I'd really like is an advanced filter that excludes crickets and other non-bat noises. Insect and bat calls overlap in frequency so a high pass or band filter won't do.
The Elekon Batlogger does this with a period trigger that analyses zero crossings. From the Batlogger website:
The trigger fires when ProdVal and DivVal are lower than the set limits, so if the values are within the yellow range. (Values mean default values): ProdVal = 8, higher values trigger easier DivVal = 20, higher values trigger easier
Translated text from the image:
Bat: Tonal signal
Period constant => zero crossings / time = stable
Period constant => zero crossings / time = differs
MN => mean value of the number of periods per measurement interval
SD => standard deviation of the number of periods
Higher values trigger better even at low frequencies (also insects!) And vice versa
Is there a way to implement this (or something to the same effect) in Raspberry Pi OS? The language I'm most familiar with is R. Based on answers to this question it seems like R would be suitable for this problem, although if R isn't the best choice then I'm open to other suggestions.
I'd really appreciate some working code for recording audio and filtering as described above. My desired output is 5 second files that contain bat calls, not insects or noise. Needs to be efficient in terms of CPU / power use and needs to work on-the-fly.
Example recordings of bats and insects here.
I've got a basic sound-activated script working in Python (based on this answer) but I'm not sure how to include an advanced filter in this:
import pyaudio import wave from array import array import time FORMAT=pyaudio.paInt16 CHANNELS=1 RATE=44100 CHUNK=1024 RECORD_SECONDS=5 audio=pyaudio.PyAudio() stream=audio.open(format=FORMAT,channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) nighttime=True # I will expand this later while nighttime: data=stream.read(CHUNK) data_chunk=array('h',data) vol=max(data_chunk) if(vol>=3000): print("recording triggered") frames= for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): data = stream.read(CHUNK) frames.append(data) print("recording saved") # write to file words = ["RECORDING-", time.strftime("%Y%m%d-%H%M%S"), ".wav"] FILE_NAME= "".join(words) wavfile=wave.open(FILE_NAME,'wb') wavfile.setnchannels(CHANNELS) wavfile.setsampwidth(audio.get_sample_size(FORMAT)) wavfile.setframerate(RATE) wavfile.writeframes(b''.join(frames)) wavfile.close() # check if still nighttime nighttime=True # I will expand this later stream.stop_stream() stream.close() audio.terminate()