3
$\begingroup$

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 difference in sound production of bats (vocal cords) and insects (stridulation) affects the period continuity. The period trigger takes advantage of this: enter image description here

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

Insects: scratching

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.


UPDATE:

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()
$\endgroup$
3
  • $\begingroup$ Regarding R: R is probably a really great choice for post-processing! It's just really not suited for processing a signal as it streams by. $\endgroup$ Jul 1 at 20:11
  • $\begingroup$ Are you feeling confident in using Python? It's relatively easy to use pysounddevice for capturing audio, applying filtering using scipy.signal and saving audio snippets to SD card/USB stick. $\endgroup$
    – jojek
    Jul 6 at 8:47
  • $\begingroup$ Hi @jojek. I'm definitely not confident with Python yet but I'm using this as a good excuse to learn. It would be really helpful if you had some exanmple code I could play around with. $\endgroup$
    – Thomas
    Jul 6 at 9:10
1
$\begingroup$

First – the fact that the filename ends in .wav indicates this is an uncompressed file. That's a great way to waste storage!

There's lossless compression formats (mostly: FLAC) that should do quite excellently on bat sounds (meaning they don't need much space) and deal relatively gracefully with usual background noise. It's quite possible that this already solves your problem! FLAC allows for sampling rates up to 655350 Hz, so your 384000 Hz digitized audio should just work.

Then: yes, zero-crossing detection is one method to measure frequency, but in the presence of more than one tone or wideband noise, it's not a good choice.

A simple band-pass filter, which lets through only the frequencies you assume bats use (mostly exclusively), followed by a power detector (take the square root and smoothen the result) would work better - and that's exactly what the sox call is doing, it seems (just with a high-pass filter, i.e. it assumes bats aren't in a frequency band, but all above 10 kHz is bats).

Now, implementing this isn't hard, but honestly, R is simply the wrong choice of tooling there. It's a nice language for data analysis after the fact, but for online analysis/filtering, it's pretty much just a hassle (I wouldn't even know how to use it for that at all). Also, I'd argue that running R on segments of sound repeatedly is anything but computationally efficient, but I don't know the R interpreter intimately enough to really back that claim with facts.

Luckily, this really doesn't have to be hard; there's dataflow modelling languages that allow you to implement this without hard coding like the language "Processing", or GNU Radio¹.

I'll just leave a quick GNU Radio companion-implemented flow graph that I had lying around as illustration (I changed a few parameters to make it fit your use case):

GNU Radio Companion Flow Graph

Note that zeroing out any signal that's not happening while there's detected bat activity makes for a signal that very nearly compresses to "nothing", so you get one continuous file (with correct timing!) that only uses significant storage space for the times that something actually happened.


Now, as you said, you're looking for something more advanced than the sox command line you cited (and this really just does essentially the same: high-pass filter, detect power, cut off if below threshold).

I'm not experienced with bat sounds (I'm assuming you're the chiropterist, here), so the better design has to be left to you. But start with this idea:

First, you use a simple high- or band pass-filter to suppress everything that's decidedly unbatty. Then, you could use something like a PLL (phase-locked loop, a device that tracks the speed of the phase of a sine over time, i.e. at what point in its cycle the sine is) to detect whether there's a dominant frequency in the signal; if that PLL's detected frequency doesn't "jump around wildly", you're likely to hear something like a single tone. You can then use that to detect the presence of a single tone and its frequency! You can then analyze the frequency "trajectories" etc. Very likely, you'll even be able to make out the Doppler signatures of the bats' movements.

But: this is all a bit "luck-based", as well, there's birds and insects that make single tones in similar frequency ranges, too. This might indicate this is actually one of the situations where learning a detector with a neural network would work fine (and these are not necessarily that heavy that a raspberry pi couldn't run the classifciation live, but there's little reason, probably, because zeroing out the definitely-non-bat-times together with audio compression will make the storage problem probably disappear).


¹I'm somewhat part of the GNU Radio project. I'm biased.

$\endgroup$
9
  • 1
    $\begingroup$ Thanks @MarcusMüller for your answer. Sorry, I should have made the problem clearer - it's not possible to seperate bats from insects only on frequency. Bats in my part of the world use frequencies ranging from 18kHz up to 115kHz, insects go up to 60kHz. So a band pass filter would not cut out all insect noises. The PLL is interesting but as you mention will also pick out other tones. RE wav vs FLAC: the issue isn't only storage - recording only bat calls makes analysis much easier. My desired output is 5 second files that contain bat calls, not insects or noise. Added this to my question $\endgroup$
    – Thomas
    Jul 1 at 14:39
  • $\begingroup$ Sounds reasonable, @Thomas! Still, R is the wrong tool for live signal processing; how handy are you with Python? $\endgroup$ Jul 1 at 15:46
  • $\begingroup$ By the way, what is shown in the German slides you refer to will not rule out other things, and as said, is not a great detector even for frequency to begin with. The PLL approach will be better than that, as far as I can tell without an example recording. Assuming you'd be able to sort out the 5s file saving later on, would this approach be what you're looking for? $\endgroup$ Jul 1 at 15:48
  • $\begingroup$ Unfortunately I don't know Python, but this could be a good excuse to learn. The PLL approach sounds like it's worth trying out. Do you have a script for this? Also - I added a link to some sample recordings to my question $\endgroup$
    – Thomas
    Jul 1 at 20:47
  • 1
    $\begingroup$ I've spent the weekend learning Python basics @MarcusMüller - do you have any example code for doing what you describe above? Thanks $\endgroup$
    – Thomas
    Jul 5 at 13:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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