Monitoring an audio line

Stefan picture Stefan · Apr 10, 2010 · Viewed 7.3k times · Source

I need to monitor my audio line-in in linux, and in the event that audio is played, the sound must be recorded and saved to a file. Similiar to how motion monitors the video feed.

Is it possible to do this with bash? something along the lines of:

#!/bin/bash

# audio device
device=/dev/audio-line-in

# below this threshold audio will not be recorded.
noise_threshold=10

# folder where recordings are stored
storage_folder=~/recordings

# run indefenitly, until Ctrl-C is pressed
while true; do
   # noise_level() represents a function to determine
   # the noise level from device
   if noise_level( $device ) > $noise_threshold; then
     # stream from device to file, can be encoded to mp3 later.
     cat $device > $storage_folder/$(date +%FT%T).raw         
   fi;
done;

EDIT: The flow I'd like to get from this program is

 a. when noise > threshold, start recording  
 b. stop recording when noise < threshold for 10 seconds
 c. save recorded piece to separate file

Answer

J&#252;rgen H&#246;tzel picture Jürgen Hötzel · Apr 10, 2010

SoX is the Swiss Army knife of sound processing. You can utilize it to analyze recordings. The only shortcoming of the folowing solutions is:

  1. You need to split up the recordings to fixed size chunks
  2. You can lose recording time (due to killing/analyzing/restarting of recordings)

So further improvements could be doing analyzing asynchronous, although this will complicate the job.

#!/bin/bash 

record_interval=5
noise_threshold=3
storage_folder=~/recordings

exec 2>/dev/null        # no default  error output
while true; do 
    rec out.wav &
    sleep $record_interval
    kill -KILL %1
    max_level="$(sox  out.wav -n stats -s 16 2>&1|awk '/^Max\ level/ {print int($3)}')"
    if [ $max_level -gt $noise_threshold ];then 
    mv out.wav ${storage_folder}/recording-$(date +%FT%T).wav;
    else 
    rm out.wav
    fi
done

Update:

The following solution uses a fifo as output from rec. By using split on this pipe to get the chunks, there should be no loss of recording time:

#!/bin/bash 

noise_threshold=3
storage_folder=~/recordings
raw_folder=~/recordings/tmp
split_folder=~/recordings/split
sox_raw_options="-t raw -r 48k -e signed -b 16"
split_size=1048576 # 1M

mkdir -p ${raw_folder} ${split_folder}

test -a ${raw_folder}/in.raw ||  mkfifo ${raw_folder}/in.raw

# start recording and spliting in background
rec ${sox_raw_options} - >${raw_folder}/in.raw 2>/dev/null &
split -b ${split_size} - <${raw_folder}/in.raw ${split_folder}/piece &


while true; do 
    # check each finished raw file
    for raw in $(find ${split_folder} -size ${split_size}c);do 
    max_level="$(sox $sox_raw_options  ${raw} -n stats -s 16 2>&1|awk '/^Max\ level/ {print int($3)}')"
    if [ $max_level -gt $noise_threshold ];then 
        sox ${sox_raw_options} ${raw} ${storage_folder}/recording-$(date +%FT%T).wav;
    fi
    rm ${raw}
    done
    sleep 1
done1