The code requires a translation from the note name (A, B) to the midi note value for each note. The pitch of a note is defined by a structure like:
<pitch> <step>C</step> <alter>1</alter> <octave>3</octave> </pitch>
One approach is to use an if -then -else construct:
declare function local:MidiNote($thispitch as element(pitch) ) 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 := if ($step = "C") then 0 else if ($step = "D") then 2 else if ($step = "E") then 4 else if ($step = "F") then 5 else if ($step = "G") then 7 else if ($step = "A") then 9 else if ($step = "B") then 11 else 0 return 12 * ($octave + 1) + $pitchstep + $alter} ;
declare variable $noteStep := ( <note name="C" step="0"/>, <note name="D" step="2"/>, <note name="E" step="4"/>, <note name="F" step="5"/>, <note name="G" step="7"/>, <note name="A" step="9"/>, <note name="B" step="11"/>);declare function local:MidiNote($thispitch as element(pitch) ) as xs:integer{ let $alter := xs:integer(($thispitch/alter,0)[1]) let $octave := xs:integer($thispitch/octave) let $pitchstep := xs:integer($noteStep[@name = $thispitch/step]/@step) return 12 * ($octave + 1) + $pitchstep + $alter} ;
or an XML element:
declare variable $noteStep := <steps> <note name="C" step="0"/> <note name="D" step="2"/> <note name="E" step="4"/> <note name="F" step="5"/> <note name="G" step="7"/> <note name="A" step="9"/> <note name="B" step="11"/></steps>;declare function local:MidiNote($thispitch as element(pitch) ) as xs:integer{ let $alter := xs:integer(($thispitch/alter,0)[1]) let $octave := xs:integer($thispitch/octave) let $pitchstep := xs:integer($noteStep/note[@name = $thispitch/step]/@step) return 12 * ($octave + 1) + $pitchstep + $alter} ;
We could also store the table in the database since it is constant.
eXist does some optimisation of XPath expressions, but it does not factor out the invariant expression $thispitch/step
in the XPath predicate.
I wrote a test suite to time these various implementations. Typically this shows that factoring the sub-expression reduces the execution time by 25%. However, even with this optimisation, the structure lookup is disappointingly slow. It is about 50% slower than the if/then expression when stored on disk, and 100% slower when in memory.
This aspect of XQuery performance is important if XQuery is to be used for general application development since data structures such as indexed and associative arrays have to be represented as sequences of atomic values or elements. This performance is not really surprising and there may be more performance to be gained by indexing the data base element.