Record Streaming Audio with Linux: Part II

March 3rd, 2006

I said I was looking into getting FIFOs working so that the filesize requirements would be reduced. It’s also more efficient this way. I was heavily influenced by the two scripts created by Daniel Howard.

So here’s how it works. You get to create two scripts. The first one is for recording. It’s an all-purpose script that I called record.sh. Here is the code:

#!/bin/bash

#

# record.sh

#

# Use mplayer to capture the stream

# at $STREAM to the file $FILE

#

# example: record.sh my_radio_show 60 mms://someserver.com/stream</p>
<p>DIR=/home/shawn/PodCasts #directory where to save the file

TEMPDIR=/tmp</p>
<p># Don’t edit anything below this line

#######################################################

DATE=`date +%d-%b-%Y`  # Save the date as DD-Mmm-YYYY

YEAR=`date +%Y` # Save just the year as YYYY

NAME=$1

DURATION=$2 # enough to catch the show, plus a bit

STREAM=$3

TEMPFILE=$TEMPDIR/$NAME-$DATE

FILE=$DIR/$NAME-$DATE # Where to save it</p>
<p># Capture Stream

mkfifo $TEMPFILE.wav

mkfifo $TEMPFILE-silenced.wav</p>
<p># The lame settings below are optimized for voice encoding

# The sox command below strips out any silent portions

lame -S -a -m m –ty “$YEAR” –vbr-new -V 9 –lowpass 13.4 –athaa-sensitivity 1 \

    –resample 32 $TEMPFILE-silenced.wav $FILE.mp3 >/dev/null &

sox $TEMPFILE.wav -c 1 $TEMPFILE-silenced.wav \

    silence 1 0.2 0.5% -1 0.2 0.5% >/dev/null&

/usr/bin/mplayer -quiet -cache 500 \

    -ao pcm:file=”$TEMPFILE.wav” -vc dummy -vo null \

    -noframedrop $STREAM >/dev/null&</p>
<p>sleep 5

# get the pid of all processes started in this script.

PIDS=`ps auxww | grep $TEMPFILE | awk ‘{print $2}’`</p>
<p># the & turns the capture into a background job

sleep `echo ${DURATION}*60 | bc`  # wait for the show to be over

kill $PIDS # kill the stream capture

rm $TEMPFILE.wav

rm $TEMPFILE-silenced.wav

This script takes three args:

  1. the name of the show you’re recording. This will be used in the final filename with the current date appended.
  2. the length of the show in minutes
  3. the URI of the stream (often mms:// or http://)

This integrates very well with another script that will hold all the data for the shows we want to record and automatically set the start times for us. Here is my script that I called today.sh

#!/bin/sh

#

# today.sh — schedule what programs you want to rip today using the

# recorder script.</p>
<p># Non-obvious paths

RECORDER=$HOME/bin/record.sh

RECORDER_HI=$HOME/bin/record-hi.sh</p>
<p># Set up stations

KFI=”http://a814.l1977144512.c19771.g.lm.akamaistream.net/D/814/19771/v0001/reflector:44512?MSWMExt=.asf”

WXNT=mms://wmc1.liquidviewer.net/WXNT

WPGB=”http://84.53.144.36:80/D/1046/20063/v0001/reflector:43803?MSWMExt=.asf”

KOZZ=mms://lotusradio-kozz.wm.llnwd.net/lotusradio_kozz</p>
<p># What day is it?

TODAY=`date +%a`</p>
<p># EVERY DAY

#everyday() {}</p>
<p># WEEKDAYS

weekday() {

	# “Record John_Ziegler for three hours, starting at 7:00pm.”

	echo $RECORDER John_Ziegler       180  $KFI | at  7:00pm

	echo $RECORDER Glenn_Beck         180 $WPGB | at  6:00am

}</p>
<p># MONDAY

if [ $TODAY = “Mon” ]; then

	# everyday

	weekday

fi</p>
<p># TUESDAY

if [ $TODAY = “Tue” ]; then

	# everyday

	weekday

fi</p>
<p># WEDNESDAY

if [ $TODAY = “Wed” ]; then

	# everyday

	weekday

fi</p>
<p># THURSDAY

if [ $TODAY = “Thu” ]; then

	# everyday

	weekday

fi</p>
<p># FRIDAY

if [ $TODAY = “Fri” ]; then

	# everyday

	weekday

fi</p>
<p># SATURDAY

if [ $TODAY = “Sat” ]; then

	# everyday

	echo $RECORDER Handel_On_The_Law   300 $KFI | at  6:00am

	echo $RECORDER Dr_Dean_Edell        60 $KFI | at  2:00pm

fi</p>
<p># SUNDAY

if [ $TODAY = “Sun” ]; then

	# everyday

	echo $RECORDER Jesus_Christ_Show   180  $KFI | at  6:00am

	echo $RECORDER Glenn_Beck          180 $WPGB | at 10:00am

	echo $RECORDER_HI Dr_Demento       120 $KOZZ | at 10:00pm

fi

Using this as a template you might notice another script being referred to. record.sh has audio processing optimized for talk radio. For higher quality and stereo, you need to change a few things. You can download all of the scripts mentioned here at the bottom of this post.

To finish off this solution you need to run today.sh at some point after 12:00am and before the start of the earliest program you wish to record. I set mine to run daily at 1:00am.

# Schedule recordings for today from radio streams

0 1 * * * /home/shawn/bin/today.sh >& /dev/null

Downloads:

11 Responses to “Record Streaming Audio with Linux: Part II”

  1. Danny Howard says:

    SWEET! My prior work has been recycled and improved upon! :)

    :) :) :)

    Thanks,
    -danny

  2. dannyman.toldme.com / HOWTO: Archive Audio Streams in to mp3 Files says:

    […] check out the comments that follow for tips and tricks, especially Shawn Dowler who has gone and wrote a page about his revised versions of thescripts. […]

  3. Shawn Dowler says:

    I was working on my own solution, but I knew it was lacking some things, so when I found your implementation I was very impressed. I especially liked the way you killed all the processes. That was what I was looking for when I found your scripts. Your scripts were great, so I merged what I had with what you had done and I’m very pleased with the results.

    I especially like your today script. It is so much nicer than what I was doing before. Now I have a central human-readable place to keep all the program scheduling data outside of my crontab.

  4. jmfv says:

    Beautiful! Easy to understand the code, easy to implement, very useful results. Just what every Linux user dreams of. Thank you and congrats!

  5. Michael Brehm says:

    Thanks for this solution the scheduling of online programs was one of the items causing me to dual boot. Now I have one less reason to keep my XP partition. I just wish that it did not take me 5 days to find this solution after I started looking for it on the net! Again THANK YOU and also to all the authors of the pages that were referenced.

    The only problem I am having getting this to work in Ubuntu is with RECORD_HI, but RECORD works great. I will figure that our soon enough, but this was a great start.

  6. Shawn Dowler says:

    @Michael Brehm:
    I’m glad that I could help you find a way to solve your problems using Free Software. I love to hear stories like yours, and the thanks are always appreciated!

  7. mxwlpxwl says:

    Your script is great, I’m trying to implement now, but I have a question as to why you are running lame, then sox, and finally mplayer. I’m sure this is correct, I am just wondering why?

    Again, your script is tight, good work :-)

    mxwlpxwl

  8. Shawn Dowler says:

    @mxwlpxwl:
    The sox command was a late addition as I noticed some of the stations were including long periods of silence in place of certain commercials. It doesn’t happen as much anymore, but it still happens every now and again. The sox command just strips out silence, nothing more. You could leave it out, but it doesn’t change much if you leave it in, so I just put it in for all of them.

    I’m glad you like the scripts. Remember, I didn’t come up with the concept, I just improved upon the original design.

  9. Xwang says:

    Hi,
    how can I add a simple frontend which permits to insert:
    1) the location where to save the stream
    2) the url
    2) the recording time
    Moreover I would like to be able to start and stop the recording using a record and a stop button.
    I’ve done in the past a simple gui using perl, but I don’t know how to modify the script in order to stop the registration using the gui

  10. Xwang says:

    Hi,
    I’ve done the following script which acts as a “wizard” with some pop up that enables the user to pass the input to the recorder.
    Do you like it?
    Do you think is it possible to modify it in order to pause the recording?
    Thank you,
    Xwang
    [bash]
    #!/bin/bash
    # radioRecoder.sh
    # v0.2.0 – 20080808 :-)
    # Xwang
    # Released under GPL 2.0 and following (can you help me and tell if it is GPL3 compliant?)

    # This script acts as a wizard enabling the user to select the URL to listen and eventually rip it after having specified
    # the directory and the file name
    # The cancel button is used to go to the previous page
    # To exit the program press cancel in the URL page and follow the instruction

    # TBD
    # check if parameter is passed or not
    # modify parmeters so that recording can be done without user iteration
    # capture Ctrl-C in order to clean all temporary files and kill opened processes before closing
    # put confirmation on registration stop
    # open the directory where last file has been saved (maybe asking for it) at the end of the recording
    # translate messanges
    #

    # assign as default stream value the one passed ad first parameters if any (check TBD)
    STREAM=$1

    TEMPDIR=/tmp
    # inizialize variables
    EXIT=1
    STATE=1
    PIDlistener2=””
    PIDrecorder2=””

    while [ $EXIT -eq 1 ]
    do
    case $STATE in
    0)
    kdialog –title “RadioRecorder” –yesno “Do you want to exit the program?”
    EXIT=$?
    if [ $EXIT -eq 1 ]
    then
    STATE=1
    fi
    ;;
    1)
    # ask radio stream URL
    STREAM=`kdialog –title “RadioRecorder” –inputbox “Insert the radio stream url” $1 `
    STREAMBUTTON=$?
    if [ $STREAMBUTTON -eq 1 ]
    then
    STATE=0
    else
    # start mplayer in background to listen to the radio only if the ok button has been selected else exit
    # xterm -e mplayer -vo null -vc null -cache 512 $STREAM&#38;
    mplayer -vo null -vc null -cache 512 $STREAM&#38;
    PIDlistener=$! # listener PID
    while [ -z $PIDlistener2 ]
    do
    #repeat the check till PIDlistener2 is correctly detected
    PIDlistener2=`ps -ef| awk ‘$3 == ‘$PIDlistener’ { print $2 }’`
    done
    STATE=2
    fi
    ;;
    2)
    # select action to do
    ACTION=`kdialog –title “RadioRecorder” –menu “Select an action:” 1 “Change URL” 2 “Record stream”`
    ACTIONBUTTON=$?

    if [ $ACTIONBUTTON -eq 1 ]
    then
    ACTION=$ACTIONBUTTON # if cancel is selected go back to url dialog
    fi
    if [ $ACTION -eq 1 ]
    then
    #if Change URL or Cancel has been selected kill listener and go back to URL page
    #kill listener processes
    kill -9 $PIDlistener2
    kill -9 $PIDlistener
    # reinitialize PID variables
    PIDlistener=””
    PIDlistener2=””
    STATE=1
    else
    STATE=3
    fi
    ;;
    3)
    # ask destination directory — default home

    DIR=$HOME
    DIR=`kdialog –title “RadioRecorder – Select destination directory” –getexistingdirectory $DIR` #default destination directory is the user home
    DIRBUTTON=$?
    if [ $DIRBUTTON -eq 1 ]
    then
    #if Cancel has been selected go back to action page
    STATE=2
    else
    STATE=4
    fi
    ;;
    4)
    # ask file name at which will be added the date and eventually record stream
    FILENAME=”radio”
    FILENAME=`kdialog –title “RadioRecorder – Save as” –inputbox “Insert the file name (date will be automatically added)” $FILENAME`
    FILENAMEBUTTON=$?
    if [ $FILENAMEBUTTON -eq 1 ]
    then
    #if Cancel has been selected go back to action page
    STATE=3
    else
    #capture stream
    DATE=`date +%Y_%m_%d_%H%M%S`
    TEMPFILE=$TEMPDIR/$FILENAME-$DATE
    FILE=$DIR/$FILENAME-$DATE
    mkfifo $TEMPFILE.wav
    lame $TEMPFILE.wav $FILE.mp3&gt;/dev/null&#38;
    PIDconverter=$! #converter PIN
    # xterm -e mplayer -cache 512 -vo null -vc null -ao pcm:fast:file=”$TEMPFILE.wav” $STREAM&gt;/dev/null&#38;
    mplayer -cache 512 -vo null -vc null -ao pcm:fast:file=”$TEMPFILE.wav” $STREAM&gt;/dev/null&#38;
    PIDrecorder=$! #recorder PIN
    while [ -z $PIDrecorder2 ]
    do
    #repeat the check till PIDrecorder2 is correctly detected
    PIDrecorder2=`ps -ef| awk ‘$3 == ‘$PIDrecorder’ { print $2 }’`
    done
    kdialog –msgbox “Press OK button to stop recording”
    #stop recording
    #kill converter

    kill -9 $PIDconverter
    #kill recorder processes
    kill -9 $PIDrecorder2
    kill -9 $PIDrecorder
    # reinitialize PID variables
    PIDconverter=””
    PIDrecorder=””
    PIDrecorder2=””
    #removing temporary file
    rm $TEMPFILE.wav
    #go back to action page
    STATE=2
    fi
    ;;
    esac
    done

    kdialog –title “RadioRecorder” –msgbox “Thank you for using radioRecorder”
    echo “Bye Bye”
    exit
    [/bash]

  11. Shawn Dowler says:

    Quite honestly, I don’t know how you would accomplish pausing the recording. It’s streaming live, so you certainly can’t stop that part, but it may be possible to divert the stream through the fifos to /dev/null when a button is pressed and go back again afterward.

    I honestly don’t have the “code fu” to do anything like that or to answer your question without a lot of fiddling and researching. I’m glad that you find the code useful, and I do appreciate your posting here with updates when you make interesting modifications.

Leave a Reply

You must be logged in to post a comment.