Converting MusicXML or midi files for Sonic Pi

Fairly frequently I transcribe existing pieces of music from printed scores to produce code that will run in Sonic Pi. This is quite a tedious and time consuming process, even with practice, and so I was very interested a few days ago to come across some work done by Japanese Sonic Pi user Hiroshi TACHIBANA who asked a question about playing long music files in Sonic Pi on the Sonic Pi Google Forum. I was able to help him out, explaining why there is a limit on buffer file size (set by the use of UDP protocols to communicate between the GUI, the server and the supercollider scsynth that together make up the Sonic Pi application. I also pointed him to the technique of splitting a large program into several buffers, which can be linked together with cue and sync commands. The net result is that he was able to complete a large piece of Japanese music called Spring of the Sea, by Michio Miyagi, which had three instrument parts and lasted over five and a half minutes. It sounded great and you can hear it via this link

What really intrigued me, is that on his (Japanese) website, which I viewed via Google Translate, he showed that he had developed a script which ran with an application called “Processing” which enabled him to convert a music file stored in MusicXML format into Sonic Pi code. He also used the music layout program MuseScore which allows the import of a midi file so that it is displayed in standard musical notation, and, more importantly, so that it can be exported in MusicXML format.

In this first article I will go through how the Sonic Pi file for the first section of the piece is produced. In a second article I will show how I have modified his script slightly, and used it to process a whole range of other pieces, converting midi and MuseScore files to work in Sonic Pi.

I already had MuseScore installed on my Mac (it is a free program with installers for Windows, Mac and Linux), and I proceeded to locate the “Processing” application, which again can be installed freely on Windows, Mac or Linux from here Both programs have good online documentation.

The final two ingredients required, were the script file MusicXMLtoSonicPi_01.pde that Hiroshi had written which you can download here and a suitable midi file (or MuseScore file) on which to experiment.

Initially I tried processing the three musicXML files associated with Hiroshi’s Spring of Sea project, which you can download here:
http://www-b.uec.tmu.ac.jp/shakuhachi/SonicPi/harunoumi_v2c-Shakuhachi.xml
http://www-b.uec.tmu.ac.jp/shakuhachi/SonicPi/harunoumi_v2c-Koto_Right.xml
http://www-b.uec.tmu.ac.jp/shakuhachi/SonicPi/harunoumi_v2c-Koto_Left.xml

To try these out, you don’t actually need MuseScore as they are already single parts in MusicXML format, but you can open each one in MuseScore if you wish.

First you need to set up the script for use with processing. The application sets up a folder called Processing in your user Documents folder when you start it up initially, and this seems a convenient place in which to play the script file MusicXMLtoSonicPi_01.pde You can ignore the other folders already there (examples, libraries, modes,tools) which we will not use. If you start up processing, you get a window containing a blank sketch (the name give to the scripts used by the application, somewhat reminiscent of the Arduino ide system if you have used that). You can close this window, and then from the File menu navigate to the MusicXMLtoSonicPi_01.pde file and open it. Processing will prompt that it wishes to create a folder for it, and you should select OK. You will then see the contents of the script on the screen. The script is in English, but some of the comments are in Japanese. (Comments follow // on a line) I have made some modifications to the file to make it easier to use, but for now we will use the supplied version.

First of all we need to make the MusicXML files available to the script. This is done by selecting Add File… from the Processing Sketch Menu, and selecting each of the three files above in turn. A data folder is created within the newly created folder containing the script and the files are copied there. To make the script process each if these files, you need to set the name of each file in turn in line 64 of the program. The easiest way to do this is to view the sketch folder (from the Sketch Menu) and open the data folder inside it. Then copy the filename of the first file harunoumi_v2c-Koto_Left.xml and paste it into the script at line 64. Then you press the Run Icon at the top of the process window. All being well, textual output will be produced in the black portion of the window beneath the script. (You can drag the dividing line to resize this section). Process1

Select all the text below the ===== Sonic Pi ===== line to the end (last three lines should each say end) by dragging across with the cursor, and thyen use cmd+C (Windows ctrl+C) to copy the text. This can be a bit tricky. I usually find it easier to drag up from the end of the window.

Start up a text editor (I have TextWrangler installed, but text edit will do, provided you set the window to text only {shift+CMD+T} before pasting in the text), and paste the text in. Insert a comment line #koto left hand at the beginning to remind you which file it is and save it as kotoLH.txt. Repeat for the other two files harunoumi_v2c-Koto_Right.xml and harunoumi_v2c-Shakuhachi.xml inserting comments #koto right hand and #shakuhachi respectively as the first line of each file and saving then as kotoRH.txt and shakuhachi.txt. You now have three Sonic Pi files, one for each part. Unfortunately there is a problem. Because of the mechanism (UDP packets) that Sonic Pi uses to communicate between the GUI, the server and the supercollider scsynth file that make up the three parts of the application, there is a limitation on the file size that can be handled by one Sonic Pi buffer, and in this case it is less than the size of each of these files, so they cannot be run directly. ***For shorter pieces this may not be a problem. In this case, the solution is to split the files up into smaller sections, and put them into a total of four files each loaded into a separate buffer which can be linked to play one after the other using Sonic Pi’s cue and sync commands which can work between different buffers.

***Stop Press. Recently a command run_file <filename with path> has been added to the development version of Sonic Pi (2.11dev) This enables long files to be played directly form a text or sonic file .txt or .rb file, rather than loaded into one of Sonic Pi’s 10 buffers. This bypasses the udp mechanism for sending the file data to the server, and is a useful addition, although you can’t edit the file directly in Sonic Pi. In particular, we can just append the three txt files created above and then play them in Sonic Pi using this command. Note you will still have to move the in_thread do lines as detailed below.

(Now follows the alternative technique which works in the current Sonic Pi version 2.10)
It is beyond the scope of this article to give full details of how this is done, but we can see now the first section of each if these files can be combined to for the first of the four files, and you can download Hiroshi’s finished files to get full details of the linking.

To form the first file do the following:
Create a new empty text file.
Copy the first section of the Shakuhachi.txt file down to and including the line b[7]=[3.0,1.0] and paste it into the new file. Add to the last few lines from the end of the file, from the line starting  c=[60,80,85,40,90,80,75…….. to the end of the file, and paste them to the end of the new file.

If you load this file into Sonic Pi, it will play, but note there are 10 bars rest before you will hear anything! you can repeat the process using the kotoLH and kotoRh files, again selecting everything down to and including the line starting b[7]=[…. and then adding on the end everything from the line starting c=[60,80,85,40,90,80,75…….. to the end of this new file. You will now have the first section of each of the three part files in one file. A couple of further tweaks and you can play this file.

Firstly you will notice that the array variables holding the note and duration information have the same names for each part. To eliminate confusion, they can be made local to each part, by simply moving the line in_thread do immediately after the lines starting c=[60,80,85,40,90,80,75 to the beginning of each part section, i.e. immediately after the #comment containing the name of each part. Secondly, you can add a use_synth command for each part, to select the appropriate instrument to use. For the Shakuhachi part this will be use_synth :dsaw for each of the koto parts it will be use_synth :pluck in each case place this line just after the line starting c=[60,80,85,40,90,80,75……..

Saved the completed file and then load it into Sonic Pi, and it should play the complete first section of the Spring of Sea. In fact the file should look more or less identical to Hiroshi’s file Harunoumi_1.rb There is one final change to make. To sustain the notes for the Shakuhachi part and reduce their voule a bit to balance the koto parts change the line play a[i][j] in the Shakuhachi thread to read play a[i][j],sustain: b[i][j]*0.6,amp: 0.3 It will work without doing this, but the notes will not last long enough, and will be too loud.

The finished file should look like this:

#shakuhachi
in_thread do
  a=[]
  b=[]
  a[0]=[:r,:r,:r,:r,:D5,:E5,:G5,:E5,:D5,:B4,:A4,:B4,:E4,:D5,:E5,:G5,:A5,:B5,:A5,:B5,:E5,:D5,:D5,:B4,:A4,:B4,:E4,:E5,:E5,:G5,:E5,:D5,:E5]
  b[0]=[4.0,2.0,1.0,0.5,0.5,2.0,0.75,0.125,0.125,0.5,0.5,4.0,3.0,0.25,0.25,0.25,0.25,1.75,0.25,3.0,0.75,0.125,0.125,1.75,0.25,3.0,0.5,0.5,3.0,0.5,0.25,0.25,4.0]
  a[1]=[:F4,:E4,:B5,:A5,:G5,:A5,:B5,:A5,:G5,:E5,:r,:r,:F6,:E6,:r,:r,:F5,:E5,:r,:r,:F4,:E4,:r,:r,:F6,:E6,:r,:r]
  b[1]=[4.0,4.0,2.0,2.25,0.25,0.25,0.25,0.75,0.25,0.5,0.5,1.0,0.0625,0.4375,0.5,1.0,0.0625,0.4375,0.5,1.0,0.0625,0.4375,0.5,1.0,0.0625,0.4375,0.5,1.0]
  a[2]=[:F6,:E6,:E6,:E5,:E6,:E6,:F6,:E6,:E6,:E5,:E6,:E6,:F6,:E6,:B5,:E5,:A5,:B5]
  b[2]=[0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334,0.33333334]
  a[3]=[:E6]
  b[3]=[2.0]
  a[4]=[:A4,:G4,:G4,:E4,:r,:E5,:G5,:A5,:G5,:A5,:G5,:G5,:E5,:r,:G5,:A5,:B5,:A5,:B5,:A5,:A5,:F5,:r,:E6,:C6,:B5,:A5,:B5,:F5,:F5,:E5,:r,:D5,:E5,:G5,:A5]
  b[4]=[1.5,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,1.5,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,1.5,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,1.5,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25]
  a[5]=[:B5,:A5,:B5,:E5,:D5,:D5,:B4,:A4]
  b[5]=[1.75,0.25,3.0,0.75,0.125,0.125,1.5,0.5]
  a[6]=[:B4,:E4,:A4,:B4]
  b[6]=[3.0,0.5,0.25,0.25]
  a[7]=[:E6,:r]
  b[7]=[3.0,1.0]
  c=[60,80,85,40,90,80,75,60,110,110,100,90,70,60,70,80,60,40,25,60,80,85,70,85,60,85,60,90,80,65,50,80]
  use_synth :dsaw
  
  for i in 0..a.length-1
    use_bpm c[i]
    for j in 0..a[i].length-1
      play a[i][j],sustain: b[i][j]*0.6,amp: 0.3
      sleep b[i][j]
    end
  end
end

#koto left hand
in_thread do
  a=[]
  b=[]
  a[0]=[:r,:r,:r,:r,:r,:r,:r,:r,:r,:r]
  b[0]=[4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0]
  a[1]=[:r,:r,:r,:r,:r,:F4,:E4,:r,[:E4,:B4,:E5],:r,:F4,:E4,:r,[:E4,:B4,:E5]]
  b[1]=[4.0,4.0,4.0,4.0,1.0,0.0625,0.9375,1.0,1.0,1.0,0.0625,0.9375,1.0,1.0]
  a[2]=[:r,:r]
  b[2]=[4.0,2.0]
  a[3]=[:r]
  b[3]=[2.0]
  a[4]=[:E3,:A3,:E3,:A3,:E3,:A3,:E3,:A3,:B2,:F3,:B2,:F3,:E3,:B3,:E3,:B3]
  b[4]=[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]
  a[5]=[[:E3,:A3],[:B2,:B3],[:E3,:A3],[:B2,:B3]]
  b[5]=[2.0,2.0,2.0,2.0]
  a[6]=[[:E3,:A3],[:B2,:B3]]
  b[6]=[2.0,2.0]
  a[7]=[[:E3,:A3],[:B2,:B3]]
  b[7]=[2.0,2.0]
  c=[60,80,85,40,90,80,75,60,110,110,100,90,70,60,70,80,60,40,25,60,80,85,70,85,60,85,60,90,80,65,50,80]
  use_synth :pluck
  for i in 0..a.length-1
    use_bpm c[i]
    for j in 0..a[i].length-1
      play a[i][j]
      sleep b[i][j]
    end
  end
end

#koto right hand
in_thread do
  a=[]
  b=[]
  a[0]=[:E3,:B3,:D4,:E4,:A4,:B4,:A4,:E4,:E3,:B3,:D4,:E4,:A4,:B4,:D5,:E5,:E3,:B3,:D4,:E4,:A4,:B4,:A4,:E4,:E3,:B3,:D4,:E4,:A4,:B4,:D5,:E5,:E3,:B3,:D4,:B4,:D5,:B4,:A4,:E5,:E3,:B3,:D4,:E5,:B4,:A4,:F4,:E4,:E3,:B3,:D4,:E4,:F4,:A4,:B4,:E5,:E3,:B3,:D4,:E5,:B4,:A4,:F4,:E4,:E3,:B3,:D4,:E4,:A4,:B4,:A4,:E4,:E3,:B3,:D4,:E4,:A4,:B4,:D5,:E5]
  b[0]=[0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0,0.5,0.25,0.25,0.25,0.25,0.25,0.25,2.0]
  a[1]=[:r,[:D4,:F4],[:D4,:F4],[:B3,:E4],[:D4,:F4],[:D4,:F4],[:B3,:E4],[:D4,:F4],[:D4,:F4],:A4,:E4,:r,[:D3,:F3],[:D3,:F3],[:B2,:E3],[:D3,:F3],[:D3,:F3],[:B2,:E3],[:D3,:F3],[:D3,:F3],:A3,:E3,:r,[:D4,:F4],[:D4,:F4],[:B3,:E4],[:D4,:F4],[:D4,:F4],[:B3,:E4],[:D4,:F4],[:D4,:F4],:A4,:E4,:r,[:D3,:F3],[:D3,:F3],[:B2,:E3],[:D3,:F3],[:D3,:F3],[:B2,:E3],[:D3,:F3],[:D3,:F3],:A3,:E3,[:B2,:D3,:E3],[:B2,:D3,:E3],[:B2,:D3,:E3],[:B2,:D3,:E3]]
  b[1]=[0.5,0.25,0.25,0.5,0.25,0.25,0.5,0.25,0.25,0.5,0.5,0.5,0.25,0.25,0.5,0.25,0.25,0.5,0.25,0.25,0.5,0.5,0.5,0.25,0.25,0.5,0.25,0.25,0.5,0.25,0.25,0.5,0.5,0.5,0.25,0.25,0.5,0.25,0.25,0.5,0.25,0.25,0.5,0.5,2.0,2.0,2.0,2.0]
  a[2]=[:r,:r]
  b[2]=[4.0,2.0]
  a[3]=[:r]
  b[3]=[2.0]
  a[4]=[:r,:D5,:B4,:A4,:F4,:E4,:r,:E5,:D5,:B4,:F4,:E4,:r,:D5,:B4,:A4,:F4,:E4,:r,:E5,:D5,:B4,:F4,:E4,:r,:D5,:B4,:A4,:F4,:D4,:r,:E5,:D5,:B4,:A4,:F4,:r,:D5,:B4,:A4,:F4,:E4,:r,:E5,:D5,:B4,:F4,:E4]
  b[4]=[0.25,0.25,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,0.5,0.5,0.25,0.25,0.25,0.25,0.5,0.5]
  a[5]=[[:A4,:B4,:E5],[:A4,:B4,:E5],[:A4,:B4,:E5],[:A4,:B4,:E5]]
  b[5]=[2.0,2.0,2.0,2.0]
  a[6]=[[:A4,:B4,:E5],[:A4,:B4,:E5]]
  b[6]=[2.0,2.0]
  a[7]=[[:A4,:B4,:E5],[:E4,:B4,:E5]]
  b[7]=[2.0,2.0]
  c=[60,80,85,40,90,80,75,60,110,110,100,90,70,60,70,80,60,40,25,60,80,85,70,85,60,85,60,90,80,65,50,80]
  use_synth :pluck
  for i in 0..a.length-1
    use_bpm c[i]
    for j in 0..a[i].length-1
      play a[i][j]
      sleep b[i][j]
    end
  end
end

Congratulations. You have created the first part fo the files that make up the complete piece. Have a look at Hiroshi’s completed files,
http://www-b.uec.tmu.ac.jp/shakuhachi/SonicPi/Harunoumi_1.rb
http://www-b.uec.tmu.ac.jp/shakuhachi/SonicPi/Harunoumi_2a.rb
http://www-b.uec.tmu.ac.jp/shakuhachi/SonicPi/Harunoumi_2b.rb
http://www-b.uec.tmu.ac.jp/shakuhachi/SonicPi/Harunoumi_3.rb
and you should understand how the remainder of the three part files we have produced are included in them. Also look out for the addition of cue and sync commands which are used to link the files together when they are placed into 4 separate buffers in Sonic Pi. (the cue command still has to be added to our versio0n of the first file shown above. One other change is that strictly not all of the arrays c=[60,80,40,….are needed but only the first 8 numbers.The remainder are used in subsequent files.

That completes the first article. In the second, I will discuss the script further, make some modifications to it, and use it to process some shorter files, which will not need to be “cut up” because of the size limitations in Sonic Pi.
Part 2 is here