XQuery/MusicXML to Arduino
Appearance
< XQuery
Motivation
[edit | edit source]You want to play music available in MusicXML format on an Arduino.
Approach
[edit | edit source]Fetch the Music XML file (either plain XML or compressed) and transform one monophic part to code to be included in an Arduino sketch.
Script
[edit | edit source](: ~
: convert a monotonic part in a MusicXML score to an Arduino code fragment suitable to include in a sketch
:
:@param uri - the uri of the MusicXML file
:@param part - the id of the part to be converted to midi notes
:@return text containing Arduino statements to :
: set the tempo,
: define the array of midi notes a
: define a parallel array of note durations in beats
:@author Chris Wallace
:)
(: offsets of the letters ABCDEFG from C :)
declare namespace fw = "http://www.cems.uwe.ac.uk/xmlwiki/fw";
declare variable $fw:step2offset := (9,11,0,2,4,5,7);
declare function fw:filter($path as xs:string, $type as xs:string, $param as item()*) as xs:boolean {
(: pass all :)
true()
};
declare function fw:process($path as xs:string,$type as xs:string, $data as item()? , $param as item()*) {
(: return the XML :)
$data
};
declare function fw:unzip($uri) {
let $zip := httpclient:get(xs:anyURI($uri), true(), ())/httpclient:body/text()
let $filter := util:function(QName("http://www.cems.uwe.ac.uk/xmlwiki/fw","fw:filter"),3)
let $process := util:function(QName("http://www.cems.uwe.ac.uk/xmlwiki/fw","fw:process"),4)
let $xml := compression:unzip($zip,$filter,(),$process,())
return $xml
};
declare function fw:MidiNote($thispitch as element() ) as xs:integer {
let $step := $thispitch/step
let $alter :=
if (empty($thispitch/alter)) then 0
else xs:integer($thispitch/alter)
let $octave := xs:integer($thispitch/octave)
let $pitchstep := $fw:step2offset [ string-to-codepoints($step) - 64]
return 12 * ($octave + 1) + $pitchstep + $alter
} ;
declare function fw:mxl-to-midi ($part) {
for $note in $part//note
return
element note {
attribute midi { if ($note/rest) then 0 else fw:MidiNote($note/pitch)},
attribute duration { ($note/duration, 1) [1] }
}
};
declare function fw:notes-to-arduino ($notes as element(note)*) as element(code) {
(: create the two int arrays for inclusion in an Arduino sketch :)
<code>
int note_midi[] = {{ {
string-join(
for $midi at $i in $notes/@midi
return
concat(if ($i mod 10 eq 0) then " " else (),$midi)
,", ")
}
}};
int note_duration[] = {{ {
string-join(
for $duration at $i in $notes/@duration
return
concat(if ($i mod 10 eq 0) then " " else (),$duration)
,", ")
}
}};
</code>
};
declare option exist:serialize "method=text media-type=text/text";
let $uri := request:get-parameter("uri",())
let $part := request:get-parameter("part","P1")
let $format := request:get-parameter("format","xml")
let $doc :=
if ($format = "xml")
then doc ($uri)
else if ($format = "zip")
then fw:unzip($uri)
else ()
(: get the requested part :)
let $part := $doc//part[@id = $part]
(: use the data in the first measure to set the temp :)
let $measure := $part/measure[1]
let $tempo := (xs:integer($measure/sound/@tempo), 100)[1]
(: convert the notes into an internal XML format :)
let $notes := fw:mxl-to-midi($part)
return
(: generate the sketch fragmemt:)
<sketch>
int tempo = {$tempo};
{fw:notes-to-arduino($notes) }
</sketch>