17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
# File 'lib/rubysketch/mml.rb', line 17
def compile!(str)
scanner = StringScanner.new expandLoops__ str.gsub(/[;%].*(?:\n|$)/, '')
seq = Beeps::Sequencer.new
note = Note__.new
pending = nil
prevOsc = nil
scanner.skip(/\s*/)
until scanner.eos?
case
when scanner.scan(/T\s*(\d+)/i)
note.bpm = scanner[1].to_i
when scanner.scan(/O\s*(\d+)/i)
note.octave = scanner[1].to_i
when scanner.scan(/([<>])/)
note.octave +=
case scanner[1]
when '<' then -1
when '>' then +1
else 0
end
when scanner.scan(/@\s*(\d+)/)
note.tone = scanner[1].to_i
when scanner.scan(/L\s*(\d+)/i)
note.length = scanner[1].to_i
when scanner.scan(/V\s*(\d+)/i)
note.velocity = scanner[1].to_i
when scanner.scan(/Q\s*(\d+)/i)
note.quantize = scanner[1].to_i
when scanner.scan(/K\s*([+-]?\d+)/i)
note.transpose = scanner[1].to_i
when scanner.scan(/Y\s*([+-]?\d+)/i)
note.detune = scanner[1].to_i
when scanner.scan(/\&\s*(\d+)?\s*(\.+)?/)
len, dots = [1, 2].map {scanner[_1]}
if len || dots
pending.seconds += seconds__ note, len&.to_i, dots&.size if pending
else
note.tie = true
end
when scanner.scan(/\^\s*(\d+)?\s*(\.+)?/)
len, dots = [1, 2].map {scanner[_1]}
pending.seconds += seconds__ note, len&.to_i, dots&.size if pending
when scanner.scan(/_/)
note.portamento = true
when scanner.scan(/R\s*(\d+)?\s*(\.+)?/i)
len, dots = [1, 2].map {scanner[_1]}
note.clearLegatoFlags
addNote__ seq, pending, note, prevOsc if pending
pending = nil
prevOsc = nil
note.time += seconds__ note, len&.to_i, dots&.size
when scanner.scan(/([CDEFGAB])\s*([#+-]+)?\s*(\d+)?\s*(\.+)?/i)&.chomp
char, offset, len, dots = [1, 2, 3, 4].map {scanner[_1]}
note.frequency = frequency__ note, char, offset
prevOsc = addNote__ seq, pending, note, prevOsc if pending
pending = note.dup
pending.seconds += seconds__ note, len&.to_i, dots&.size
note.clearLegatoFlags
else
raise "Unknown input: #{scanner.rest[..10]}"
end
scanner.skip(/\s*/)
end
addNote__ seq, pending, note, prevOsc if pending
return seq, note.time
end
|