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:

  1. #!/bin/bash
  2. #
  3. # record.sh
  4. #
  5. # Use mplayer to capture the stream
  6. # at $STREAM to the file $FILE
  7. #
  8. # example: record.sh my_radio_show 60 mms://someserver.com/stream
  9.  
  10. DIR=/home/shawn/PodCasts #directory where to save the file
  11. TEMPDIR=/tmp
  12.  
  13. # Don't edit anything below this line
  14. #######################################################
  15. DATE=`date +%d-%b-%Y`  # Save the date as DD-Mmm-YYYY
  16. YEAR=`date +%Y` # Save just the year as YYYY
  17. NAME=$1
  18. DURATION=$2 # enough to catch the show, plus a bit
  19. STREAM=$3
  20. TEMPFILE=$TEMPDIR/$NAME-$DATE
  21. FILE=$DIR/$NAME-$DATE # Where to save it
  22.  
  23. # Capture Stream
  24. mkfifo $TEMPFILE.wav
  25. mkfifo $TEMPFILE-silenced.wav
  26.  
  27. # The lame settings below are optimized for voice encoding
  28. # The sox command below strips out any silent portions
  29. lame -S -a -m m --ty "$YEAR" --vbr-new -V 9 --lowpass 13.4 --athaa-sensitivity 1 \
  30.     --resample 32 $TEMPFILE-silenced.wav $FILE.mp3 >/dev/null &
  31. sox $TEMPFILE.wav -c 1 $TEMPFILE-silenced.wav \
  32.     silence 1 0.2 0.5% -1 0.2 0.5% >/dev/null&
  33. /usr/bin/mplayer -quiet -cache 500 \
  34.     -ao pcm:file="$TEMPFILE.wav" -vc dummy -vo null \
  35.     -noframedrop $STREAM >/dev/null&
  36.  
  37. sleep 5
  38. # get the pid of all processes started in this script.
  39. PIDS=`ps auxww | grep $TEMPFILE | awk '{print $2}'`
  40.  
  41. # the & turns the capture into a background job
  42. sleep `echo ${DURATION}*60 | bc`  # wait for the show to be over
  43. kill $PIDS # kill the stream capture
  44. rm $TEMPFILE.wav
  45. 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

  1. #!/bin/sh
  2. #
  3. # today.sh -- schedule what programs you want to rip today using the
  4. # recorder script.
  5.  
  6. # Non-obvious paths
  7. RECORDER=$HOME/bin/record.sh
  8. RECORDER_HI=$HOME/bin/record-hi.sh
  9.  
  10. # Set up stations
  11. KFI="http://a814.l1977144512.c19771.g.lm.akamaistream.net/D/814/19771/v0001/reflector:44512?MSWMExt=.asf"
  12. WXNT=mms://wmc1.liquidviewer.net/WXNT
  13. WPGB="http://84.53.144.36:80/D/1046/20063/v0001/reflector:43803?MSWMExt=.asf"
  14. KOZZ=mms://lotusradio-kozz.wm.llnwd.net/lotusradio_kozz
  15.  
  16. # What day is it?
  17. TODAY=`date +%a`
  18.  
  19. # EVERY DAY
  20. #everyday() {}
  21.  
  22. # WEEKDAYS
  23. weekday() {
  24.     # "Record John_Ziegler for three hours, starting at 7:00pm."
  25.     echo $RECORDER John_Ziegler       180  $KFI | at  7:00pm
  26.     echo $RECORDER Glenn_Beck         180 $WPGB | at  6:00am
  27. }
  28.  
  29. # MONDAY
  30. if [ $TODAY = "Mon" ]; then
  31.     # everyday
  32.     weekday
  33. fi
  34.  
  35. # TUESDAY
  36. if [ $TODAY = "Tue" ]; then
  37.     # everyday
  38.     weekday
  39. fi
  40.  
  41. # WEDNESDAY
  42. if [ $TODAY = "Wed" ]; then
  43.     # everyday
  44.     weekday
  45. fi
  46.  
  47. # THURSDAY
  48. if [ $TODAY = "Thu" ]; then
  49.     # everyday
  50.     weekday
  51. fi
  52.  
  53. # FRIDAY
  54. if [ $TODAY = "Fri" ]; then
  55.     # everyday
  56.     weekday
  57. fi
  58.  
  59. # SATURDAY
  60. if [ $TODAY = "Sat" ]; then
  61.     # everyday
  62.     echo $RECORDER Handel_On_The_Law   300 $KFI | at  6:00am
  63.     echo $RECORDER Dr_Dean_Edell        60 $KFI | at  2:00pm
  64. fi
  65.  
  66. # SUNDAY
  67. if [ $TODAY = "Sun" ]; then
  68.     # everyday
  69.     echo $RECORDER Jesus_Christ_Show   180  $KFI | at  6:00am
  70.     echo $RECORDER Glenn_Beck          180 $WPGB | at 10:00am
  71.     echo $RECORDER_HI Dr_Demento       120 $KOZZ | at 10:00pm
  72. 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.

  1. # Schedule recordings for today from radio streams
  2. 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

    1. #!/bin/bash
    2. # radioRecoder.sh
    3. # v0.2.0 - 20080808 :-)
    4. # Xwang
    5. # Released under GPL 2.0 and following (can you help me and tell if it is GPL3 compliant?)
    6.  
    7. # This script acts as a wizard enabling the user to select the URL to listen and eventually rip it after having specified
    8. # the directory and the file name
    9. # The cancel button is used to go to the previous page
    10. # To exit the program press cancel in the URL page and follow the instruction
    11.  
    12.  
    13. # TBD
    14. # check if parameter is passed or not
    15. # modify parmeters so that recording can be done without user iteration
    16. # capture Ctrl-C in order to clean all temporary files and kill opened processes before closing
    17. # put confirmation on registration stop
    18. # open the directory where last file has been saved (maybe asking for it) at the end of the recording
    19. # translate messanges
    20. #
    21.  
    22.  
    23.  
    24. # assign as default stream value the one passed ad first parameters if any (check TBD)
    25. STREAM=$1
    26.  
    27. TEMPDIR=/tmp
    28. # inizialize variables
    29. EXIT=1
    30. STATE=1
    31. PIDlistener2=""
    32. PIDrecorder2=""
    33.  
    34. while [ $EXIT -eq 1 ]
    35. do
    36.     case $STATE in
    37.     0)
    38.         kdialog --title "RadioRecorder" --yesno "Do you want to exit the program?"
    39.         EXIT=$?
    40.         if [ $EXIT -eq 1 ]
    41.         then
    42.             STATE=1
    43.         fi
    44.     ;;
    45.     1)
    46.     # ask radio stream URL
    47.         STREAM=`kdialog --title "RadioRecorder" --inputbox "Insert the radio stream url" $1 `
    48.         STREAMBUTTON=$?
    49.         if [ $STREAMBUTTON -eq 1 ]
    50.         then
    51.             STATE=0
    52.         else
    53.             # start mplayer in background to listen to the radio only if the ok button has been selected else exit
    54. #           xterm -e mplayer -vo null -vc null -cache 512 $STREAM&
    55.             mplayer -vo null -vc null -cache 512 $STREAM&
    56.             PIDlistener=$! # listener PID
    57.             while [ -z $PIDlistener2 ]
    58.             do
    59.                 #repeat the check till PIDlistener2 is correctly detected
    60.                 PIDlistener2=`ps -ef| awk '$3 == '$PIDlistener' { print $2 }'`
    61.             done
    62.             STATE=2
    63.         fi
    64.     ;;
    65.     2)
    66.     # select action to do
    67.         ACTION=`kdialog --title "RadioRecorder" --menu "Select an action:" 1 "Change URL" 2 "Record stream"`
    68.         ACTIONBUTTON=$?
    69.  
    70.         if [ $ACTIONBUTTON -eq 1 ]
    71.         then
    72.             ACTION=$ACTIONBUTTON # if cancel is selected go back to url dialog
    73.         fi
    74.         if [ $ACTION -eq 1 ]
    75.         then
    76.             #if Change URL or Cancel has been selected kill listener and go back to URL page
    77.             #kill listener processes
    78.             kill -9 $PIDlistener2
    79.             kill -9 $PIDlistener
    80.             # reinitialize PID variables
    81.             PIDlistener=""
    82.             PIDlistener2=""
    83.             STATE=1
    84.         else
    85.             STATE=3
    86.         fi
    87.     ;;
    88.     3)
    89.     # ask destination directory -- default home
    90.  
    91.         DIR=$HOME
    92.         DIR=`kdialog --title "RadioRecorder - Select destination directory" --getexistingdirectory $DIR` #default destination directory is the user home
    93.         DIRBUTTON=$?
    94.         if [ $DIRBUTTON -eq 1 ]
    95.         then
    96.             #if Cancel has been selected go back to action page
    97.             STATE=2
    98.         else
    99.             STATE=4
    100.         fi
    101.     ;;
    102.     4)
    103.     # ask file name at which will be added the date and eventually record stream
    104.         FILENAME="radio"
    105.         FILENAME=`kdialog --title "RadioRecorder - Save as" --inputbox "Insert the file name (date will be automatically added)" $FILENAME`
    106.         FILENAMEBUTTON=$?
    107.         if [ $FILENAMEBUTTON -eq 1 ]
    108.         then
    109.             #if Cancel has been selected go back to action page
    110.             STATE=3
    111.         else
    112.             #capture stream
    113.             DATE=`date +%Y_%m_%d_%H%M%S`
    114.             TEMPFILE=$TEMPDIR/$FILENAME-$DATE
    115.             FILE=$DIR/$FILENAME-$DATE
    116.             mkfifo $TEMPFILE.wav
    117.             lame $TEMPFILE.wav $FILE.mp3>/dev/null&
    118.             PIDconverter=$! #converter PIN
    119. #           xterm -e mplayer -cache 512 -vo null -vc null -ao pcm:fast:file="$TEMPFILE.wav" $STREAM>/dev/null&
    120.             mplayer -cache 512 -vo null -vc null -ao pcm:fast:file="$TEMPFILE.wav" $STREAM>/dev/null&
    121.             PIDrecorder=$! #recorder PIN
    122.             while [ -z $PIDrecorder2 ]
    123.             do
    124.                 #repeat the check till PIDrecorder2 is correctly detected
    125.                 PIDrecorder2=`ps -ef| awk '$3 == '$PIDrecorder' { print $2 }'`
    126.             done
    127.             kdialog --msgbox "Press OK button to stop recording"
    128.             #stop recording
    129.             #kill converter
    130.  
    131.             kill -9 $PIDconverter
    132.             #kill recorder processes
    133.             kill -9 $PIDrecorder2
    134.             kill -9 $PIDrecorder
    135.             # reinitialize PID variables
    136.             PIDconverter=""
    137.             PIDrecorder=""
    138.             PIDrecorder2=""
    139.             #removing temporary file
    140.             rm $TEMPFILE.wav
    141.             #go back to action page
    142.             STATE=2
    143.         fi
    144.     ;;
    145.     esac
    146. done
    147.  
    148. kdialog --title "RadioRecorder" --msgbox "Thank you for using radioRecorder"
    149. echo "Bye Bye"
    150. exit
  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

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>


yandex.ruyandex.ru