A musical 12-tone alarm for the Raspberry Pi (part 1)

This project started off relatively simply when I came across a cool script by Jonathan Kulp to generate and play a random 12 tone tune with three chord bars at the end. the output was displayed in a browser on the Pi screen as a midi file of the tune was played. it could be used as a cron job to form a simple alarm.
The program requires a standard raspian distribution plus the added packages
lilypond timidity and lame. It is also useful to install mpg321

This article will first discuss the original file, and an alteration I made to it, together with some of its limitations. I will then show how I developed this, first to allow for random rhythms in the three main bars of the tune, then to allow the script to be run via a remote ssh connection, whilst still displaying ok on the Pi LXDE desktop.

You are very unlikely to get a repeat of a generated tune with random rhythms, so I added a program to play the last created tune again, and also an option to archive all created tunes, which could subsequently all be played one after the other, or displayed on a web page, where they could be selected to play. To generate a large number of tunes, you can set up the script as a cron job, running every minute, or indeed at a set time as an alarm.

As a final option I amended the program to generate the tunes on the Pi, but to copy them via a mounted share to my Mac, where the program played and displayed them automatically on a Mac Browser. This way the pi could be run without a screen at all.

This is a great project, as it involved learning about a couple of great programs lilypond and timidity, doing battle with the bash scripting language, and doing a musical analysis in order to see how to generate the random rhythms in the tune. It also looks at what is involved in remotely operating scripts on two computers.

Set up your SD card

In order to run this of course you need to prepare your Pi. Starting with an up to date Raspian-Wheezy distribution (mine was on an 8Gb card, but 4Gb will do)
1 Go through the initial setup if necessary with raspi-config (or sudo raspi-config)
Don’t forget to expand the filesystem to fit your card if necessary.
2. after sudo apt-install update and sudo apt-install upgrade install the required apps
sudo apt-get -y install lilypond (about 358Mb with dependencies needs 912Mb of disk space)
sudo apt-get -y install timidity
sudo apt-get -y install lame mpg321

After all that I still have 642Mb left when installed on a 4Gb card

Time to get going
Although I think it is a good idea to type out scripts so that you can get to grips with how they work, in this case because they are quite complex I think it is safer to download them to avoid typos.  Spaces and use of . , ‘ ` which are all different, and difficult to distinguish, sometimes can be  critical in bash scripts, so I suggest that you just read the scripts here, (or more easily on a Pi using the links given)

The starting point was the original script is reproduced here from the front page of his article. However it has two or three errors and the chord section will not compile “as is”. (To be fair to Jon he lists the program elsewhere in a slightly different form that does work. I think this was  a miss-copy)
Since writing this, Jon has updated the original file and corrected the error. The “new” original version has the three lines I pick out below altered to ones that work

Click Original File below to expand
If you refresh the page the File will “collapse” again.

#!/bin/bash
#===========================================================================
#
#          FILE: serial.sh
#
#         USAGE: ./serial.sh
#
#   DESCRIPTION: generates tonerow randomly, compiles on Lilypond
#                and plays resulting MIDI file. Suitable for gentle
#                alarm in morning on my raspberry pi radio
#       OPTIONS: ---
#  REQUIREMENTS: lilypond, timidity, shuf
#          BUGS: ---
#         NOTES: ---
#        AUTHOR: Jonathan Kulp (),
#       CREATED: 03/20/2013 07:35:41 AM CDT
#===========================================================================

pitches=/tmp/pitches.ily
random=/tmp/random.ily
lilyfile=/tmp/tonerow.ly
midifile=/tmp/tonerow.midi
stem=$(readlink -f $lilyfile | sed -e 's/\..*$//')

withrhythms=/tmp/withrhythms.ily
chordOne=/tmp/chord1.ily
chordTwo=/tmp/chord2.ily
chordThree=/tmp/chord3.ily
prime=/tmp/prime.txt

getpitches (){
  # first make a list of all 12 chromatic pitches
  # using lilypond's naming conventions
  # one per line b/c it's easier to shuffle, add rhythms, etc
  cat > $pitches << EOFallpitches
c'
cis'
d'
dis'
e'
f'
fis'
g'
gis'
a'
bes'
b'
EOFallpitches
# now run them through the shuf command to put them in random order
shuf $pitches > $random
} # ------------ end of getpitches ---------------

add_rhythms(){
# todo: randomize the rhythms
sed -f - $random > $withrhythms << EOFrhythms
  1s/$/4/
  3s/$/4../
  4s/$/16/
  5s/$/4/
  6s/$/4./
  7s/$/8/
  8s/$/4/
  9s/$/8/
  10s/$/8/
  11s/$/8/
  12s/$/8/
EOFrhythms
} # ------------ end of add_rhythms ---------------

chords(){
  # stack up the notes of the row in 3 chords of
  # 4 notes each
  cat $random | sed -n '1,4p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' -e 'P;D' \
  | sed -e 's/^/2/' > $chordOne
  cat $random | sed -n '5,8p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' -e 'P;D' \
  | sed -e 's/^/4/' > $chordTwo
  cat $random | sed -n '9,12p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' -e 'P;D' \
  | sed -e 's/^/2./' > $chordThree
} # ------------ end of chords ---------------

make_lily_file (){
  # determine lilypond version for later inclusion
  version=$(lilypond --version | grep LilyPond | cut -d " " -f3)
  # make it cat the random list of pitches
  lilypitches=$(cat $withrhythms $chordOne $chordTwo $chordThree)
  # assemble the lilypond source file, sticking the
  # pitches in at the right place
  cat > $lilyfile << EOFscore
\\score {
{
\\version "$version"
\\time 3/4
#(set-accidental-style 'dodecaphonic)
#(set-global-staff-size 24)
\\set Staff.midiInstrument = "violin"
\\partial 4
$lilypitches
\\bar "|."
}
\\layout {} \\midi {\\tempo 4 = 120}
}
EOFscore
#cat $lilyfile
} # ------------ end of chords ---------------

runlily(){
# compile the score redirecting all console output to /dev/null
lilycmd="lilypond -dno-point-and-click -ddelete-intermediate-files -dpreview"
$lilycmd $lilyfile &> /dev/null
} # ------------ end of runlily ---------------

html(){
web="$stem".html
image="$stem".preview.png
midi="$stem".midi
cat > $web << EOFhtml
<meta http-equiv=Content-Type content="text/html; charset=UTF-8">
<title>12-Tone Tune of the Day</title>
<h2>Random 12-Tone Tune of the Day</h2>
<p>
<img src="tonerow.preview.png" alt="randomly generated 12-tone
row"
title="randomly-generated 12-tone row">
</p>
<a href="tonerow.midi">Midi file</a>
<p>
Generated with bash fu and <a href="http://lilypond.org"
target="_blank">Lilypond</a>
</p>
EOFhtml
} # ------------ end of html ---------------

# RUN ALL FUNCTIONS HERE

cd /tmp

getpitches
chords
add_rhythms
make_lily_file
runlily
# sleep for half a second to make sure Lilypond has time to compile
sleep .5
html
xdg-open $web
timidity $midifile &> /dev/null

#clean stuff up
rm /tmp/*.ily /tmp/*.eps

I had a problem with the last line in each chord section and had to alter lines 75, 78 and 81 respectively to

  | sed -e 's/.*/<&>2/' > $chordOne

  | sed -e 's/.*/<&>4/' > $chordTwo

  | sed -e 's/.*/<&>2./' > $chordThree

I also made one or two other changes to allow remote operation from an ssh terminal and changed some comments. Note for this version, if you are going to run it from an ssh connection you need to set midori as the default browser, which is NOT the same as the preferred application in Preferences. To do this, create a blank file in leafpad and save it as empty.html Then open FileManager, navigate to the file and right click on its icon. Choose Properties, and set Open with… to Midori instead of Netsurf Web Browser. You can change back later if you want.

I find that the midi file played through the hdmi sound channel often has the first note truncated. If you switch to the analog 3.5″ jack output on the Pi ti works ok, but you need to connect an amplifier to that. Changing the sound output is done by
amixer cset numid=3 1 to change to the analog output, and amixer cset numid=3 2 to use hdmi.

My amended file below will run “as is” directly from an LXterminal on the Pi. It is called serial1.sh and can be expanded below

#!/bin/bash
#===========================================================================
#
#          FILE: serial1.sh
#
#         USAGE: ./serial1.sh
#
#   DESCRIPTION: generates tonerow randomly, compiles on Lilypond
#                and plays resulting MIDI file. Suitable for gentle
#                alarm in morning on my raspberry pi radio
#       OPTIONS: ---
#  REQUIREMENTS: lilypond, timidity, shuf
#          BUGS: ---
#         NOTES: ---
#        AUTHOR: Jonathan Kulp (),
#       CREATED: 03/20/2013 07:35:41 AM CDT
#	minor amendments by Robin Newman
#===========================================================================
export DISPLAY=:0 #add so can run via ssh terminal
#needs midori as default browser to run via ssh
# save blank file from leafpad as empty.html, view file in Filemanager, right click
# choose properties and set open with to Midori
pitches=/tmp/pitches.ily
random=/tmp/random.ily
lilyfile=/tmp/tonerow.ly
midifile=/tmp/tonerow.midi
stem=$(readlink -f $lilyfile | sed -e 's/\..*$//')

withrhythms=/tmp/withrhythms.ily
chordOne=/tmp/chord1.ily
chordTwo=/tmp/chord2.ily
chordThree=/tmp/chord3.ily
prime=/tmp/prime.txt

getpitches (){
  # first make a list of all 12 chromatic pitches
  # using lilypond's naming conventions
  # one per line b/c it's easier to shuffle, add rhythms, etc
  cat > $pitches << EOFallpitches
c'
cis'
d'
dis'
e'
f'
fis'
g'
gis'
a'
bes'
b'
EOFallpitches
# now run them through the shuf command to put them in random order
shuf $pitches > $random
} # ------------ end of getpitches ---------------

add_rhythms(){
# todo: randomize the rhythms
sed -f - $random > $withrhythms << EOFrhythms
  1s/$/4/
  3s/$/4../
  4s/$/16/
  5s/$/4/
  6s/$/4./
  7s/$/8/
  8s/$/4/
  9s/$/8/
  10s/$/8/
  11s/$/8/
  12s/$/8/
EOFrhythms
} # ------------ end of add_rhythms ---------------

chords(){
  # stack up the notes of the row in 3 chords of
  # 4 notes each
  cat $random | sed -n '1,4p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' -e 'P;D' \
  | sed -e 's/.*/<&>2/' > $chordOne #line amended from original
  cat $random | sed -n '5,8p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' -e 'P;D' \
  | sed -e 's/.*/<&>4/' > $chordTwo #line amended from original
  cat $random | sed -n '9,12p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' -e 'P;D' \
  | sed -e 's/.*/<&>2./' > $chordThree #line amended from original
} # ------------ end of chords ---------------

make_lily_file (){
  # determine lilypond version for later inclusion
  version=$(lilypond --version | grep LilyPond | cut -d " " -f3)
  # make it cat the random list of pitches
  lilypitches=$(cat $withrhythms $chordOne $chordTwo $chordThree)
  # assemble the lilypond source file, sticking the
  # pitches in at the right place
  cat > $lilyfile << EOFscore
\\score {
{
\\version "$version"
\\time 3/4
#(set-accidental-style 'dodecaphonic)
#(set-global-staff-size 24)
\\set Staff.midiInstrument = "violin"
\\partial 4
$lilypitches
\\bar "|."
}
\\layout {} \\midi {\\tempo 4 = 100}
}
EOFscore
#cat $lilyfile
} # ------------ end of make_lily_file ---------------

runlily(){
# compile the score redirecting all console output to /dev/null
lilycmd="lilypond -dno-point-and-click -ddelete-intermediate-files -dpreview"
$lilycmd $lilyfile &> /dev/null
} # ------------ end of runlily ---------------

html(){
web="$stem".html
image="$stem".preview.png
midi="$stem".midi
cat > $web << EOFhtml
<meta http-equiv=Content-Type content="text/html; charset=UTF-8">
<title>12-Tone Tune of the Day</title>
<h2>Random 12-Tone Tune of the Day</h2>
<p>
<img src="tonerow.preview.png" alt="randomly generated 12-tone
row"
title="randomly-generated 12-tone row">
</p>
<a href="tonerow.midi">Midi file</a>
<p>
Generated with bash and <a href="http://lilypond.org"
target="_blank">Lilypond</a>
</p>
EOFhtml
} # ------------ end of html ---------------

# RUN ALL FUNCTIONS HERE

cd /tmp

getpitches
chords
add_rhythms
make_lily_file
runlily
# sleep for half a second to make sure Lilypond has time to compile
sleep .5
html
#rm -f ~/.config/midori/session.xbel #neater if using midori. deletes other tabs
#add & to next line to run as sub process so can work over ssh
xdg-open $web &
#wait for browser to open added a delay
sleep 8
timidity $midifile &> /dev/null
#clean stuff up
rm /tmp/*.ily /tmp/*.eps
 # ------------ end of script ---------------

You can view it here http://r.newman.ch/rpi/sounding-off/serial1.txt or you can type
wget http://r.newman.ch/rpi/sounding-off/serial1.txt to download it as a text file to your Pi, where you can rename it using mv serial1.txt serial1.sh and then make it executable by typing chmod 755 serial1.sh

The version with Random Rhythms

The main development now starts. Jon has an innocuous line in the program saying

# todo: randomize the rhythms

It turns out to be a huge can of worms!
Right click on the image below and open it in a separate page for reference

analysis

tonerow.preview

I decided to keep the crotchet up beat to each tune, and the rhythm of the three chords art the end, but to introduce random rhythms for the three intervening bars. There are a total of 11 notes to play with, and I decided to restrict note lengths to those shown in the analysis table (purple colour on the Analysis picture), ranging from a semi-quaver through quaver, dotted quaver,crotchet, dotted crotchet, double dotted crotched, minim to dotted minim. For analysis these have relative durations of 1 to 12. I could see that there were 6 Allowed 3 Bar Patterns (yellow section) (although the order of the bars could be shuffled too), shown in the table diagram covering the different ways in which 11 notes could be distributed in the three bars, constrained both to 3/4 time and the allowed note durations. Across the top of the Analysis picture (Blue section) are the different ways in which a bar could be made up with 1,2,3,4,5 and 6 notes. (Again the notes in these patterns could be shuffled in order). There are 28 discrete patterns. I gave names to each of these patterns shown in the Bar Names list.
In the light green section we see the duration notations used by the lilypond music engraver program substituted. Thus a dotted minim length 12 is represented by 2. and a quaver length 2 by 8.

Program Logic

So what the program would have to do to generate the random rhythms is this:

1 generate a barlist of the 28 patterns with names and structures, using the format
1n1 2. x 2n1 2 4 x 2n2 4. 4. x   etc where x was used as a terminator for each entry
2 strip just the bar names from barlist to get barnames
3 generate a list of the 6 allowed 3 bar combinations
4 choose one of these 3 bar combinations at random and shuffle the order of the three digts
for example choose 236 and shuffle it to 362
5 fill each of the three bars in turn.

In this example   (362) for the first bar (3 notes) choose a 3 note pattern at random from 3n1,352…3n6, say 3n4, extract the note values for this choice 4. 8. 8., put them in random order,say 8. 8. 4, and add them to the output note buffer called randombarnotes, which is initialised to contain a 4, the duration of the first crotchet. Repeat for the second bar (6 notes), choose a 6 note pattern at random from 6n1…654 say 6n3, extract the note values for this  4 8. 8 16 16 16 and put them in random order, say 16 8 16 16 4 8. , and add these to the output buffer, finally repeat for the third bar choose a 2 note pattern at random from 2n1 and 2n2, say 2n1, extract the note values 2 4, put them in random order, say 4 2, and add them to the output note buffer. So the output buffer will contain:
4 | 8. 8. 4 | 16 8 16 16 4 8. | 4 2 | for the durations of the 12 notes in the tune where I have added the | to indicate the bar lines, although they would not be in the actual list.

It sounds simple but is interesting to achieve in practice.

6 The remainder of the music generation  works much as in the original. A list of the twelve chromatic tones is generated, and then shuffled at random, this list is then merged with the randombarnotes containing the durations, so that the first note has the first duration appended, the second the second duration etc. and stored in a file withrhythms
7 a chord function reads note pitches 4 notes at a time from the tune and generates the appropriate lilypond code for them
8 a makelilyfile function assembles the complete surcecode file for lilypond, adding information such as tempo time signature.
9 an html function generates the webpage to display a preview picture of the music and a link to the midifile

Debugging Help

Because this is quite difficult to follow, and for debugging purposes, I wrote a function pvrar which could display vales of selected variables and temporary files at different stages in the running of the script. This is selected by changing the verbose variable from FALSE to TRUE at the start of the script. If you run the script with this set you get typical output as shown below.
Click the title to expand it. Refresh the page to collapse it again

./serialrpi.sh
Lapsed time initialised to 0
prefbrowser is midori
Generating source music file lilyfile.ly
==========
list of bar structures (variable name barlist)
1n1
2.
x
2n1
2
4
x
2n2
4.
4.
x
3n1
4..
4
16
x
3n2
4..
8.
8
x
3n3
4.
4
8
x
3n4
4.
8.
8.
x
3n5
4
4.
8
x
3n6
4
4
4
x
4n1
2
8
16
16
x
4n2
4..
8.
16
16
x
4n3
4..
8
8
16
x
4n4
4.
4
16
16
x
4n5
4.
8.
8
16
x
4n6
4
4
8.
16
x
4n7
4
4
8
8
x
4n8
8.
8.
8.
8.
x
5n1
2
16
16
16
16
x
5n2
4..
8
16
16
16
x
5n3
4.
8.
16
16
16
x
5n4
4.
8
8
16
16
x
5n5
4
4
8
16
16
x
5n6
4
8.
8
8
16
x
5n7
4
8
8
8
8
x
6n1
4.
8
16
16
16
16
x
6n2
4
4
16
16
16
16
x
6n3
4
8.
8
16
16
16
x
6n4
4
8
8
8
16
16
x
==========

==========
list of barnames from barlist (variable name barnames)
1n1
2n1
2n2
3n1
3n2
3n3
3n4
3n5
3n6
4n1
4n2
4n3
4n4
4n5
4n6
4n7
4n8
5n1
5n2
5n3
5n4
5n5
5n6
5n7
6n1
6n2
6n3
6n4
==========

==========
allowed 3 bar lengths (variable name allowed3bars)
146
155
236
245
335
344
==========

==========
Shuffled list (variable name chosenlist)
245
155
335
236
146
344
==========

==========
Chosen 3 bars (variable name rbars)
245
==========

==========
Chosen 3 bars in a list (variable name chosenlist)
2
4
5
==========

==========
Chosen 3 bars in random order list (variable name rbars)
2
5
4
==========

bar 1 has 2 notes
==========
possible bar names (variable name subbarlist)
2n1
2n2
==========

==========
Choice (variable name firstchoice)
2n2
==========

==========
barnotes (variable name barnotes)
4.
4.
==========

bar 2 has 5 notes
==========
possible bar names (variable name subbarlist)
5n2
5n3
5n4
5n5
5n6
5n7
==========

==========
Choice (variable name firstchoice)
5n5
==========

==========
barnotes (variable name barnotes)
4
4
8
16
16
==========

bar 3 has 4 notes
==========
possible bar names (variable name subbarlist)
4n1
4n2
4n3
4n4
4n5
4n6
4n7
4n8
==========

==========
Choice (variable name firstchoice)
4n6
==========

==========
barnotes (variable name barnotes)
4
4
8.
16
==========

==========
randomised rhythms for each of the three bars joined to first note (variable name randombarnotes)
4
4.
4.
16
4
16
4
8
8.
16
4
4
==========

==========
The 12 note pitches in random order (variable name random)
cis'
dis'
gis'
e'
a'
f'
g'
c'
fis'
bes'
d'
b'
==========

==========
random tune (variable name withrhythms)
cis' 4
dis' 4.
gis' 4.
e' 16
a' 4
f' 16
g' 4
c' 8
fis' 8.
bes' 16
d' 4
b' 4
==========

==========
1st chord (variable name chordOne)
<cis' dis' gis' e'>2
==========

==========
2nd chord (variable name chordTwo)
<a' f' g' c'>4
==========

==========
3rd chord (variable name chordThree)
<fis' bes' d' b'>2.
==========

==========
The completed source lilyfile (variable name lilyfile)
\score {
{
\version "2.14.2"
\time 3/4
\tempo 4 = 100
#(set-accidental-style 'dodecaphonic)
#(set-global-staff-size 22)
\set Staff.midiInstrument = "violin"
\partial 4
cis' 4
dis' 4.
gis' 4.
e' 16
a' 4
f' 16
g' 4
c' 8
fis' 8.
bes' 16
d' 4
b' 4
<cis' dis' gis' e'>2
<a' f' g' c'>4
<fis' bes' d' b'>2.
\bar "|."
}
\layout {} \midi {}
}
==========

Total 0 minutes and 2 seconds elapsed.
Compiling lilyfile.ly with lilypond
Total 0 minutes and 22 seconds elapsed.
==========
The html page (variable name web)
<meta http-equiv=Content-Type content="text/html; charset=UTF-8">
<title>12-Tone Tune of the Day</title>
<h2>Random 12-Tone Tune of the Day, with random rhythms</h2>
<p>
<img src="tonerow.preview.png" alt="randomly generated 12-tone
row"
title="randomly-generated 12-tone row">
</p>
<a href="tonerow.midi">Midi file</a>
<p>
Generated with bash and <a href="http://lilypond.org"
target="_blank">Lilypond</a>
<p>
generated on Tue Aug  6 17:44:15 BST 2013
<p>
Modified and developed by Robin Newman from an original script
by <a href="http://jonathankulp.org/archives/662">
Jonathan Kulp</a>
</p>
==========

Starting Web Browser
Playing midi file
browser killed
./serialrpi.sh: line 341:  2787 Terminated              $prefbrowser -e Fullscreen -a $web
Archiving
Finished!
Total 0 minutes and 44 seconds elapsed.

A word about lilypond At this stage I should perhaps say a bit more about lilypond, the musical engraver program at the heart of this project. It enables you to write out a musical piece in a text form, which it then parses and turns into a printable form using standard musical notation, which is output in various formats, including a postscript file, a pdf file and an optional preview image of the first line of the music. It can also produce a midi source file which can be played by any midi synthesiser. An actual source file for one of the randomly generated tunes is shown below. I won’t go into full details, but if you are interested the lilypond documentation is excellent. Have a look here at the Learning Manual, especially the tutorial section.

\score {
{
\version "2.14.2"
\time 3/4
\tempo 4 = 100
#(set-accidental-style 'dodecaphonic)
#(set-global-staff-size 22)
\set Staff.midiInstrument = "violin"
\partial 4
cis' 4
dis' 4.
gis' 4.
e' 16
a' 4
f' 16
g' 4
c' 8
fis' 8.
bes' 16
d' 4
b' 4
<cis' dis' gis' e'>2
<a' f' g' c'>4
<fis' bes' d' b'>2.
\bar "|."
}
\layout {} \midi {}
}

Briefly note pitches are represented by the letters c d e f g a b which are modified by adding is for a sharp es for a flat and by ‘ symbols to indicate the octave. The base octave is from the c below middle c. one ‘ raises the octave so the range here is the octave from middle c upward. Note duration as already indicated is in terms of length relative to a breve (not used here) which is four times the length of a crotchet. A minim is half a breve and this 1/2 note is represented by a duration 2. A crotchet is a quarter note 1/4 and is represented by a 4, a quaver an 1/8 and a semiquaver a 1/16 represented by 8 and 16. Dotted notes augment the duration by half the note value, and double dots add a further 1/4 of the note value. So a double dotted crotchet is 4.. and lasts for 1/4 + 1/8 + 1/16 or crotchet+quaver+semi-quaver. \partial 4 means the pice starts with a crotchet before the first bar (the upbeat). \time 3/4 gives the time signature. bar “|.” gives the closing bar line. Other bar lines are generated automatically as each bar is filled. The overall score inside {} contains a {} section containing the music followed by a \midi{} section which tells it to generate a midi file too. The eagle eyed amongst you will have noted that I moved the tempo setting from the midifile where Jon had it to the score section in a slightly different format. This displays the tempo on the printout, but more importantly I couldn’t change the midi speed with the first version, whereas in this version if you alter 100 to say 180, then the mid file plays more quickly as you would expect. I am really a beginner with this powerful program, but I would encourage you to explore it. It is very powerful, although probably needs a more powerful machine than a Pi to do itself full justice.
The source file is compiled with the rather fierce looking command:
lilypond -dno-point-and-click -ddelete-intermediate-files -dpreview  FILENAME
which disables a point and click debugging feature used with pdf output, deletes intermediate files used in the compilation process, and adds the generation of preview graphical output of the first system  (line of music). It is this preview image (.png) which is used in the generated web page

Other enhancements

Checking environment This version is designed to run either directly on the pi, or via an ssh connection> In this latter case, you need to check if there is a desktop running on the Pi already, and if not, alert the user, with instructions as to how to start one remotely. If the desktop is running, then you must set the DISPAY variable to point to the screen to use. The code below accomplishes this (situated after all the function definitions)

#check if Desktop is running
if [[ `pidof lxsession` = "" ]]
 then
 echo 'no desktop running on the Pi.startx & from this terminal, then rerun'
 exit
else
 export DISPLAY=:0
fi

Archiving Because there are millions of possible tunes, some of which you may like, others not so, I added the facility to add each tune generated to an archive location, ordered bu the date the tune was generated. Otherwise each tune is lost and overwritten when a new one is generated. I wrote additional programs described later to manipulate the archive. Archiving is achieved by setting the archivesave variable to TRUE at the start of the script, and is implemented by the archive function in the script. You also have to choose and set the archivepath variable to a suitable location. Choice of Browser The first version of the program uses the default browser, which is set to Netsurf on a new installation. As already discussed, to get the first version to use midori is a bit of a fiddly task, because of the program using xdg-open to select the browser when the music html page is opened. If you run the program from a remote ssh connection, it doesn’t work (version 1) with netsurf. I spent a long time fiddling with this, and looking at the configuration of xdg-open and why netsurf would not work (it loads, but doesn’t load the web page). Eventually I decided on a different approach which was to launch a specific browser directly, with the script choosing which one to use. This is set by the user in the prefbrowser variable at the beginning of the script. It is independent of any default setttings on the Pi, and it also has the advantage of working for either browser over an ssh connection, and gives the ability to launch midori in full screen mode. Instead of a line xdg-open $web & the program uses $prefbrowser -e Fullscreen -a £web & when prefbrowser is midori $prefbrowser file://$web & when prefbrowser is netsurf There is a commented line in the program #rm -f ~/.config/midori/session.xbel This was used before the Fullscreen option was added to make sure that all tabs in use the previous time midori was used are closed when the program is opened Optional close browser The original program leaves the browser open. I added an option to close it after the tune has played, again set as an option quitbrowser set to TRUE or FALSE at the start of the program. Timer For interest I added two timer functions timestart and timeduration to initialise and then display the elapsed time at various stages of the script running. The majority of the time is in compiling the music and the midifile. The resulting script The new script is called serialrpi.sh (serial tune r(andom) (displayed on the) pi The script can be expanded below. As before reload the page to collapse it again

#!/bin/bash
#===========================================================================
#					File serialrpi.sh
#	Can be run in LXDE terminal with ./serialrpi.sh
#	Or from a remote ssh connection
#	This version adds random rhythms
#   Option verbose FALSE or TRUE to control information out
#	Option archivesave TRUE or FALSE enables each tune to be saved to an archive
#	Set archive path appropriately if used, (will be created if necessary)
#	Written by Robin Newman, Aug 2013, based on a script by Jonathan Kulp (debug 10Mar2014)
#===========================================================================

#five manually defined parameters
verbose=FALSE #FALSE or TRUE. TRUE allows full output of process
prefbrowser=midori #select midori or netsurf
quitbrowser=TRUE #TRUE forces browser to quit at end of script or not if FALSE
archivesave=TRUE #set TRUE or FALSE to save archived tunes in archivepath
archivepath=~/tonearchive/ #set path to save timestamped archives (end in /)
#------------ set up variables for filenames ------------
pitches=/tmp/pitches.ily #pitches in 12 tone scale
random=/tmp/random.ily #pitches in scale in random order
lilyfile=/tmp/tonerow.ly #completed source music file for lilypond
midifile=/tmp/tonerow.midi #midifile created by lilypond
mp3file=/tmp/tonerow.mp3 #mp3 file created from the midi file by timidity

stem=$(readlink -f $lilyfile | sed -e 's/\..*$//') #prefix path for tone* files

withrhythms=/tmp/withrhythms.ily #used to hold assembled lilypond tune code
chordOne=/tmp/chord1.ily #lilypond code for the first chord
chordTwo=/tmp/chord2.ily #lilypond code for the second chord
chordThree=/tmp/chord3.ily #lilypond code for the third chord
#following files used in generation of random rhythms for the notes
allowed3bars=/tmp/allowed3bars.ily #3 bar patterns of 3/4 bars 11 notes in total
barnames=/tmp/barnames.ily #names of the 28 bar structures
barlist=/tmp/barlist.ily #all the possible 3/4 bar rhythms
                         #for 1 to 6 notes restricted to lengths 1 2 3
subbarlist=/tmp/subbarlist.ily #used to hold all the bar choices for a given number of notes in the bar
chosenlist=/tmp/chosenlist.ily # 3 bar list chosen from barlist
rbars=/tmp/rbars.ily #used to hold the chosen 3 bar lengths in random order
barnotes=/tmp/tune.ily #bar note values for each of the three bars in turn
randombarnotes=/tmp/randomtune.ily #assemble chosen note durations inc upbeat
 # ------------ end of file variable setup ---------------

 # ------------ start definition of functions ---------------

pvar(){
 #used to print a text string, a variable name and cat the contents (if a file)
 # or echo if not. Uses two supplied arguments $1 and $2
if [ $verbose = TRUE ] #allows suppression if not verbose mode
then
	echo -e '==========\n'$1' (variable name '$2')';
	if [ -f ${!2} ] #variable name passed as parameter is dereferenced to get the value
					#using the format ${!2}
		then
			cat ${!2}
		else
			echo ${!2}
		fi
	echo -e '==========\n'
fi
}
 # ------------ end of pvar ---------------

getpitches (){
 #makes a file of the pitches in the 12 tone scale and then puts in random order
  # first make a list of all 12 chromatic pitches
  # using lilypond's naming conventions
  # one per line b/c it's easier to shuffle, add rhythms, etc
  cat > $pitches << EOFallpitches
c'
cis'
d'
dis'
e'
f'
fis'
g'
gis'
a'
bes'
b'
EOFallpitches
# now run them through the shuf command to put them in random order
shuf $pitches > $random
pvar 'The 12 note pitches in random order' random
} # ------------ end of getpitches ---------------

getallowedbars () {
 #read in list of allowed bar patterns for 3 bars of 11 notes total
cat > $allowed3bars << EOFallowed3bars
146
155
236
245
335
344
EOFallowed3bars
pvar "allowed 3 bar lengths" allowed3bars
} #------------ end of getallowedbars ------------

getbarlist () {
 #generates a file containing the structure of the 28 possible distinct
 #bar types  in the structure name n n n x
 #first build as a string
b="1n1 2. x "
b+="2n1 2 4 x 2n2 4. 4. x "
b+="3n1 4.. 4 16 x 3n2 4.. 8. 8 x 3n3 4. 4 8 x 3n4 4. 8. 8. x \
3n5 4 4. 8 x 3n6 4 4 4 x "
b+="4n1 2 8 16 16 x 4n2 4.. 8. 16 16 x 4n3 4.. 8 8 16 x 4n4 4. 4 16 16 \
x 4n5 4. 8. 8 16 x 4n6 4 4 8. 16 x 4n7 4 4 8 8 x 4n8 8. 8. 8. 8. x "
b+="5n1 2 16 16 16 16 x 5n2 4.. 8 16 16 16 x 5n3 4. 8. 16 16 16 \
x 5n4 4. 8 8 16 16 x 5n5 4 4 8 16 16 x 5n6 4 8. 8 8 16 x 5n7 4 8 8 8 8 x "
b+="6n1 4. 8 16 16 16 16 x 6n2 4 4 16 16 16 16 x 6n3 4 8. 8 16 16 16 \
x 6n4 4 8 8 8 16 16 x"
echo $b |sed -e 's/\s\s*/\n/g' > $barlist
#convert string to file with one entry on each line (spaces replaced by LF)
pvar 'list of bar structures' barlist
} #------------ end of getbarlist ------------

chords(){
  # stack up the notes of the row in 3 chords of (re  -e 'P;D')
  # 4 notes each
  #NB modified from original script which didn't work for me
  cat $random | sed -n '1,4p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' \
  | sed -e 's/.*/<&>2/' > $chordOne
  cat $random | sed -n '5,8p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' \
  | sed -e 's/.*/<&>4/' > $chordTwo
  cat $random | sed -n '9,12p' \
  | sed -e :a -e '$!N;s/\n/ /;ta' \
  | sed -e 's/.*/<&>2./' > $chordThree
  pvar '1st chord' chordOne
  pvar '2nd chord' chordTwo
  pvar '3rd chord' chordThree

} # ------------ end of chords ---------------

make_lily_file (){
  # determine lilypond version for later inclusion
  version=$(lilypond --version | grep LilyPond | cut -d " " -f3)
  # make it cat the random list of pitches
  lilypitches=$(cat $withrhythms $chordOne $chordTwo $chordThree)
  # assemble the lilypond source file, sticking the
  # pitches in at the right place
  cat > $lilyfile << EOFscore
\\score {
{
\\version "$version"
\\time 3/4
\\tempo 4 = 100
#(set-accidental-style 'dodecaphonic)
#(set-global-staff-size 22)
\\set Staff.midiInstrument = "violin"
\\partial 4
$lilypitches
\\bar "|."
}
\\layout {} \\midi {}
}
EOFscore
pvar 'The completed source lilyfile' lilyfile
} # ------------ end of make_lily_file ---------------

runlily(){
# compile the score redirecting all console output to /dev/null
lilycmd="lilypond -dno-point-and-click -ddelete-intermediate-files -dpreview"
$lilycmd $lilyfile &> /dev/null
} # ------------ end of runlily ---------------

html(){ #generate html file
web="$stem".html
image="$stem".preview.png
midi="$stem".midi
mdate=`date +%Y-%m-%d-%H-%M-%S` #for archive date
mdate2=`date` #for html date
cat > $web << EOFhtml
<meta http-equiv=Content-Type content="text/html; charset=UTF-8">
<title>12-Tone Tune of the Day</title>
<h2>Random 12-Tone Tune of the Day, with random rhythms</h2>
<p>
<img src="tonerow.preview.png" alt="randomly generated 12-tone
row"
title="randomly-generated 12-tone row">
</p>
<a href="tonerow.midi">Midi file</a>
<p>
Generated with bash and <a href="http://lilypond.org"
target="_blank">Lilypond</a>
<p>
generated on $mdate2
<p>
Modified and developed by Robin Newman from an original script
by <a href="http://jonathankulp.org/archives/662">
Jonathan Kulp</a>
</p>
EOFhtml
pvar 'The html page' web
} # ------------ end of html ---------------

initrandombarnotes () {
#put 4 (crotchet duration) as first note value of randombarnotes
cat > $randombarnotes << EOFnotes
4
EOFnotes
} # ------------ end of initrandombarnotes ----------------

getbarnames() {
#generate list of barnames from barlist
cat $barlist |sed -n /n/p > $barnames #generate list of barnames from barlist
pvar 'list of barnames from barlist' barnames
} # ------------ end of getbarnames ----------------

makechosenbarsfile() {
#generate a file containing the number of notes in each of the three tune bars
shuf $allowed3bars > $chosenlist #get allowed bars in random order
pvar 'Shuffled list' chosenlist
cat $chosenlist |sed q > $rbars #select 1st line of randomised allowed bars list
pvar 'Chosen 3 bars' rbars
#this is the number we want. Need individual digits shuffled
cat $rbars |sed -e 's/\(.\)/\1\n/g'| sed -e '$d' >$chosenlist
#write 3 digit number to a file 1 digit per line
pvar 'Chosen 3 bars in a list' chosenlist
shuf $chosenlist > $rbars #put thes number in random order
pvar 'Chosen 3 bars in random order list' rbars
}
 # ------------ end of makechosenbarsfile ----------------

fillbar() {
 #add random rhythm for bar with number of notes in supplied arg $1
action=`cat $rbars |sed -n "$1{p;q;}"` #select no of notes from the list
echo bar $1 has $action notes
sed -n /${action}n/p $barnames > $subbarlist #get list of possible barnames
pvar 'possible bar names' subbarlist
read -r firstchoice < <(shuf $subbarlist) #shuffle and take first one as choice
pvar Choice firstchoice
cat $barlist | sed -n "/${firstchoice}/{:a;n;/x/q;p;ba}" > $barnotes
#extract relevant note durations from barlist to barnotes
shuf $barnotes >> $randombarnotes
#put durations in random order and add to randombarnotes
pvar barnotes barnotes
}
 # ------------ end of fillbar ----------------

addrhythms() {
#merges the pitches and durations together to get tune
paste -d " " $random $randombarnotes > $withrhythms
pvar 'random tune' withrhythms
}
 # ------------ end of addrhythms ----------------

timestart() {
#initialise a datetimevariable
date1=$(date +"%s")
echo "Lapsed time initialised to 0"
}
 # ------------ end of timestart ----------------

timeduration() {
#calculate elapsed time from timestart and print
date2=$(date +"%s")
diff=$(($date2-$date1))
echo "Total $(($diff / 60)) minutes and $(($diff % 60)) seconds elapsed."
}
 # ------------ end of timeduratiopn ----------------

 archive(){
 #archive the tonerow folder
 archdir=$archivepath'tone-'$mdate  #$made date/time from html function
 mkdir -p $archdir #-p in case path doesn't exist
 cp /tmp/tone* $archdir
 }
# ------------ end of archive ----------------

#------------ program commands start from here -------------

timestart
#check if Desktop is running
if [[ `pidof lxsession` = "" ]]
 then
 echo 'no desktop running on the Pi.startx & from this terminal, then rerun'
 exit
else
 export DISPLAY=:0
fi
echo prefbrowser is $prefbrowser #print preferred browser name
echo 'Generating source music file lilyfile.ly'
cd /tmp #work in temp directory
getbarlist
getbarnames
getallowedbars
initrandombarnotes
makechosenbarsfile
fillbar 1
fillbar 2
fillbar 3

pvar 'randomised rhythms for each of the three bars joined to first note' randombarnotes

#at this stage random rhythm list is complete
#assemble final tune

getpitches
addrhythms
chords
make_lily_file
timeduration

echo 'Compiling lilyfile.ly with lilypond'
runlily
sleep 0.5
timeduration
html
if [ "$(pidof $prefbrowser)" ]  #first kill existing browser if running
then
killall $prefbrowser
fi
echo 'Starting Web Browser'
if [ $prefbrowser = midori ]
then
#rm -f ~/.config/midori/session.xbel
$prefbrowser -e Fullscreen -a $web &
else
$prefbrowser file://$web &
fi
sleep 4
#allow time for web browser to open
echo Playing midi file
sleep 2
timidity $midifile &> /dev/null #play midi file

#wait before killing browser
sleep 2
if [ $quitbrowser = TRUE ] #then force browser to quit
then
killall $prefbrowser
echo browser killed
fi
#clean stuff up
rm /tmp/*.ily /tmp/*.eps
if [ $archivesave = "TRUE" ]
then
echo 'Archiving';archive
fi
echo "Finished!"
timeduration
#------------ end of script -------------

you can view it also here  or you can type
wget http://r.newman.ch/rpi/sounding-off/serialrpi.txt to download it as a text file to your Pi, when you can rename it using mv serialrpi.txt serialrpi.sh and then make it executable by typing chmod 755 serialrpi.sh

Playing the last tune generated again
As I mentioned, each time serialrpi.sh is run, it generates a new tune, overwriting the original, although this may be archived elsewhere.
Accordingly I wrote another script called playlastpi.sh which plays and displays again on the Pi the last tune generated. It is esentially the second half of the serialrpi.sh script starting after the tune source file has been generated and compiled. As such it only has two optional parameters for quitbrowser and prefbrowser as described previously.

The script can be expanded below. As before reload the page to collapse it again

#!/bin/bash
#===========================================================================
#					File playlastpi.sh
#	Checks if files exist, and if so plays last compiled 12 note tune
#	generated by serialrpi.sh
#	Can be run in LXDE terminal with ./playlastpi.sh
#	Or from a remote ssh connection
#   Option quitbrowser FALSE or TRUE to control whether browser is shut at the end
#   Option prefbrowser selects browser to use, midori or netsurf
#	Written by Robin Newman, Aug 2013
#===========================================================================

quitbrowser=TRUE #forces browser to quit at end of script TRUE/FALSE
prefbrowser=midori #select midori or netsurf
#check if Desktop is running
if [[ `pidof lxsession` = "" ]]
  then
  echo 'no desktop running on the Pi.startx & from this terminal, then rerun'
  exit
 else
  export DISPLAY=:0 #set display
fi
echo prefbrowser is $prefbrowser
web=/tmp/tonerow.html
midifile=/tmp/tonerow.midi
if [ -f $web ] #check if webpage exists
then
if [ "$(pidof $prefbrowser)" ]  #first kill existing browser if running
then
killall $prefbrowser
fi
echo Starting Web Browser
if [ $prefbrowser = midori ]
then
$prefbrowser -e Fullscreen -a $web &
else
$prefbrowser file://$web &
fi
sleep 4
#allow time for web browser to open
echo Playing midi file
sleep 2
timidity $midifile &> /dev/null #play midi file
#wait before killing browser
sleep 2

if [ $quitbrowser = TRUE ] #then force browser to quit
then
killall $prefbrowser
echo browser killed
fi
else
echo "No $web so can't run"
fi

echo "Finished!"
#------------ end of script -------------

you can also view it here  or you can type
wget http://r.newman.ch/rpi/sounding-off/playlastpi.txt to download it as a text file to your Pi, when you can rename it using mv playlastpi.txt playlastpi.sh and then make it executable by typing chmod 755 playlastpi.sh

That’s the end of part 1. In part 2 I will add the programs to handle the archive and the produce pages such as this one. I will also show how to set up an alarm using a cron job.

Part 3 will further enhance the program to allow the option to move the generated tunes to a Mac computer, and to remotely launch and play them there.

At the end of the series a downloadable zip file of all the programs will be made available.

Leave a comment