Tuesday, 18 August 2026

BattyBirdNET-Pi: audio detection method to replace freq-shift

The Freq Shift feature on BirdNET-Pi works well

But its useless on the Batty version

So I've been developing alternatives that can give a live audible indication when bats are calling within range.

The Frequency Shift feature lowers bird call audio frequency for old users like me, having a relatively poor high frequency hearing range.

However, bat calls cover a very wide ultrasonic range and are very short duration.

 

 

In this soprano spectrogram, the 2 social calls contain frequencies from about 20kHz to around 60kHz, while the echolocation calls range well above the high 50s.

What I wanted was an audible alarm that worked, so I'd know when to dash outside, and look up!

testing system with WAV files

Initially I tried a few ideas offline, by just running commands with bat recordings on my Linux laptop. But in the longer term, I created a test version of the script: livestream.sh

 

#!/usr/bin/env bash
#=======================================================================
# Live Audio Stream Service Script; TEST SCRIPT FOR USE WITH .wav FILES!
#=======================================================================
source /etc/birdnet/birdnet.conf

# Read the logging level from the configuration option
LOGGING_LEVEL="${LogLevel_LiveAudioStreamService}"
# If empty for some reason default to log level of error
[ -z $LOGGING_LEVEL ] && LOGGING_LEVEL='error'
# Additionally if we're at debug or info level then allow printing of script commands and variables
if [ "$LOGGING_LEVEL" == "info" ] || [ "$LOGGING_LEVEL" == "debug" ];then
  # Enable printing of commands/variables etc to terminal for debugging
  set -x
fi

# ultrasonic envelope extraction: provide means to 'hear' bat calls, by converting 20-60kHz
if [ "$ACTIVATE_FREQSHIFT_IN_LIVESTREAM" == "true" ]; then
   
#Original method
#    FREQSHIFT_OPT='-af rubberband=pitch='${FREQSHIFT_LO}'/'${FREQSHIFT_HI}
   
# Squared Envelope method
    FREQSHIFT_OPT=
'-af highpass=f=20000,lowpass=f=60000,aeval=val(0)*val(0),lowpass=f=1000,aeval=sqrt(val(0)),volume=10'
   
# Squared & noise-gate
#    FREQSHIFT_OPT='-af highpass=f=20000,lowpass=f=60000,aeval=val(0)*val(0),lowpass=f=1000,aeval=sqrt(val(0)),agate=threshold=0.02:ratio=10:attack=1:release=50,volume=10'
   
# Rectified Envelope method
#    FREQSHIFT_OPT='-af highpass=f=20000,lowpass=f=60000,aeval=abs(val(0)),lowpass=f=8000,volume=10'
   
# RMS-envelope method - 0.5 ms (128 samples at 256 kHz)
#    FREQSHIFT_OPT='-af highpass=f=20000,lowpass=f=60000,aeval=val(0)*val(0),lowpass=f=318,aeval=sqrt(val(0)),volume=10'
fi

if [ -z ${REC_CARD} ];then
  echo "Stream not supported"
elif [[ ! -z ${RTSP_STREAM} ]];then
  # Explode the RTSP stream setting into an array so we can count the number we have
  RSTP_STREAMS_EXPLODED_ARRAY=(${RTSP_STREAM//,/ })

  if [[ -z ${RTSP_STREAM_TO_LIVESTREAM} ]];then
    RTSP_STREAM_TO_LIVESTREAM=0
  fi

 SELECTED_RSTP_STREAM=${RSTP_STREAMS_EXPLODED_ARRAY[RTSP_STREAM_TO_LIVESTREAM]}

  if [[ -z ${SELECTED_RSTP_STREAM} ]];then
    SELECTED_RSTP_STREAM=${RSTP_STREAMS_EXPLODED_ARRAY[0]}
  fi

  ffmpeg -nostdin -loglevel $LOGGING_LEVEL -ac ${CHANNELS} -i ${SELECTED_RSTP_STREAM} -acodec libmp3lame \
    -b:a 320k -ac ${CHANNELS} -content_type 'audio/mpeg' \
    ${FREQSHIFT_OPT} \
    -f mp3 icecast://source:${ICE_PWD}@localhost:8000/stream -re

else
  # TEMPORARY BAT TEST — replace microphone with WAV file
  ffmpeg -nostdin -loglevel $LOGGING_LEVEL \
    -re -stream_loop -1 \
    -i
/home/<user>/BirdNET-Pi/tests/bat-test.wav \
    -acodec libmp3lame \
    -b:a 320k -ac ${CHANNELS} -content_type 'audio/mpeg' \
    ${FREQSHIFT_OPT} \
    -f mp3 icecast://source:${ICE_PWD}@localhost:8000/stream -re
fi

 

This allowed me to [more realistically] test the system with a few pre-recorded bat wav files.

I used AI to help with possible ideas & code using ffmpeg, most of which are commented out in the code above. Obviously the original 'rubberband' method doesn't work, but the rest do. My opinion is that the Squared Envelope method gives best results, but there is not much in it. And some work slightly better with weak low frequency calls (e.g. Noctule), others better with weak high frequency calls (e.g. Soprano Pipistrelle).

config & test

On my Linux laptop, I used a combination of web browser, file manager & SSH via terminal to conduct these tests. Be sure to rename the file path in the test code above to suit your system: /home/<user>/BirdNET-Pi/tests/bat-test.wav

  1. Temporarily replace .../scripts/livestream.sh with the test version.
  2. Select a few bat recordings, some strong, some weak, for range of species.
  3. For each individual test, a bat recording needs to be copied to: .../BirdNET-Pi/tests/ and renamed: bat-test.wav
  4. Via terminal SSH session: sudo systemctl stop livestream.service
  5. Via web browser, exit Spectrogram page (go to Overview)
  6. Via terminal SSH session: sudo systemctl start livestream.service
  7. Via terminal SSH session: systemctl status livestream.service --no-pager
  8. Via web browser, enter Spectrogram page & listen to detection

It may also be helpful to record this audio, as its then easier to quickly switch between test recordings.

If you want to visualise each test, try:-

ffmpeg -y -i bat-test.wav -lavfi "showspectrumpic=s=1520x728:legend=1" bat-test.png

When you're done, just edit the original version of livestream.sh with the single line that defines your new ffmpeg option, then reboot.

I'd suggest the test version of livestream.sh be renamed (test-livestream.sh) and saved in the BattyBirdNet 'tests' folder.




No comments:

Post a Comment