#!/bin/bash # rtttl player by Jonas Häggqvist # # Based on the following documents: # - http://www.convertyourtone.com/rtttl.html # - http://www.sseyo.com/tao_group/ave/iss/mom/midifile/meta/rtttl/api.html # - http://en.wikipedia.org/wiki/Notes # - http://en.wikipedia.org/wiki/Scientific_pitch_notation # # License: BSD without advertising clause # Beep command. Must end with "-l 0" commandline="beep -l 0" case $1 in "-h*"|"--h*") echo "Usage: `basename $0` [rtttlfile] [verbose]" ;; "") rtttl="Bubble Bobble:d=4,o=5,b=125:8a#6,8a6,8g.6,16f6,8a6,8g6,8f6,8d#6,8g6,8f6,16d#6,8d.6,f.6,16d6,16c6,8a#,8c6,8d6,8d#6,8c6,16d6,8d#.6,8f6,8f6,8g6,16a6,8g.6,8f6,8f6,8g6,8a6,8a#6,8a6,8g.6,16f6,8a6,8g6,8f6,8d#6,8g6,8f6,16d#6,8d.6,f.6,16d6,16c6,8a#,8c6,8d6,8d#6,8c6,16d6,8d#.6,8f6,8f6,8g6,16a6,8f.6,8a#.6" ;; *) if [ ! -f $1 ]; then echo "Usage: `basename $0` rtttlfile [debug]" exit 1 else # Read entire file rtttl=`cat $1` fi ;; esac # Default values def_duration=4 def_scale=6 bpm=63 if [ "$2" != "" ]; then echo $rtttl echo fi # Extract the name name=`echo $rtttl |sed "s/\([^:]*\).*/\1/"` echo "-- $name --" # Extract settings settings=`echo $rtttl |sed "s/[^:]*:\([^:]*\).*/\1/"` # Parse settings for setting in `echo $settings |sed "s/,/ /g"`; do case ${setting%%=*} in d) def_duration=${setting##*=} ;; o) def_scale=${setting##*=} ;; b) bpm=${setting##*=} ;; *) echo "Error: Unknown default value: ${setting}" exit 1; esac done bar=`expr \( 4 \* 60000 \) / $bpm` if [ "$2" != "" ]; then echo "Default duration: $def_duration" echo "Default scale: $def_scale" echo "BPM: $bpm" echo "One bar: $bar ms" echo fi # Precalculate note values dur1=$bar dur2=`expr $bar / 2` dur4=`expr $bar / 4` dur8=`expr $bar / 8` dur16=`expr $bar / 16` dur32=`expr $bar / 32` echo -n "Parsing.. " # The two documents mentioned at the top disagree on the location of the # special-duration marker search="^\(1\|2\|4\|8\|16\|32\)\?\([PpCcDdEeFfGgAaHhBb]#\?\)\(\.\?\)\([5678]\?\)\$" song=`echo $rtttl |sed "s/[^:]*:[^:]*:\(.*\)/\1/"` for command in `echo $song |sed "s/,/ /g"`; do test=`echo $command |sed "s/${search}//"` if [ "$test" != "" ]; then echo "Error: Unrecognised note: $command" exit 2 else duration=`echo $command |sed "s/${search}/\1/"` if [ "$duration" == "" ]; then duration=$def_duration; fi note=`echo $command |sed "s/${search}/\2/"` scale=`echo $command |sed "s/${search}/\4/"` if [ "$scale" == "" ]; then scale=$def_scale fi dotted=`echo $command |sed "s/${search}/\3/"` if [ "$dotted" == "." ]; then dotted="yes"; else dotted="no"; fi fi if [ "$note" == "p" -o "$note" == "P" ]; then # This is not really a pause, but it works frequence="1" else case $scale in "5") case $note in "C"|"c") frequence="261.6" ;; "C#"|"c#") frequence="277.2" ;; "D"|"d") frequence="293.7" ;; "D#"|"d#") frequence="311.1" ;; "E"|"e") frequence="329.6" ;; "F"|"f") frequence="349.2" ;; "F#"|"f#") frequence="370.0" ;; "G"|"g") frequence="392.0" ;; "G#"|"g#") frequence="415.3" ;; "A"|"a") frequence="440.0" ;; "A#"|"a#") frequence="466.2" ;; "H"|"h"|"B"|"b") frequence="493.9" ;; *) echo "Error: Unknown note $note in $command" exit 4 ;; esac ;; "6") case $note in "C"|"c") frequence="523.3" ;; "C#"|"c#") frequence="554.4" ;; "D"|"d") frequence="587.3" ;; "D#"|"d#") frequence="622.3" ;; "E"|"e") frequence="659.3" ;; "F"|"f") frequence="698.5" ;; "F#"|"f#") frequence="740.0" ;; "G"|"g") frequence="784.0" ;; "G#"|"g#") frequence="830.6" ;; "A"|"a") frequence="880.0" ;; "A#"|"a#") frequence="932.3" ;; "H"|"h"|"B"|"b") frequence="987.8" ;; *) echo "Error: Unknown note $note in $command" exit 4 ;; esac ;; "7") case $note in "C"|"c") frequence="1047" ;; "C#"|"c#") frequence="1109" ;; "D"|"d") frequence="1175" ;; "D#"|"d#") frequence="1245" ;; "E"|"e") frequence="1319" ;; "F"|"f") frequence="1397" ;; "F#"|"f#") frequence="1480" ;; "G"|"g") frequence="1568" ;; "G#"|"g#") frequence="1661" ;; "A"|"a") frequence="1760" ;; "A#"|"a#") frequence="1865" ;; "H"|"h"|"B"|"b") frequence="1976" ;; *) echo "Error: Unknown note $note in $command" exit 4 ;; esac ;; "8") case $note in "C"|"c") frequence="2093" ;; "C#"|"c#") frequence="2217" ;; "D"|"d") frequence="2349" ;; "D#"|"d#") frequence="2489" ;; "E"|"e") frequence="2637" ;; "F"|"f") frequence="2794" ;; "F#"|"f#") frequence="2960" ;; "G"|"g") frequence="3136" ;; "G#"|"g#") frequence="3322" ;; "A"|"a") frequence="3520" ;; "A#"|"a#") frequence="3729" ;; "H"|"h"|"B"|"b") frequence="3951" ;; *) echo "Error: Unknown note $note in $command" exit 4 ;; esac ;; *) # This shouldn't happen since the sed command shouldn't accept it echo "Error: Invalid scale $scale in $command" exit 3 esac fi eval duration_ms=\$dur$duration if [ "$dotted" == "yes" ]; then extra=`expr $duration_ms / 2` duration_ms=`expr $extra + $duration_ms` fi if [ "$2" != "" ]; then echo " Note: $note" echo " Scale: $scale" echo " Frequence=$frequence Hz" echo " Duration: $duration" echo " Dotted: $dotted" echo " Duration: $duration_ms ms" echo fi commandline="${commandline} -n -f ${frequence} -l ${duration_ms}" done echo "done" if [ "$2" != "" ]; then echo "Command to run:" echo $commandline fi echo -n "Playing.. " eval $commandline echo "done"