diff --git a/Filters/VLLilypondType.reader b/Filters/VLLilypondType.reader index 117ce29..107346f 100755 --- a/Filters/VLLilypondType.reader +++ b/Filters/VLLilypondType.reader @@ -16,7 +16,7 @@ OUTPUT = {'measures' => []} tokens = [] $stdin.each do |line| line.chomp!.sub!(/%.*/, "") - line.scan(/\G\s*(\{|\}|\(|\)|\||=|~|<<|>>|#'|#\(|##t|##f|\\\w+|\".*?\"|[-+\w\d.',:]+|.)/) do |token| + line.scan(%r$\G\s*(\{|\}|\(|\)|\||=|~|<<|>>|#'|#\(|##t|##f|\\\w+|\".*?\"|\w[-+\w\d.',:*/]+|.)$) do |token| tokens.push(token[0]) end end @@ -51,7 +51,7 @@ PITCH = { } def lyPitch(pitch, base=-1) - if !pitch || pitch =~ /[rs]/ + if !pitch || pitch =~ /^[rs]/ return VL::NoPitch end p = PITCH[pitch[0]] || 0 @@ -63,7 +63,7 @@ def lyPitch(pitch, base=-1) end $RELPITCH = p else - p += 60 + p += 48 end pitch.scan(/'/) {|c| p += 12} pitch.scan(/,/) {|c| p -= 12} @@ -78,12 +78,18 @@ def lyPitch(pitch, base=-1) end def lyDur(dur) - dur =~ /^(\d+)(?:\*(\d+).(\d+))?/ + dur =~ /^(\d+)(\.*)(?:\*(\d+).(\d+))?/ num = 1 den = $1.to_i - if $2 - num *= $2.to_i - den *= $3.to_i + if $2.length > 0 + (0...$2.length).each do |x| + den = 2*den + num = 2*num+1 + end + end + if $3 + num *= $3.to_i + den *= $4.to_i end return [num,den] end @@ -147,20 +153,20 @@ MINORKEY = [ ]; def lySteps(steps) - steps =~ /^(|m|maj|dim7?|aug|sus[42]?)/ + steps =~ /^(maj|dim7?|aug|sus[42]?|m|)/ s = STEPS[$1] steps = $' - if !($1 =~ /\d/) && steps =~ /^(7|9|11|13)/ - if !(s & VL::Maj7th) + if !($1 =~ /\d$/) && steps =~ /^(7|9|11|13)/ + if (s & VL::Maj7th) == 0 s |= VL::Min7th end case $1 when '9' s |= VL::Maj9th when '11' - s |= VL::Maj9th+VL::Maj11th + s |= VL::Maj9th+VL::Eleventh when '13' - s |= VL::Maj9th+VL::Maj11th+VL::Maj13th + s |= VL::Maj9th+VL::Eleventh+VL::Maj13th end steps = $' end @@ -213,7 +219,7 @@ while tokens.length > 0 [a-g](?:[ei]?s)? # g, ges, fis, es, as ) (\d+ # 1, 2, 4, 8, 16 ... - (?:\*\d+/\d+)? # *3/4 + \.*(?:\*\d+/\d+)? # ., *3/4 )? (?:\:([-+^:.a-z\d]+))? # :maj9.7-^2 (?:/\+( # /+ @@ -242,11 +248,11 @@ while tokens.length > 0 [a-g](?:[ei]?s)? # g, ges, fis, es, as [',]* # g''' ) - (\d+ # 1, 2, 4, 8, 16 ... + (\d+\.* # 1, 2, 4, 8, 16 ... (?:\*\d+/\d+)? # *3/4 )? $}x - pitch = lyPitch($1, 60) + pitch = lyPitch($1) dur = $2 || lastDur lastDur = dur d = lyDur(dur) @@ -295,8 +301,8 @@ while tokens.length > 0 level = nestLevel when '\time' if tokens[0] =~ %r{(\d+)/(\d+)} - timeNum = $1 - timeDenom = $2 + timeNum = $1.to_i + timeDenom = $2.to_i tokens[0..0] = nil end if block != 'voice' @@ -315,6 +321,63 @@ while tokens.length > 0 end end +measureLen = VL::Fract.new(timeNum, timeDenom) + +# +# Make measures +# +measCount= -1 +measures = [] + +while notes.size > 0 || chords.size > 0 + measCount += 1 + meas = {} + meas['measure'] = measCount + meas['properties'] = 0 + if chords.size > 0 + mchords = [] + len = VL::Fract.new(0, 1) + while len < measureLen && chords.size > 0 + chord = chords.shift + chordLen = VL::Fract.new(chord['durNum'], chord['durDenom']) + if len+chordLen > measureLen + remLen = len+chordLen-measureLen + chordLen -= remLen + remChord = chord.dup + remChord['durNum'] = remLen.num + remChord['durDenom'] = remLen.denom + chords.unshift(remChord) + end + mchords.push(chord) + len += chordLen + end + meas['chords'] = mchords + end + if notes.size > 0 + mnotes = [] + len = VL::Fract.new(0, 1) + while len < measureLen && notes.size > 0 + note = notes.shift + noteLen = VL::Fract.new(note['durNum'], note['durDenom']) + if len+noteLen > measureLen + remLen = len+noteLen-measureLen + noteLen -= remLen + remNote = note.dup + remNote['durNum'] = remLen.num + remNote['durDenom'] = remLen.denom + remNote['tied'] = (remNote['tied'] || 0) | VL::TiedWithPrev + note['tied'] = (note['tied'] || 0) | VL::TiedWithNext + notes.unshift(remNote) + end + mnotes.push(note) + len += noteLen + end + meas['melody'] = mnotes + end + measures.push(meas) +end + +OUTPUT['measures'] = measures OUTPUT['properties'] = [{ 'divisions' => divisions, 'key' => key, diff --git a/Filters/vl.rb b/Filters/vl.rb index d03b6c8..933d045 100644 --- a/Filters/vl.rb +++ b/Filters/vl.rb @@ -79,4 +79,53 @@ class VL Sus4 = VL::Unison+VL::Fourth+VL::Fifth Sus2 = VL::Unison+VL::Maj2nd+VL::Fifth end + + class Fract + include Comparable + + def initialize(num, denom) + @num = num + @denom = denom + + normalize() + end + + def num + return @num + end + + def denom + return @denom + end + + def _gcd(x, y) + while y != 0 + x,y = [y, x % y] + end + + return x + end + + def normalize + g = _gcd(@num, @denom) + @num /= g + @denom /= g + end + + def -() + return Fract.new(-@num, @denom) + end + + def +(other) + return Fract.new(@num*other.denom+other.num*@denom, @denom*other.denom) + end + + def -(other) + return Fract.new(@num*other.denom-other.num*@denom, @denom*other.denom) + end + + def <=>(other) + return @num*other.denom <=> other.num*@denom + end + end end diff --git a/Sources/VLDocument.mm b/Sources/VLDocument.mm index ea1e068..3514ee9 100644 --- a/Sources/VLDocument.mm +++ b/Sources/VLDocument.mm @@ -14,6 +14,7 @@ #import "VLMMADocument.h" #import "VLMIDIDocument.h" #import "VLPDFDocument.h" +#import "VLPListDocument.h" #import "VLPDFWindow.h" #import "VLLogWindow.h" #import "VLSheetWindow.h" @@ -352,6 +353,12 @@ if ([typeName isEqual:@"VLNativeType"] || [typeName isEqual:@"VLMusicXMLType"]) { return [self readFromXMLFileWrapper:wrapper error:outError]; + } else if ([typeName isEqual:@"VLLilypondType"]) { + if ([self readFromFileWrapper:wrapper withFilter:typeName error:outError]) { + [self setFileURL:nil]; + return YES; + } else + return NO; } else { if (outError) *outError = [NSError errorWithDomain:NSCocoaErrorDomain diff --git a/Sources/VLPListDocument.mm b/Sources/VLPListDocument.mm index 60dd325..1c03701 100644 --- a/Sources/VLPListDocument.mm +++ b/Sources/VLPListDocument.mm @@ -432,7 +432,7 @@ advanceAt: [NSException raise:NSInvalidArgumentException format:@"Filter %@: %@", filterName, errStr]; } - + return output; } @@ -469,9 +469,9 @@ advanceAt: mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:&errString]; if (!outPlist) - return NO; - else - return [self readFromPlist:outPlist error:outError]; + [NSException raise:NSInvalidArgumentException + format:@"Plist %@: %@", filterName, errString]; + return [self readFromPlist:outPlist error:outError]; } @end diff --git a/VocalEasel.xcodeproj/project.pbxproj b/VocalEasel.xcodeproj/project.pbxproj index a935e63..a1a64cb 100644 --- a/VocalEasel.xcodeproj/project.pbxproj +++ b/VocalEasel.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 95049CF30BDC32EB0015EE6E /* installLilypond.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 95049CF20BDC32CD0015EE6E /* installLilypond.scpt */; }; 95049D020BDC436A0015EE6E /* installPython.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 95049D010BDC43510015EE6E /* installPython.scpt */; }; 950795E10B4A34D9008911A6 /* stop.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 950795E00B4A34D9008911A6 /* stop.tiff */; }; + 95140E3C0C944F7F00966576 /* VLLilypondType.reader in Copy Filters */ = {isa = PBXBuildFile; fileRef = 95140E3B0C944F7F00966576 /* VLLilypondType.reader */; }; 9524DAFB0BE569C50002AC03 /* Help in Resources */ = {isa = PBXBuildFile; fileRef = 9524DAF70BE569C50002AC03 /* Help */; }; 9524DB390BE5CA070002AC03 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9524DB380BE5CA070002AC03 /* Carbon.framework */; }; 952CBB9C095FD1CA00434E43 /* VLSoundOut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952CBB9A095FD1CA00434E43 /* VLSoundOut.cpp */; }; @@ -136,6 +137,7 @@ dstPath = Filters; dstSubfolderSpec = 7; files = ( + 95140E3C0C944F7F00966576 /* VLLilypondType.reader in Copy Filters */, 95795CE60C88B25D00E4A21F /* vl.rb in Copy Filters */, 95795CE70C88B25D00E4A21F /* VLMusicXMLType.reader in Copy Filters */, 95EF921A0C786CB90093E5F4 /* plistReader.rb in Copy Filters */, @@ -174,6 +176,7 @@ 95049CF20BDC32CD0015EE6E /* installLilypond.scpt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.scpt; name = installLilypond.scpt; path = Resources/installLilypond.scpt; sourceTree = ""; }; 95049D010BDC43510015EE6E /* installPython.scpt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.scpt; name = installPython.scpt; path = Resources/installPython.scpt; sourceTree = ""; }; 950795E00B4A34D9008911A6 /* stop.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = stop.tiff; path = Resources/stop.tiff; sourceTree = ""; }; + 95140E3B0C944F7F00966576 /* VLLilypondType.reader */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = VLLilypondType.reader; sourceTree = ""; }; 9524DAF70BE569C50002AC03 /* Help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Help; path = Resources/Help; sourceTree = ""; }; 9524DB380BE5CA070002AC03 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; 952CBB98095FD19D00434E43 /* TVLSoundOut */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TVLSoundOut; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -539,6 +542,7 @@ 95795CE40C88B25D00E4A21F /* vl.rb */, 95795CE50C88B25D00E4A21F /* VLMusicXMLType.reader */, 95EF92270C78E9390093E5F4 /* VLMusicXMLType.writer */, + 95140E3B0C944F7F00966576 /* VLLilypondType.reader */, 95EF92120C786B2C0093E5F4 /* plistReader.rb */, 95EF92130C786B2C0093E5F4 /* plistWriter.rb */, );