diff --git a/Filters/VLMusicXMLType.reader b/Filters/VLMusicXMLType.reader new file mode 100755 index 0000000..6a10be3 --- /dev/null +++ b/Filters/VLMusicXMLType.reader @@ -0,0 +1,172 @@ +#!/usr/bin/ruby +# +# VLMusicXMLType.reader - Create plist from MusicXML +# + +require File.dirname($0)+'/plistWriter' +require File.dirname($0)+'/vl' +require 'rexml/document' + +XML = REXML::Document.new $stdin +ROOT = XML.root +OUTPUT = {} + +if ROOT.name == "score-timewise" + $stderr.puts "Can't read timewise MusicXML files yet" + exit 1 +end + +def textOrNot(elt) + return elt ? elt.text : "" +end + +OUTPUT['title'] = + textOrNot ROOT.elements['./work/work-title'] +OUTPUT['composer'] = + textOrNot ROOT.elements['./identification/creator[@type="composer"]'] +OUTPUT['lyricist'] = + textOrNot ROOT.elements['./identification/creator[@type="poet"]'] +OUTPUT['groove'] = + textOrNot ROOT.elements['./identification/miscellaneous-field[@name="VocalEasel-groove"]'] + +CHORDS = ROOT.elements['./part[@id="HARM"]'] +MELODY = ROOT.elements['./part[@id="MELO"]'] +PROP = {} +$LASTPROP = nil + +OUTPUT['measures'] = [] +OUTPUT['properties'] = [] + +def updateProp(meas) + if attr = meas.elements['./attributes'] + if div = attr.elements['./divisions'] + PROP['divisions'] = div.text.to_i + end + if key = attr.elements['./key/fifths'] + PROP['key'] = key.text.to_i + end + if mode = attr.elements['./key/mode'] + PROP['mode'] = mode.text == 'minor' ? -1 : 1 + end + if timeNum = attr.elements['./time/beats'] + PROP['timeNum'] = timeNum.text.to_i + end + if timeDenom = attr.elements['./time/beat-type'] + PROP['timeDenom'] = timeDenom.text.to_i + end + end +end + +PITCH = { + 'C' => 0, + 'D' => 2, + 'E' => 4, + 'F' => 5, + 'G' => 7, + 'A' => 9, + 'B' => 11 +} + +def parseNote(note) + n = {} + if note.elements['./rest'] + n['pitch'] = -128 + else + step = note.elements['./pitch/step'] + alter = note.elements['./pitch/alter'] + octave = note.elements['./pitch/octave'] + n['pitch'] = (step ? PITCH[step.text] : 0) + + (alter ? alter.text.to_i : 0) + + (octave ? octave.text.to_i+1 : 0)*12 + end + if dur = note.elements['./duration'] + n['durNum'] = dur.text.to_i + n['durDenom'] = PROP['divisions']*4 + end + if note.elements['./tie'] + n['tied'] = 0 + if note.elements['./tie[@type="start"]'] + n['tied'] |= VL::TiedWithNext + end + if note.elements['./tie[@type="stop"]'] + n['tied'] |= VL::TiedWithPrev + end + end + return n +end + +def makeChord(root, steps, num, denom) + chord = {} + chord['root'] = root + chord['durNum'] = num + chord['durDenom'] = denom + st = steps.sort + pitch = st[0] || -128 + steps = 0 + st.each do |step| + steps |= 1<<(step-pitch) + end + chord['pitch'] = pitch + chord['steps'] = steps + + return chord +end + +CHORDS.elements.each('measure') do |meas| + updateProp meas + chords = [] + root = -128 + steps= [] + num = nil + denom= nil + meas.elements.each('note') do |note| + if !note.elements['./chord'] && num + chords.push makeChord(root, steps, num, denom) + root = -128 + steps= [] + num = nil + denom= nil + end + note = parseNote(note) + num ||= note['durNum'] + denom||= note['durDenom'] + if note['pitch'] < 60 + root = note['pitch'] + else + steps.push note['pitch'] + end + end + chords.push makeChord(root, steps, num, denom) if num + measNo = meas.attributes['number'].to_i-1 + OUTPUT['measures'][measNo] = { + 'measure' => measNo, + 'properties' => 0, + 'chords' => chords, + 'melody' => [] + } +end + +MELODY.elements.each('measure') do |meas| + updateProp meas + if PROP != $LASTPROP + OUTPUT['properties'].push PROP + $LASTPROP = PROP + end + melody = [] + meas.elements.each('note') do |note| + melody.push parseNote(note) + end + measNo = meas.attributes['number'].to_i-1 + OUTPUT['measures'][measNo] ||= { + 'measure'=> measNo, + 'chords' => [] + } + OUTPUT['measures'][measNo]['properties'] = OUTPUT['properties'].length-1 + OUTPUT['measures'][measNo]['melody'] = melody +end + +writePlist($stdout, OUTPUT) + +# Local Variables: +# mode:ruby +# End: diff --git a/Filters/plistWriter.rb b/Filters/plistWriter.rb index f4e464f..5f8f717 100644 --- a/Filters/plistWriter.rb +++ b/Filters/plistWriter.rb @@ -47,7 +47,7 @@ def _encodePlist(object) e.add_element(_encodePlist(elt)) end else - raise "plistWriter can't encode objects of type `#{object.class}'[#{object.class.id}]" + raise "plistWriter can't encode objects of type `#{object.class}'" end return e diff --git a/Sources/VLPListDocument.mm b/Sources/VLPListDocument.mm index 0583cf3..0d4036a 100644 --- a/Sources/VLPListDocument.mm +++ b/Sources/VLPListDocument.mm @@ -219,8 +219,10 @@ void VLPlistVisitor::VisitChord(VLChord & c) NSDictionary * ndict = [ne nextObject]; ) { VLLyricsNote note; - note.fDuration.fNum = [[ndict objectForKey:@"durNum"] intValue]; - note.fDuration.fDenom = [[ndict objectForKey:@"durDenom"] intValue]; + note.fDuration = + VLFraction([[ndict objectForKey:@"durNum"] intValue], + [[ndict objectForKey:@"durDenom"] intValue], + true); note.fPitch = [[ndict objectForKey:@"pitch"] intValue]; note.fTied = 0; @@ -272,8 +274,10 @@ advanceAt: NSDictionary * cdict = [ce nextObject]; ) { VLChord chord; - chord.fDuration.fNum = [[cdict objectForKey:@"durNum"] intValue]; - chord.fDuration.fDenom = [[cdict objectForKey:@"durDenom"] intValue]; + chord.fDuration = + VLFraction([[ndict objectForKey:@"durNum"] intValue], + [[ndict objectForKey:@"durDenom"] intValue], + true); chord.fPitch = [[cdict objectForKey:@"pitch"] intValue]; chord.fRootPitch = [[cdict objectForKey:@"root"] intValue]; chord.fSteps = [[cdict objectForKey:@"steps"] intValue]; @@ -356,8 +360,10 @@ advanceAt: ) { VLProperties prop; - prop.fTime.fNum = [[pdict objectForKey:@"timeNum"] intValue]; - prop.fTime.fDenom = [[pdict objectForKey:@"timeDenom"] intValue]; + prop.fTime = + VLFraction([[ndict objectForKey:@"timeNum"] intValue], + [[ndict objectForKey:@"timeDenom"] intValue], + false); prop.fKey = [[pdict objectForKey:@"key"] intValue]; prop.fMode = [[pdict objectForKey:@"mode"] intValue]; prop.fDivisions = [[pdict objectForKey:@"divisions"] intValue];