Sedsongs
Quote from Doug McIlroy, creator of pipes in Unix:
This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.
source: wikipedia: Unix philosophy
This exercise explores how the pipeline can be used to translate textual data (in this case the numbers of the current date and time) into a short musical sequence.
See Bash
timidity
MIDI is a binary file format (and general protocol for communication between keyboards and synthesizers) that was created in the 1980s. Timidity is a good program to play midi files. Here is a sample file (File:I.mid). Play with:
timidity I.mid
midge
The MIDI file format is a binary one. This means that you need special tools that understand the format to work with them (create / modify). seq24 is one such tool.
Midge is a program that tranlates a (plain) text file in a particular markup language, into a playable (binary) midi file. This lets us work simply with text as a means of producing a MIDI song.
According to midge's special notation / markup, a very simple song would be:
@head {
$time_sig 4/4
$tempo 120
}
@body {
@channel 1 {
$patch 1
$octave 4
$length 16
# notes here!
c e g
}
}
(For more example files, see the examples that come with midge. Where are those? Try the command:
dpkg --listfiles midge
to see and look for the .mg files)
To prepare the midi file, you'd type the following command (assuming the above is called "song.mg":
midge song.mg
This produces an output file (if there were no errors), called "a.mid.out". You can choose a nicer name with the -o option:
midge song.mg -o song.mid
sed
So the idea is to make a simple translation between digits and tones. Taking a page from Arnold Schoenberg, who made use of all 12 halftones in the melodic scale (as opposed to the established Western conventions of musical keys using 7 base tones), we will map each of the ten digits to a halftone scale (so we actually only use 10 of the 12).
In other words:
1 becomes c 2 becomes c# 3 becomes d 4 becomes d#
and so on...
To do this transformation automatically, we'll make use of the commandline tool sed.
sed is one of the most powerful (if sometimes cryptic) commandline program. In this case we create a "sed script" (text file with a list of commands) to perform a number of search and replaces. In each case the rule takes the form:
s/a/b/g
Which means, search for "a", replace with "b", and replace all (the "g" or global option).
Below, the rules replace each digit with a tone (from c to a sharp). The first rule means "replace anything that isn't a number with nothing" (ie delete them).
s/[^0123456789]//g
s/1/c /g
s/2/c+ /g
s/3/d /g
s/4/d+ /g
s/5/e /g
s/6/f+ /g
s/7/g /g
s/8/g+ /g
s/9/a /g
s/0/a+ /g
You can run this sed script (assume it's called "digits.sed") with:
sed -f digits.sed
This will then wait for you to type some input, type something like:
123123 111 222 333
And press Ctrl-D and see what happens.
Note that extra spaces are added so that each note is separated as midge requires.
wrap.sh
This is a simple script that outputs the midge file header, then outputs whatever is coming through the pipeline (stdin) -- this is the lone "cat" command, and finally outputs the end of the midge file format. In other words, when you pipe some notes into this script, you get a simple complete midge file.
cat <<EOF
@head {
\$time_sig 4/4
\$tempo 120
}
@body {
@channel 1 {
\$patch 1
\$octave 4
\$length 16
# notes here!
EOF
cat
cat <<EOF
}
}
EOF
pipeline
Putting it all together now with the "pipe" character (|) connecting the parts:
date | sed -f digits.sed | bash wrap.sh | midge -o date.mid
timidity date.mid
looping
To here what happens over time, the following script runs the command 30 times, renames each file to a numbered file, then uses the oggCat tool to join all the files to a single audio file. [1]
for ((i=0; i<30; i++))
do
date | sed -f digits.sed | bash wrap.sh | midge -o date.mid
timidity date.mid -Ov
mv date.ogg date$(printf '%02d' $i).ogg
done
oggCat dateloop.ogg date*.ogg
The result: File:Dateloop.ogg