In Part 3 of my epic series “Building a Raspberry Pi-powered Barkometer”, I covered how to capture audio using the arecord command and how I’d discovered that I got better sound out of an old USB webcam than the fancy-schmancy (but very cheap) USB sound card I started out with. I wound up in Part 3 collecting 60-second sound files in WAV format in a subdirectory on the Raspberry Pi, each named for the sample’s start time (e.g. 2016-09-07-04-42-27.wav
).
We need to get these files off of the RPi so we can analyze them. In theory, this could be done on the board but for now we’ll FTP them to another machine for analysis. So, to get the recordings to somewhere we can slice and dice them, we’re going to use ncftpput
, an FTP utility included the ncftp
package. To install ncftp
we'll run the following command on the RPi command line (as always, when installing a new package, you should run sudo apt-get update
first):
sudo apt-get install ncftp
Our recordings are in $HOME/bark
so we can now FTP them to another machine using the following command line:
ncftpput -u admin \ -p password \ -DD \ -V \ ftpserver \ /bark \ $HOME/bark/*.*
Let’s break this down (see the manual page for the ncftpput
command line options):
-u admin
and-p password
are the login credentials for the target FTP server-DD
causes the local file to be deleted after successfully uploading it.-V
suppresses the display of the upload progress meterftpserver
is either the IP address or name of the target FTP server/bark
is the remote directory to store the files in$HOME/bark/*.*
is the specification of the files to be FTPed
Now what we need to do is automate this so that every time a recording is completed, it gets shipped out to the FTP server. This is where we can use a really cool command, inotifywait
, to monitor for changes to files and directories. inotifywait
uses inotify
, “a Linux kernel subsystem that acts to extend filesystems to notice changes to the filesystem, and report those changes to applications” (Wikipedia). This tool is in the package inotify-tools
which is installed thusly:
sudo apt-get install inotify-tools
With inotifywait
installed, we can set up a BASH script like this:
arecord -D plughw:1,0 -q --buffer-time=5000000 -f dat -c 1 -t wav --max-file-time 60 --use-strftime $HOME/bark/%Y-%m-%d-%H-%M-%S.wav &
while true
do
NEWFILE=`inotifywait -q --format %f -e close_write $HOME/bark`
ncftpput -u admin -p password -DD ftpserver /bark $HOME/bark/$NEWFILE
done
I discussed the line starting with arecord
in the previous post and the &
at the end of the command tells BASH to run it as a background task. The while … done
creates an infinite loop and the line starting with NEWFILE
breaks down as follows:
NEWFILE=
sets a temporary environment variable`
this backtick causes everything up to the next backtick to be evaluated as a whole before being passed to the command, in this case, setting the value ofNEWFILE
. Without this,NEWFILE
would take the value “inotifywait” rather than the output ofinotifywait
inotifywait
blocks until a specific filesystem events occurs-q
prevents the program from printing basic status messages which we don't need--buffer-time=
5000000
allocates a buffer of 5 seconds (see below)--format %f
specifies that when the filesystem event occurs, the output frominotifywait
will be the name of the file that has been affected-e close_write
this specifies the event to wait for, a file close$HOME/bark
is the subdirectory to be monitored
So, when a file created by arecord
, is closed in the $HOME/bark
subdirectory, inotifywait
stop blocking and print the name of the closed file which will then be stored in the environment variable NEWFILE
and the script will resume. The command ncftpput
will then execute and transfer the file name discovered by inotifywait
to the FTP server and, voila! Job done!
Running the above BASH script on the Raspberry Pi generates output like this:
pi@RPi-01:~ $ sh barkometer.sh
/home/pi/bark/2016-10-01-13-18-57.wav:5.49 MB 5.28 MB/s
/home/pi/bark/2016-10-01-13-19-57.wav:5.49 MB 5.19 MB/s
Now, you may have noticed that in the parameters for arecord
in the script above, I added something that wasn’t in command line I discussed in part 3. What I added was --buffer-time
=
5000000
because with my original arecord
command line you will occasionally see overruns reported like this:
pi@RPi-01:~ $ sh barkometer.sh
/home/pi/bark/2016-10-01-13-18-57.wav:5.49 MB 5.28 MB/s
/home/pi/bark/2016-10-01-13-19-57.wav:5.49 MB 5.19 MB/s
/home/pi/bark/2016-10-01-13-19-57.wav:5.49 MB 5.19 MB/s
overrun!!! (at least 191.372 ms long)
/home/pi/bark/2016-10-01-13-20-57.wav:5.49 MB 5.77 MB/s
/home/pi/bark/2016-10-01-13-21-57.wav:5.49 MB 5.72 MB/s
/home/pi/bark/2016-10-01-13-22-57.wav:5.49 MB 5.79 MB/s
overrun!!! (at least 38.328 ms long)
These errors are apparently caused by resources not being available but exactly what causes them is annoyingly mysterious. I thought that it might be due to I/O limitations of the SD card but when I tried using the /tmp
subdirectory (which is RAM-based) to store that audio file I still got the same result; if you have any insight into this issue, please let me know. The solution I found was to add a 5 second buffer (the value is specified in the command line in microseconds) which fixes the problem at the cost of using a little more RAM.
Our final task in this installment is to set our script running when the RPi starts. There are multiple ways to achieve this but we’ll use one of the simplest, editing the user autostart file:
nano ~/.config/lxsession/LXDE-pi/autostart
The default desktop for Raspbian is LXDE and the autostart file's contents will look like this unless you've made changes:
@lxpanel --profile LXDE-pi
@pcmanfm --desktop --profile LXDE-pi
@xscreensaver -no-splash
@point-rpi
We need to add a line at the end of the file:
@bash /home/pi/barkometer.sh
This will invoke a new instance of BASH and have it execute our script. Because this is in the context of a user it means that the user has to be logged in so automatic login is a good idea. An important issue is that the user login can’t occur until Raspbian has completed starting all subsystems so we can be certain that, unless there’s a problem, the services we require such as a wireless connection will be established.
Now there’s lots more we can do to make the script robust such aschecking if the FTP server is accessible and taking action of some kind if it's not, logging the output of the commands we run and transferring the log to our TFP server, tidying up any WAV or log files left over from a restart, and so on but, for now, we’ve achieved our primary goal; recording the ambient audio environment and saving it to a remote server for analysis.
In the next part of this series, we’ll start figuring out how to analyze our recordings.
Comments? Thoughts? Drop me a line or comment below then follow me on Twitter and Facebook.