Search

Learning Arduino - D4 - MusicXML to Sketches with XQuery

MusicXML is an XML format for music representation developed by Recordare.  There is a tutorial and an increasing number of scores available in this format in the public domain, including Wikifonia, the Guttenberg Project and a site which contains a large number of Carols:

http://www.hymnsandcarolsofchristmas.com/Hymns_and_Carols/XML

and here is the music for that familiar Christmas Carol.

http://www.hymnsandcarolsofchristmas.com/Hymns_and_Carols/XML/Good_King_Wenceslas2.xml

One architecture would be to do the transformation from MusicXML on the Arduino and I found some work on an XML parser.  However some of the files, paricularly those on Wikifonia are compressed with zip and will need expanding.

Instead I chose to process the XML in XQuery in a script which generates a fragment of Arduino code which can be edited into a skeleton sketch.  After some indecision, I reverted to two parallel arrays of ints, one for the midi codes, one for the durations plus an int for the tempo and another for the divisions per quarter note.  This leaves  the Arduino to convert to frequency and length of note. The Xquery script provides a web service which can be used to convert a part in a MusicXML file to Arduino.

Arduino skeleton sketch



/*
 Midi tune 
 
 Plays a sequnce of midi notes.
  
 created 30 Nov 2010
 by Chris Wallace
 
*/

// tune data

// end of tune


// First note is midi 21, so frequency of midi note x is  midi[x - 21]

// midi note frequencies - 0 is midi 21
int midifrequencies[] = {
  28, 29, 31, 33, 35, 37, 39, 41, 44, 46
    , 49, 52, 55, 58, 62, 65, 69, 73, 78, 82
    , 87, 93, 98, 104, 110, 117, 124, 131, 139, 147
    , 156, 165, 175, 185, 196, 208, 220, 233, 247, 262
    , 277, 294, 311, 330, 349, 370, 392, 415, 440, 466
    , 494, 523, 554, 587, 622, 659, 699, 740, 784, 831
    , 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480
    , 1568, 1661, 1760, 1865, 1976, 2093, 2218, 2349, 2489, 2637
    , 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186} 
;

int speaker = 8;

void setup() {
  pinMode(speaker, OUTPUT);  
  Serial.begin(9600);
}

void loop() {
  playtune();
  delay(2000); 
}

void playtune() {
  // iterate over the notes of the melody:
  for (int thisNote = 0; thisNote < sizeof(note_midi)/sizeof(int); thisNote++) {
    playnote(note_midi[thisNote], note_duration[thisNote]);
  }
  delay (3000); 
}

void playnote(int midi, int duration) { 
  //compute the note length in milisecs
  int millisecs = (double(duration) / double(divisions))  * 60.0 / double(tempo) * 1000;
  Serial.println(millisecs);
  // play the note if not a rest
  if (midi > 0) {
     int freq = midifrequencies[midi - 21];
     tone(speaker, freq, millisecs);
  };
  //delay while the note plays and a bit longer
  delay (millisecs * 1.05);
  // turn the note off
  if (midi > 0) noTone(speaker);
}


XQuery Transformation

I use XQuery to perform the translation of a part in the XML score to Arduino code. Any server-side language would do but XQuery is my tool of choice, given the easy of processing XML structures. This code is described in an article in the XQuery wikibook.

For example, the "Good King Wenselsas" Carol, whose MusicXMl is linked above, can be converted to :



int tempo = 120;
int divisions = 4;

int note_midi[] = {
 67, 67, 67, 69, 67, 67, 62, 64, 62, 
64, 66, 67, 67, 67, 67, 67, 69, 67, 67, 
62, 64, 62, 64, 66, 67, 67, 74, 72, 71, 
69, 71, 69, 67, 64, 62, 64, 66, 67, 67, 
62, 62, 64, 66, 67, 67, 69, 74, 72, 71, 
69, 67, 72, 67};

int note_duration[] = {
 4, 4, 4, 4, 4, 4, 8, 4, 4, 
4, 4, 8, 8, 4, 4, 4, 4, 4, 4, 
8, 4, 4, 4, 4, 8, 8, 4, 4, 4, 
4, 4, 4, 8, 4, 4, 4, 4, 8, 8, 
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 
4, 8, 8, 16};


which can then be pasted into the skeleton sketch, compiled and down-loaded to the Arduino. Maybe this is not the best way? Perhaps I should #include the skeleton code in the Arduino IDE, or in XQuery?

Web Service

The XQuery script can be used to generate this Arduino fragment from any MusicXML document on the web. Care needs to be taken to choose a monophonic part.

http://www.cems.uwe.ac.uk/xmlwiki/Music/mxl2arduinoForm.xq

or

http://184.73.216.20/exist/rest/db/apps/music/mxl2arduinoForm.xq

endpoint

  http://www.cems.uwe.ac.uk/xmlwiki/Music/mxl2arduinoForm.xq

parameters

 uri - URI of MusicXML file , either plain XML (default) or a zip archive (format=zip)

 part - the id of a part in the score - default P1

 format - XML (default) or ZIP [should be able to figure this out in the script]

 tempo - by default 120 but this may be a bit slow.

e.g.

Review

I tried to use larger pieces, such as the First Violin part of a Beethoven Sonata from Project Guttenberg but there were too many notes.  I need to understand how storage works on the Arduino.

I found it tricky to compute the actual note length and kept getting interger overflow problems.

I thought the tempo was slow and finally realised that the tempo was slowed by a factor or 1.3 by the added delay which was in the original example - cutting that to 1.05 improved the tempo matching, but the sound is very square still.  Bristol Hackspace helped here, in the form of Richard Sewell who pointed me to impressive work done by himself and other members of the group on the Pisan-O-Matic  where a more flexible to note generation is used.