A third visit to Mozart’s Dice generated minuet using Sonic Pi

Screen Shot 2016-01-20 at 18.11.59

I have previously written two articles  on this topic, the first in August 2013, which used python and lilypond together with timidity to play the midi files produced. Imagemagick was used to facilitate the production a web pages to display the music while it played. Whilst this worked it was quite complex to set up, and fairly slow to generate each minuet.

In the second article in January 2015 I used Sonic Pi to play the minuets, but still used lilypond to produce the music display, and was quite complex to setup.

Now in this third visit, the whole process is controlled by Sonic Pi, and I utilise 176 images of the different bars to build a web page displaying the current minuet being played.
Sonic Pi is helped by an external script, written in Ruby which interacts with the Sonic Pi workspace to produce the webpages which are displayed in the Epiphany browser on Raspbian Jesse, using a Pi2 to run the whole process.

I have taken the opportunity to rework the code in Sonic Pi which produces the sound from the version used in 2015. The present version is much quicker in producing the webpages and the whole process works in a more streamlined manner than previously.

Two additions needs to be installed. The first is the sonic-pi-cli gem. In order to do this, without installing and using a system like rvm, we have to do a bit of preliminary work so that the correct permissions will be in place during the install process. First, if you have switched to booting into a command line environment, start the graphical environment using startx. You may also like to switch back to this environment on boot by using Raspberry Pi Configuration utility in Menu -> Preferences. The graphics environment is needed for Sonic Pi to work.
Start a terminal window and type the following:

sudo mkdir /var/lib/gems
sudo chown pi /var/lib/gems
sudo chown pi /usr/local/bin
gem install sonic-pi-cli

Then revert the ownership of the /usr/local/bin folder

sudo chown root /usr/local/bin

Note if you a have a pre-existing /var/lib/gems folder you will get an error when you try and create it. Ignore the error, but alter the second line above from

sudo chown pi /var/lib/gems

to

sudo chown pi -R /var/lib/gems

Now test the cli by starting Sonic Pi, loading in any piece of reasonable duration, eg one of the example files, and running it. While it is running from the terminal window type:

sonic_pi stop

The piece should stop playing, showing that the cli command is working.

The second addition is to add the package xautomation which is used to switch the browser to full screen view.

sudo apt-get update
sudo apt-get install xautomation

Other than that, you only need to download the Sonic Pi file MozartDiceMinuet.rb and the helper Ruby script minuetcontrol.rb together with the image folder cards which contains the 176 images of the separate bars. Download them all to the pi home folder.

Open MozartDiceMinuets.rb with leafpad and copy the text into an empty Sonic Pi buffer.
start a terminal window, and start the control script running by typing

ruby minuetcontrol.rb

and pressing return

(You will not see anything happening, as the script awaits the creation of a text file which is done by Sonic Pi)
Now switch to Sonic Pi and run the workspace containing MozartDiceMinuet.

As supplied the program should play three waltzes one after the other, displaying the music on the Epiphany web-browser.

To re-run, you just need to start the minuetcontrol.rb script again, before pressing run on Sonic Pi.

In the sonic-pi program there are three user adjustments you can make.

numtunes is a variable that holds the number of minutes to generate. (default value 3)
use_random_seed(123456) is used to adjust the random number generator seed, and hence the sequence of minutes that will be generated. If you leave it unalterd you will always get the SAME three minutes generated. Alter it to produce a different sequence.
testbars=0 is a variable which is normally set to 0. If you set it to 1 then the program will generate 11 sequences, each containing 16 bars in numerical order from the 176 that are available. This is used for testing, to check each bar sequence is correct.

In the accompanying minuetcontrol.rb script there is one user adjustment
The variable bpix=1200 holds the width of the table (in pixels) which will display the bar images in the html page displayed on the Epiphany browser. This should be adjusted to suit the resolution of the screen you are using.  It should be approx 80-100 pixels LESS than the screen width, but should also be a multiple of 8. (I used 1200 for my HDMPPi monitor, 720 when running on a Raspberry Pi official touch screen monitor, and 1840 when running on my 19.5″ BENQ monitor).

The listings for the Sonic-Pi MozartDiceProgram and the minuetcontrol.rb script are shown below.

#Mozart's Musikalisches Wurkfelspiel KV 516f coded for Sonic Pi
#by Robin Newman January 2016
#This version uses a helper ruby script minuetcontrol.rb which should be launched
#prior to starting the Sonic Pi script using: ruby minuetcontrol.rb
#It also requires the folder cards to be located in the pi home directory.
#The music is displayed in the epiphany browser.

###### user adjustable parameters ####################
#set the number of minuets (numtunes) to play and the random number seed
numtunes=3
use_random_seed(123456)
testbars=0 #set to 1 to test all bars in sequence
###### end of user adjustable parameters #############

numtunes=11 if testbars==1
set_sched_ahead_time! 5 #give SP plenty of time to setup notes
use_debug false
use_synth :tri
#define arrays to holds note info for each bar lh and rh
rn=[]
ln=[]
rd=[]
ld=[]
#duration info: s is tempo scale factor
s=0.2
dsq=0.5*s
sq=1*s
q=2*s
qd=3*s
c=4*s
cd=6*s

p1=[sq]*6 #rhythm patterns
p2=[sq,sq,sq,sq,q]
p3=[q,sq,sq,sq,sq]
p4=[q,q,q]
p5=[c,q]
p6=[q,c]
p7=[q,sq,sq,q]
p8=[sq,sq,q,q]
p9=[sq,sq,q,sq,sq]
#plays a given bar, rh and lh, flip is used for 2nd time bar change
define :pb do |n,flip=0|
in_thread do
rn[n].zip(rd[n]).each do |p,d|
play p,attack:0.08*d,sustain: 0.82*d,release: 0.1*d,amp: 0.8
sleep d
end
end
nl=n
#change lh for repeat bars
if (flip==1) and [5,24,30,33,81,91,94,100,107,123,127].include? n then
nl=177 #alternative lh for second time bar
end
ln[nl].zip(ld[nl]).each do |p,d|
play p,attack:0.08*d,sustain: 0.82*d,release: 0.1*d,amp: 0.8
sleep d
end
end
define :trn do |n,num,offset=2| #produces trill sequence using n and tone (or semitone) above: n notes total
n = note_info(n).midi_note
return [n,n+offset]*(num / 2) #trill set to start on lower note. Can be swapped over
end
define :trd do |d,num| #produces trill note durations. d is total duration, num number of notes
return [d/num]*num
end
#define lookup table which selects bar numbers to play
tbl=[[96, 22, 141, 41, 105, 122, 11, 30, 70, 121, 26, 9, 112, 49, 109, 14],
[32, 6, 128, 63, 146, 46, 134, 81, 117, 39, 126, 56, 174, 18, 116, 83],
[69, 95, 158, 13, 153, 55, 110, 24, 66, 139, 15, 132, 73, 58, 145, 79],
[40, 17, 113, 85, 161, 2, 159, 100, 90, 176, 7, 34, 67, 160, 52, 170],
[148, 74, 163, 45, 80, 97, 36, 107, 25, 143, 64, 125, 76, 136, 1, 93],
[104, 157, 27, 167, 154, 68, 118, 91, 138, 71, 150, 29, 101, 162, 23, 151],
[152, 60, 171, 53, 99, 133, 21, 127, 16, 155, 57, 175, 43, 168, 89, 172],
[119, 84, 114, 50, 140, 86, 169, 94, 120, 88, 48, 166, 51, 115, 72, 111],
[98, 142, 42, 156, 75, 129, 62, 123, 65, 77, 19, 82, 137, 38, 149, 8],
[3, 87, 165, 61, 135, 47, 147, 33, 102, 4, 31, 164, 144, 59, 173, 78],
[54, 130, 10, 103, 28, 37, 106, 5, 35, 20, 108, 92, 12, 124, 44, 131]]

define :throw do #throws two dice and returns total (throws offset 0-5 to match arrays)

return rrand_i(0,5)+rrand_i(0,5)
end
#pick the bars for a minuet
define :minuet do
w=[]
8.times do |i|
w.concat [tbl[throw][i]]
end
8.times do |i|
w.concat [tbl[throw][i+8]]
end
return w
end
rn[1]=[:f5,:d5,:g5];rd[1]=p4
ln[1]=[:f3,:d3,:g3];ld[1]=p4
rn[2]=[:a4,:fs4,:g4,:b4,:g5];rd[2]=p3
ln[2] = ln[86] = ln[121] = ln[133] = [[:b2,:g3],:r];ld[2] = ld[86] = ld[121] = ld[133] =p5
rn[3]=rn[59]=rn[87]=rn[144]=[:g5,:c5,:e5];rd[3]=rd[59]=rd[87]=rd[144]=p4
ln[3] = ln[6] = ln[32] = ln[40] = ln[41] = ln[43] = ln[51] = ln[60] = ln[69] = ln[74] = ln[84] = \
ln[95] = ln[115] = ln[119] = ln[136] = ln[142] = ln[148] = ln[152] = ln[167] = [[:c3,:e3],:r];ld[3] = ld[6] = ld[32] = ld[40] = ld[41] = \
ld[43] = ld[51] = ld[60] = ld[69] = ld[74] = ld[84] = ld[95] = ld[115] = ld[119] = ld[136] = ld[142] = ld[148] = ld[152] = ld[167] = p5
rn[4]=[:g5]+trn(:d5,8);rd[4]=[q]+trd(c,8)
ln[4]=[:g2,:b2,:g3,:b2];ld[4]=p8
rn[5]=rn[24]=rn[30]=rn[33]=rn[81]=rn[91]=rn[94]=rn[100]=rn[107]=rn[123]=rn[127]=[[:g4,:b4,:d5,:g5],:r]; \
rd[5]=rd[24]=rd[30]=rd[33]=rd[81]=rd[91]=rd[94]=rd[100]=rd[107]=rd[123]=rd[127]=p5
ln[5] = ln[24] = ln[30] = ln[33] = ln[81] = ln[91] = ln[94] = ln[100] = ln[107] = ln[123] = ln[127] = [:g2,:g3,:f3,:e3,:d3]; \
ld[5] = ld[24] = ld[30] = ld[33] = ld[81] = ld[91] = ld[94] = ld[100] = ld[107] = ld[123] = ld[127] = p3
rn[6]=rn[32]=rn[174]=[:g4,:c5,:e5];rd[6]=rd[32]=rd[174]=p4
rn[7]=[:e5,:c5,:e5,:g5,:c6,:g5];rd[7]=p1
ln[7]=[[:c3,:g3],:r];ld[7]=p5
rn[8] = rn[14] = rn[79] = rn[83] = rn[93] = rn[111] = rn[131] = rn[151] = rn[170] = rn[172] = [:c5,:r];\
rd[8] = rd[14] = rd[79] = rd[83] = rd[93] = rd[111] = rd[131] = rd[151] = rd[170] = rd[172] = p5
ln[8] = ln[14] = ln[79] = ln[83] = ln[93] = ln[111] = ln[131] = ln[151] = ln[170] = ln[172] = [:c3,:g2,:c2];\
ld[8] = ld[14] = ld[79] = ld[83] = ld[93] = ld[111] = ld[131] = ld[151] = ld[170] = ld[172] = p4
rn[9]=[[:c5,:e5],[:b4,:d5],:r];rd[9]=p4
ln[9]=[:g3,:g2];ld[9]=p5
rn[10]=[:b4,:a4,:b4,:c5,:d5,:b4];rd[10]=[sq]*8
ln[10]= ln[34]= [:g3,:r];ld[10]= ld[34]= p5
rn[11]=[:e5,:c5,:b4,:a4,:g4,:fs4];rd[11]=[sq]*8
ln[11] = ln[21] = ln[36] = ln[62] = ln[106] = ln[110] = ln[118] = ln[134] = ln[147] = ln[159] = ln[169] = [:c3,:d3,:d2];\
ld[11] = ld[21] = ld[36] = ld[62] = ld[106] = ld[110] = ld[118] = ld[134] = ld[147] = ld[159] = ld[169] = p4
rn[12] = rn[54] = rn[124] = rn[130] = [[:e4,:c5]]*3;rd[12] = rd[54] = rd[124] = rd[130] =p4
ln[12] = ln[54] = ln[75] = ln[124] = ln[130] = ln[146] = ln[161]= [:c3,:c3,:c3];\
ld[12] = ld[54] = ld[75] = ld[124] = ld[130] = ld[146] = ld[161]= p4
rn[13]=[:c5,:g4,:e4];rd[13]=p4
ln[13] = ln[17] = ln[45] = ln[50] = ln[61] = ln[85] = ln[103] = ln[156] = [[:e3,:g3],:r];\
ld[13] = ld[17] = ld[45] = ld[50] = ld[61] = ld[85] = ld[103] = ld[156] = p5
rn[15]=[:e5,:g5,:e5,:c5];rd[15]=p7
ln[15] = ln[19] = ln[48] = ln[101] = ln[108] = ln[162] = [[:c3,:g3],[:c3,:e3]];\
ld[15] = ld[19] = ld[48] = ld[101] = ld[108] = ld[162] = p5
rn[16]=[:a5,:fs5,:d5];rd[16]=p4
ln[16]=ln[120]=[[:d3,:fs3],[:c3,:fs3]];ld[16]=ld[120]=p5
rn[17]=[:c5,:g4,:c5,:e5,:g4,:c5];rd[17]=p1
rn[18]=[:g4,:c5,:e5];rd[18]=p4
ln[18] = ln[76] = ln[87] = [[:c3,:e3],[:c3,:g3]];ld[18] = ld[76] = ld[87] = p5
rn[19]=[:e5,:c5,:e5,:g5];rd[19]=p8
rn[20]=[:g5,:b5,:d6,:d5];rd[20]=p7
ln[20]=ln[139]=[:b2,:r];ld[20]=ld[139]=p5
rn[21]=[:c5,:e5,:g5,:d5,:a4,:fs5];rd[21]=p1
rn[22]=rn[57]=rn[96]=rn[112]=[:e5,:c5,:g4];rd[22]=rd[57]=rd[96]=rd[112]=p4
ln[22] = ln[53] = ln[63] = ln[80] = ln[96] = ln[104] = ln[105] = ln[153] = ln[154] = ln[157] = [:c3,:r];\
ld[22] = ld[53] = ld[63] = ld[80] = ld[96] = ld[104] = ld[105] = ld[153] = ld[154] = ld[157] = p5
rn[23]=[:f5,:e5,:d5,:e5,:f5,:g5];rd[23]=p1
ln[23]=[:f3,:e3,:d3,:e3,:f3,:g3];ld[23]=p1
rn[25]=[:d4,:fs4,:a4,:d5,:fs5,:a5];rd[25]=p1
ln[25]=ln[70]=[:d3,:c3];ld[25]=ld[70]=p5
rn[26]=[[:c5,:e5]]*3;rd[26]=p4
ln[26]=[:c3,:e3,:g3,:e3,:c4,:c3];ld[26]=p1
rn[27]=[:f5,:e5,:f5,:d5,:c5,:b4];rd[27]=p1
ln[27] = ln[113]=ln[166]=[[:g3,:b3],:r];ld[27]=ld[113]=ld[166]=p5
rn[28]=[:fs5,:d5,:a4,:a5,:fs5,:d5];rd[28]=p1
ln[28]=[[:c3,:a3],:r];ld[28]=p5
rn[29]=[:b4,:d5,:g5,:d5,:b4];rd[29]=p2
ln[29]=[:g3,:g2];ld[29]=p5
rn[31]=[:e5,:c5,:g4,:e5];rd[31]=p8
ln[31]=ln[64]=[[:c3,:g3],[:c3,:g3]];ld[31]=ld[64]=p5
rn[34]=[:e5,:c5,:d5,:b4,:g4];rd[34]=p2
rn[35]=[:c5,:d5,:fs5];rd[35]=p4
ln[35]=[[:d3,:fs3],[:c3,:a3]];ld[35]=p5
rn[36]=[:a4,:e5,:d5,:g5,:fs5,:a5];rd[36]=p1
rn[37]=[:g5,:b5,:g5,:d5,:b4];rd[37]=p2
ln[37] = ln[46] = ln[47] = ln[55] = ln[155] = ln[163] =[[:b2,:d3],:r];\
ld[37] = ld[46] = ld[47] = ld[55] = ld[155] = ld[163] =p5
rn[38]=rn[98]=rn[137]=rn[142]=[:c5,:g4,:e5];rd[38]=rd[98]=rd[137]=rd[142]=p4
ln[38] = ln[49] = ln[57] = ln[58] = ln[59] = ln[73] = ln[98] = ln[112] = ln[137] = ln[144] = ln[174] =[[:c3,:e3],:g3]*3;\
ld[38] = ld[49] = ld[57] = ld[58] = ld[59] = ld[73] = ld[98] = ld[112] = ld[137] = ld[144] = ld[174] = p1
rn[39]=[:g5,:g4,:g4];rd[39]=p4
ln[39]=[:b2,:d3,:g3,:d3,:b2,:g2];ld[39]=p1
rn[40]=rn[67]=rn[160]=[:c5,:b4,:c5,:e5,:g4,:c5];\
rd[40]=rd[67]=rd[160]=p1
rn[41]=[:c5,:b4,:c5,:e5,:g4];rd[41]=p2
rn[42]=[:b4,:c5,:d5,:b4,:a4,:g4];rd[42]=p1
ln[42] = ln[128] = ln[158] =[:g2,:r];ld[42] = ld[128] = ld[158] =p5
rn[43]=rn[60]=rn[152]=rn[168]=[:g5,:f5,:e5,:d5,:c5];\
rd[43]=rd[60]=rd[152]=rd[168]=p3
rn[44]=[:a4,:f5,:d5,:a4,:b4];rd[44]=p3
ln[44] = ln[52] = ln[72] = ln[116] = ln[145] = ln[149] = ln[173] =[:f3,:g3];\
ld[44] = ld[52] = ld[72] = ld[116] = ld[145] = ld[149] = ld[173] =p5
rn[45]=[:c5,:b4,:c5,:g4,:e4,:c4];rd[45]=p1
rn[46]=[:g5,:b5,:g5,:d5,:b4];rd[46]=p3
rn[47]=[:g5,:g5,:d5,:b5];rd[47]=p7
rn[48]=[:e5,:c5,:e5,:g5,:c6];rd[48]=p3
rn[49]=[:e5,:c5,:g4];rd[49]=p4
rn[50]=[:c5,:e5,:c5,:g4];rd[50]=p7
rn[51]=rn[84]=rn[115]=[:c5,:g4,:e5,:c5,:g5,:e5];rd[51]=rd[84]=rd[115]=p1
rn[52]=[:d5,:cs5,:d5,:f5,:g4,:b4];rd[52]=p1
rn[53]=[[:c5,:e5],[:c5,:e5],[:d5,:f5],[:e5,:g5]];rd[53]=p7
rn[54]=[[:e4,:c5]]*3;rd[54]=p4
rn[55]=[:g5,:b5,:d5];rd[55]=p4
ln[55]=[[:b2,:d3],:r];ld[55]=p5
rn[56]=[:d5,:b4,:g4,:r];rd[56]=p8
ln[56] = ln[92] = [[:g2,:g3],:g3];ld[56] = ld[92] = p5
rn[58]=[:g5,:e5,:c5];rd[58]=p4
rn[61]=[:c5,:e5,:c5,:g5];rd[61]=p7
rn[62]=[:e5,:c5,:b4,:g4,:a4,:fs4];rd[62]=p1
rn[63]=[:e5,:c5,:b4,:c5,:g4];rd[63]=p2
rn[64]=[:e5,:g5,:c6,:g5,:e5,:c5];rd[64]=p1
rn[65]=[:d5,:a4,:d5,:fs5];rd[65]=p8
ln[65]=ln[117]=[[:d3,:fs3],:r];ld[65]=ld[117]=p5
rn[66]=[:fs5,:a5,:fs5];rd[66]=p4
ln[66]=[[:d3,:a3],[:d3,:fs3],[:c3,:d3]];ld[66]=p4
ln[67] = ln[168] =[[:c3,:e3],[:e3,:g3]];ld[67] = ld[168] =p5
rn[68]=[:g5,:b5,:g5,:d5,:g5];rd[68]=p3
ln[68] = ln[165] = [:b2,:r];ld[68] = ld[165] = p5
rn[69]=[:g5,:e5,:c5];rd[69]=p4
rn[70]=rn[105]=[:fs5,:a5,:fs5,:d5,:fs5];rd[70]=rd[105]=p3
rn[71]=[:g5,:b5,:d6,:b5,:g5];rd[71]=p2
ln[71] = ln[88] = ln[143] = ln[176] = [[:b2,:d3]]*2;ld[71] = ld[88] = ld[143] = ld[176] =p5
rn[72]=[:f5,:e5,:d5,:c5,:b4,:d5];rd[72]=p1
rn[73]=[:g5,:e5,:c5];rd[73]=p4
rn[74] = rn[76] = rn[136] = rn[148] = [:c6,:b5,:c6,:g5,:e5,:c5];rd[74] = rd[76] = rd[136] = rd[148] = p1
rn[75]=[[:d5,:fs5]]*3;rd[75]=p4
rn[77]=[:g5,:b5,:g5,:d5];rd[77]=p8
ln[77]=[[:b2,:d3],[:b2,:g3]];ld[77]=p5
rn[78]=[:c5,:c4,:r];rd[78]=p4
ln[78]=[:c3,:c2];ld[78]=p5
rn[80]=[:d5,:b4,:a4,:g4,:a4,:fs5];rd[80]=[q,dsq,dsq,dsq,dsq,q]
rn[82]=[:d5,:b4,:g4,:g5];rd[82]=p8
ln[82]=[[:b2,:g3],[:b2,:d3]];ld[82]=p5
rn[85]=[:c5,:e5,:g4];rd[85]=p4
rn[86]=[:d5,:d5,:g5,:b5];rd[86]=p7
rn[88]=[:g5,:d5,:g5,:b5,:g5,:d5];rd[88]=p1
rn[89]=[:f5,:e5,:d5,:g5];rd[89]=p8
ln[89]=[:f3,:e3,:d3,:g3];ld[89]=p8
rn[90]=[:fs5,:a5,:d6,:a5,:fs5,:a5];rd[90]=p1
ln[90]=[[:c3,:a3]]*2;ld[90]=p5
rn[92]=[[:b4,:d5],:g5,:b5,:d5];rd[92]=p7
rn[95]=[:g5,:e5,:c5];rd[95]=p4
rn[97]=rn[163]=[:g5,:fs5,:g5,:d5,:b4,:g4];rd[97]=rd[163]=p1
ln[97]=[[:b2,:d3],[:b2,:g3]];ld[97]=p5
rn[99]=[:fs5,:a5,:d5];rd[99]=p4
ln[99] = ln[102] =[[:c3,:a3]]*2;ld[99] = ld[102]=p5
rn[101]=rn[104]=rn[157]=rn[162]=[:e5,:d5,:e5,:g5,:c6,:g5];\
rd[101]=rd[104]=rd[157]=rd[162]=p1
rn[102]=[:fs5,:d5,:a4,:fs5];rd[102]=p8
rn[103]=[:c5,:e5,:c5,:g4,:e4];rd[103]=p2
rn[106]=[:a4,:d5,:c5,:b4,:a4];rd[106]=p3
rn[108]=[:e5,:g5,:c6];rd[108]=p4
rn[109]=[:d5,:f5,:d5,:f5,:b4,:d5];rd[109]=p1
ln[109]=[[:f3,:a3],[:g3,:d4]];ld[109]=p5
rn[110]=[[:b4,:d5],[:a4,:c5],[:a4,:c5],[:g4,:b4],[:g4,:b4],[:fs4,:a4]];rd[110]=p1
rn[113]=[:f5,:d5,:b4];rd[113]=p4
rn[114]=[[:b4,:d5]]*3;rd[114]=p4
ln[114]=[:g3,:g3,:g3];ld[114]=p4
rn[116]=[:d5,:f5,:a5,:f5,:d5,:b4];rd[116]=p1
rn[117]=[:d5,:a4,:d5,:fs5,:a5,:fs5];rd[117]=p1
rn[118]=[:e5,:a5,:g5,:b5,:fs5,:a5];rd[118]=p1
rn[119]=[:e5,:c5,:g5,:e5,:c6,:g5];rd[119]=p1
rn[120]=[:d6,:a5,:fs5,:d5,:a4];rd[120]=p1
rn[121]=[:g5,:b5,:g5,:d5];rd[121]=p7
rn[122]=[:g5,:fs5,:g5,:b5,:d5];rd[122]=p2
ln[122]=[[:b2,:d3]]*3;ld[122]=p4
rn[125]=[:g5,:e5,:d5,:b4,:g4];rd[125]=p2
ln[125]=ln[132]=ln[175]=[:g3,:g2,:r];ld[125]=ld[132]=ld[175]=p4
rn[126]=[:c5,:g4,:c5,:e5,:g5,[:c5,:e5]];rd[126]=p1
ln[126]=[:e3,:e3,:c3];ld[126]=[c,sq,sq]
rn[128]=[:b4,:d5,:g5];rd[128]=p4
rn[129]=[:a5,:g5,:fs5,:g5,:d5];rd[129]=p2
ln[129]=[[:b2,:d3],[:b2,:d3],[:b2,:g3]];ld[129]=p4
rn[132]=[[:c5,:e5],[:b4,:d5],[:g4,:b4],:g4];rd[132]=p7
rn[133]=[:d5,:g5,:d5,:b4,:d5];rd[133]=p3
rn[134]=[:a4,:e5,[:b4,:d5],[:a4,:c5],[:g4,:b4],[:fs4,:a4]];rd[134]=p1
rn[135]=[:fs5,:fs5,:d5,:a5];rd[135]=p7
ln[135]=[[:c3,:d3]]*3;ld[135]=p4
rn[138]=[[:a4,:d5,:fs5]]+trn(:g5,8,-1);rd[138]=[q]+trd(c,8)
ln[138]=[:d2,:d3,:cs3,:d3,:c3,:d3];ld[138]=p1
rn[139]=[:g5,:b5,:g5,:b5,:d5];rd[139]=p2
rn[140]=[:a4,:a4,:d5,:fs5];rd[140]=p7
ln[140]=[[:c3,:fs3],[:c3,:fs3],[:c3,:a3]];ld[140]=p4
rn[141]=[:d5,:e5,:f5,:d5,:c5,:b4];rd[141]=p1
ln[141]=[[:b2,:g3],:g2];ld[141]=p5
rn[143]=[:g5,:d5,:b4,:g4];rd[143]=p7
rn[145]=[:d5,:f5,:a4,:d5,:b4,:d5];rd[145]=p1
rn[146]=[[:fs4,:d5],[:d5,:fs5],[:fs5,:a5]];rd[146]=p4
rn[147]=[:e5,:c6,:b5,:g5,:a5,:fs5];rd[147]=p1
rn[149]=[:f5,:d5,:a4,:b4];rd[149]=p8
rn[150]=[[:g4,:c5,:e5]]+trn(:f5,8,-1);rd[150]=[q]+trd(c,8)
ln[150]=[:c3,:b2,:c3,:d3,:e3,:fs3];ld[150]=p1
rn[153]=[:d5,:a4,:fs5,:d5,:a5,:fs5];rd[153]=p1
rn[154]=[:d5,:cs5,:d5,:fs5,:a5,:fs5];rd[154]=p1
rn[155]=[:g5,:b5,:g5,:d5,:b4,:g4];rd[155]=p1
rn[156]=[:c5,:g4,:e5,:c5,:g5];rd[156]=p1
rn[158]=[:b4,:d5,:b4,:a4,:g4];rd[158]=p3
rn[159]=[:e5,:g5,:d5,:c5,:b4,:a4];rd[159]=p1
ln[160]=[[:c3,:e3]]*2;ld[160]=p5
rn[161]=[[:fs4,:d5]]*3;rd[161]=p4
rn[164]=[:d5,:g4];rd[164]=p6
ln[164]=[:g3,:fs3,:g3,:d3,:b2,:g2];ld[164]=p1
rn[165]=[:d5,:b4,:g4];rd[165]=p4
rn[166]=[:d5,:b5,:g5,:d5,:b4];rd[166]=p2
rn[167]=[:c5,:c5,:d5,:e5];rd[167]=p7
rn[169]=[:e5,:g5,:d5,:g5,:a4,:fs5];rd[169]=p1
rn[171]=[:b4,:c5,:d5,:e5,:f5,:d5];rd[171]=p1
ln[171]=[[:g2,:g3],[:b2,:g3]];ld[171]=p5
rn[173]=[:f5,:a5,:a4,:b4,:d5];rd[173]=p9
rn[175]=[:e5,:c5,:b4,:d5,:g5];rd[175]=p2
rn[176]=[:a5,:g5,:b5,:g5,:d5,:g5];rd[176]=p1
#second time bars
ln[177]=[:g2,:b3,:g3,:fs3,:e3];ld[177]=p3

define :playminuets do |numtimes,test=0| #test=1 when testing all bars
numtunes.times do |i|
if test==0 then
perform=minuet #for normal use test = 0 setup minuet
else
perform =range(16*i+1,16*i+17) #otherwise set up bar linear list
end
puts perform
performplus=[numtunes]+perform
file=File.open("/tmp/perform.txt","w") #write current bar numbers to a file
file.puts performplus.to_s
file.close
puts"wait for sync via sonic-pi-cli"
sync :go
if test==0 then #if not testing then do repeats: y=2
y=2
else #if testing y=1
y=1
end
y.times do |k|
8.times do |i|
pb(perform[i],k)
end
end
y.times do
8.times do |i|
pb(perform[i+8])
end
end
sleep 3*c
end
end

playminuets(numtunes,testbars) #play the minuets
#setup epiphany kill
file=File.open("/tmp/kill.txt","w")
file.puts "kill"
file.close
sleep 5 #allow for sched ahead
set_sched_ahead_time! 1 #reset
#Ruby control program for Sonic Pi Mozart Dice Minuet by Robin Newman, January 2016
#use in conjunction with SP MozartDiceMinuet.rb
#requires sonic-pi-cli gem to be installed
#requires a Pi2. Will NOT work on a Mac or PC
#requires folder "cards" containing images of the 176 bars
#This script reads the bar numbers for each minuet from the perform.txt file
#and creates an html page of the relevant bar number images
#which it then displays in the Epiphany browser.
#It polls for the existance of the perform.txt file
#reads its data, which consists of the number of minuets to be played,
#followed by the bar numbers for the current minuet.
#Having read the data, it creates a web page and displays it, then sends a cue to Sonic Pi
#to start playing the minuet, before deleting the perform.txt file.
#A "while" loop is used to repeat this process till all the minuets have been played.
#After the loop ends it waits for a file kill.txt to be generated by Sonic Pi,
#then after a pause to allow SP to finish playing the final minuet, because of the set_sched_ahead! 5s value,
#it quits the epiphany browser, and deletes temp files to clean up and exits.

bpix=1200 #browser width in pixels: adjust as desired 1200 for HDMIPi, 1840 for my 1920x1080 monitor, 740 for RP touch screen monitor

#function todelete any leftover files
def cleanup()
system("rm /tmp/kill.txt >/dev/null 2>&1") #ignore errors if file not found
  system("rm /tmp/minuet.html >/dev/null 2>&1")
  system("rm /tmp/go.txt >/dev/null 2>&1")
system("rm /tmp/perform.txt >/dev/null 2>&1")
end

cleanup()

imgwidth=bpix/8 #image width
imgheight=imgwidth*1.58
cnt=0 #variable for current minuet number
flag=false #flag set to true when final minuet is played.
file=File.open("/tmp/go.txt","w") #create cue :go file used to send cue to Sonic Pi
file.puts "cue :go"
file.close

#start main loop
while flag==false do
    while not File.file?("/tmp/perform.txt") #wait for next perform.txt file to be generated by Sonic Pi
      sleep 0.25 #pause before next poll
    end
    sleep 5 #wait to make sure file is fully written and closed by Sonic Pi
    file=File.open("/tmp/perform.txt","r") #read the data
    l=file.gets
    file.close
    l=l[1..-3] #tidy up l
    l=l.split(",").map(&:to_i) #convert to integer array
    numtunes=l[0] #first entry is number of minuets to play (set by Sonic Pi)
    cnt +=1 #bump for next minuet number
    l=l[1..-1] #strip numtunes leaving bar number data list
    file=File.open("/tmp/minuet.html","w") #write html file for displaying selected bars in a table
    file.puts "<html><head><title>minuet "+cnt.to_s+" of "+numtunes.to_s+"</title></head>"
    file.puts"
<h1 align='center'>Sonic Pi plays Mozart Minuet "+cnt.to_s+" of "+numtunes.to_s+"</h1>
"
    file.puts"
<h2 align='center'>Generated from Musikalisches W&uuml;rfelspiel FV 516 f</h2>
"
    file.puts "
<table align='center' width='"+bpix.to_s+"' cellspacing='0' cellpadding='0'>
<tr>"
    8.times do |i|
      file.puts "
<td><img src='file:///home/pi/cards/"+l[i].to_s.rjust(4,"0")+".jpg' style='width:"+imgwidth.to_s+";height:"+imgheight.to_s+"'>
<td>"
    end
    file.puts"</tr>
<tr>"
    8.times do |i|
      file.puts "
<td><img src='file:///home/pi/cards/"+l[i+8].to_s.rjust(4,"0")+".jpg' style='width:"+imgwidth.to_s+";height:"+imgheight.to_s+"'>
<td>"
    end
    file.puts"</tr>
</table>
"
    file.close
   system("epiphany-browser file:///tmp/minuet.html &") #open epiphany-browser with next web page
    if cnt==1 then
        system("xte 'sleep 6' 'key F11'&")
    end
     sleep 1 #allow to open
    system('cat /tmp/go.txt | sonic_pi&') #send cue: go to SP using sonic-pi-cli to start playing
    system("rm /tmp/perform.txt&") #delete perform.txt file ready for next minuet (if any) to be created
    sleep 10 #pause while minuet plays. Makes sure don't poll for next perform.txt too early
    flag=true if (cnt == numtunes)
  end
  while not File.file?("/tmp/kill.txt") #last time, wait for SP to create kill file
    sleep 0.25 #pause between polls
  end
  sleep 7 #pause to make sure SP finshed playing, allowng for 5s sched_ahead_time
  system("killall epiphany-browser&") #shut down browser
  sleep 2
cleanup()

The file spmozart.tar.gz  can be downloaded as described below.
This contains the two files above, plus the cards folder containing the 176 images for the different bars. It also has a README.txt file with installation details.
To install the system on your Pi2 use the following procedure.

open a terminal window on the Pi2

cd ~/
wget http://r.newman.ch/rpi/spmozart.tar.gz
tar zxvf spmozart.tar.gz
mv spmozart/cards ~/
mv spmozart/minuetcontrol.rb ~/

Open a FileManager Window and navigate to spmozart
Right click MozartDiceMinuet.rb and select Text Editor to open it with the leafpad text editor.
Select all (ctrl+A) and copy (ctrl+C) (leave the window open)
Switch to Sonic Pi and select a blank buffer space.
Paste the file data in. (Alt+V)

Switch back to the terminal window, and start the minuetcontrol script by typing
ruby minuetcontrol.rb
(then press return)
You will not see anything happening, as the script is waiting for Sonic Pi to generate a text file.
Switch back to Sonic Pi and click RUN to run the workspace containing the MozartDiceMinuet program.

 

 

 

 

 

Leave a comment