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ü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.