brevity 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.gitignore +7 -0
  4. data/.rspec +1 -0
  5. data/.ruby-version +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.rdoc +4 -0
  8. data/Gemfile +3 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.rdoc +27 -0
  11. data/Rakefile +67 -0
  12. data/bin/debrief +56 -0
  13. data/brevity.gemspec +28 -0
  14. data/examples/alley_cat.br +50 -0
  15. data/examples/hip.br +15 -0
  16. data/examples/missed_connection.br +6 -0
  17. data/examples/song1.br +11 -0
  18. data/examples/song2.br +10 -0
  19. data/examples/twinkle.br +18 -0
  20. data/lib/brevity/commands/constants.rb +18 -0
  21. data/lib/brevity/commands/expr.rb +17 -0
  22. data/lib/brevity/commands/meter.rb +20 -0
  23. data/lib/brevity/commands/part.rb +47 -0
  24. data/lib/brevity/commands/tempo.rb +13 -0
  25. data/lib/brevity/itemization.rb +73 -0
  26. data/lib/brevity/parsing/expression/dynamic.rb +280 -0
  27. data/lib/brevity/parsing/expression/dynamic.treetop +41 -0
  28. data/lib/brevity/parsing/expression/dynamic_nodes.rb +55 -0
  29. data/lib/brevity/parsing/expression/expression.rb +429 -0
  30. data/lib/brevity/parsing/expression/expression.treetop +39 -0
  31. data/lib/brevity/parsing/expression/expression_nodes.rb +26 -0
  32. data/lib/brevity/parsing/expression/gradual.rb +44 -0
  33. data/lib/brevity/parsing/expression/gradual.treetop +9 -0
  34. data/lib/brevity/parsing/expression/gradual_node.rb +11 -0
  35. data/lib/brevity/parsing/expression/label.rb +75 -0
  36. data/lib/brevity/parsing/expression/label.treetop +9 -0
  37. data/lib/brevity/parsing/expression/label_node.rb +15 -0
  38. data/lib/brevity/parsing/expression/sequence.rb +130 -0
  39. data/lib/brevity/parsing/expression/sequence.treetop +12 -0
  40. data/lib/brevity/parsing/expression/sequence_node.rb +14 -0
  41. data/lib/brevity/parsing/file/command.rb +216 -0
  42. data/lib/brevity/parsing/file/command.treetop +17 -0
  43. data/lib/brevity/parsing/file/command_node.rb +11 -0
  44. data/lib/brevity/parsing/file/comment.rb +178 -0
  45. data/lib/brevity/parsing/file/comment.treetop +21 -0
  46. data/lib/brevity/parsing/file/comment_node.rb +3 -0
  47. data/lib/brevity/parsing/file/file.rb +152 -0
  48. data/lib/brevity/parsing/file/file.treetop +16 -0
  49. data/lib/brevity/parsing/file/file_node.rb +7 -0
  50. data/lib/brevity/parsing/file/path.rb +235 -0
  51. data/lib/brevity/parsing/file/path.treetop +12 -0
  52. data/lib/brevity/parsing/modifiers/duplicate_modifier.rb +65 -0
  53. data/lib/brevity/parsing/modifiers/duplicate_modifier.treetop +11 -0
  54. data/lib/brevity/parsing/modifiers/duplicate_modifier_node.rb +8 -0
  55. data/lib/brevity/parsing/modifiers/modifier.rb +64 -0
  56. data/lib/brevity/parsing/modifiers/modifier.treetop +13 -0
  57. data/lib/brevity/parsing/modifiers/stretch_modifier.rb +69 -0
  58. data/lib/brevity/parsing/modifiers/stretch_modifier.treetop +11 -0
  59. data/lib/brevity/parsing/modifiers/stretch_modifier_node.rb +21 -0
  60. data/lib/brevity/parsing/modifiers/transpose_modifier.rb +69 -0
  61. data/lib/brevity/parsing/modifiers/transpose_modifier.treetop +11 -0
  62. data/lib/brevity/parsing/modifiers/transpose_modifier_node.rb +13 -0
  63. data/lib/brevity/parsing/note/accent.rb +44 -0
  64. data/lib/brevity/parsing/note/accent.treetop +9 -0
  65. data/lib/brevity/parsing/note/duration.rb +203 -0
  66. data/lib/brevity/parsing/note/duration.treetop +23 -0
  67. data/lib/brevity/parsing/note/duration_nodes.rb +19 -0
  68. data/lib/brevity/parsing/note/link.rb +69 -0
  69. data/lib/brevity/parsing/note/link.treetop +11 -0
  70. data/lib/brevity/parsing/note/link_node.rb +19 -0
  71. data/lib/brevity/parsing/note/note.rb +300 -0
  72. data/lib/brevity/parsing/note/note.treetop +30 -0
  73. data/lib/brevity/parsing/note/note_nodes.rb +77 -0
  74. data/lib/brevity/parsing/note/pitch.rb +81 -0
  75. data/lib/brevity/parsing/note/pitch.treetop +9 -0
  76. data/lib/brevity/parsing/note/pitch_node.rb +50 -0
  77. data/lib/brevity/parsing/numbers/nonnegative_integer.rb +53 -0
  78. data/lib/brevity/parsing/numbers/nonnegative_integer.treetop +9 -0
  79. data/lib/brevity/parsing/numbers/positive_integer.rb +91 -0
  80. data/lib/brevity/parsing/numbers/positive_integer.treetop +15 -0
  81. data/lib/brevity/read_file.rb +18 -0
  82. data/lib/brevity/score_maker.rb +64 -0
  83. data/lib/brevity/version.rb +4 -0
  84. data/lib/brevity.rb +53 -0
  85. data/manuals/brevity.pdf +0 -0
  86. data/manuals/brevity.tex +273 -0
  87. data/spec/brevity_spec.rb +8 -0
  88. data/spec/commands/expr_spec.rb +15 -0
  89. data/spec/commands/meter_spec.rb +21 -0
  90. data/spec/commands/part_spec.rb +16 -0
  91. data/spec/commands/tempo_spec.rb +20 -0
  92. data/spec/itemization_spec.rb +46 -0
  93. data/spec/parsing/expression/dynamic_nodes_spec.rb +32 -0
  94. data/spec/parsing/expression/dynamic_spec.rb +23 -0
  95. data/spec/parsing/expression/expression_nodes_spec.rb +87 -0
  96. data/spec/parsing/expression/expression_spec.rb +85 -0
  97. data/spec/parsing/expression/gradual_spec.rb +10 -0
  98. data/spec/parsing/expression/label_node_spec.rb +13 -0
  99. data/spec/parsing/expression/label_spec.rb +35 -0
  100. data/spec/parsing/file/command_node_spec.rb +29 -0
  101. data/spec/parsing/file/command_spec.rb +18 -0
  102. data/spec/parsing/file/comment_spec.rb +14 -0
  103. data/spec/parsing/file/file_node_spec.rb +19 -0
  104. data/spec/parsing/file/file_spec.rb +30 -0
  105. data/spec/parsing/modifiers/modifier_nodes_spec.rb +25 -0
  106. data/spec/parsing/modifiers/modifier_parsers_spec.rb +20 -0
  107. data/spec/parsing/note/accent_spec.rb +27 -0
  108. data/spec/parsing/note/duration_nodes_spec.rb +79 -0
  109. data/spec/parsing/note/duration_spec.rb +69 -0
  110. data/spec/parsing/note/link_node_spec.rb +30 -0
  111. data/spec/parsing/note/link_spec.rb +23 -0
  112. data/spec/parsing/note/note_nodes_spec.rb +82 -0
  113. data/spec/parsing/note/note_spec.rb +188 -0
  114. data/spec/parsing/note/pitch_node_spec.rb +48 -0
  115. data/spec/parsing/note/pitch_spec.rb +23 -0
  116. data/spec/parsing/numbers/nonnegative_integer_spec.rb +11 -0
  117. data/spec/parsing/numbers/positive_integer_spec.rb +17 -0
  118. data/spec/spec_helper.rb +112 -0
  119. metadata +293 -0
@@ -0,0 +1,82 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe RestNoteNode do
4
+ {
5
+ '/2' => Note.new(Rational(1,2)),
6
+ '4/2' => Note.new(Rational(4,2)),
7
+ '28' => Note.new(Rational(28,1)),
8
+ '56/33' => Note.new(Rational(56,33)),
9
+ }.each do |str,tgt|
10
+ res = NOTE_PARSER.parse(str)
11
+ context str do
12
+ it 'should parse as RestNoteNode' do
13
+ res.should be_a RestNoteNode
14
+ end
15
+
16
+ describe '#to_note' do
17
+ n = res.to_note
18
+ it 'should produce a Note' do
19
+ n.should be_a Note
20
+ end
21
+
22
+ it 'should produce value matching input str' do
23
+ n.should eq tgt
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ describe MonophonicNoteNode do
31
+ {
32
+ '/2C2=C2' => Note.new(Rational(1,2),[C2],links:{C2=>Link::Slur.new(C2)}),
33
+ '4/2D#6^' => Note.new(Rational(4,2),[Eb6],accent:Accent::Martellato.new),
34
+ '28Eb7>' => Note.new(Rational(28,1),[Eb7],accent:Accent::Marcato.new),
35
+ '56/33B1.' => Note.new(Rational(56,33),[B1],accent:Accent::Staccato.new),
36
+ }.each do |str,tgt|
37
+ res = NOTE_PARSER.parse(str)
38
+ context str do
39
+ it 'should parse as MonophonicNoteNode' do
40
+ res.should be_a MonophonicNoteNode
41
+ end
42
+
43
+ describe '#to_note' do
44
+ n = res.to_note
45
+ it 'should produce a Note' do
46
+ n.should be_a Note
47
+ end
48
+
49
+ it 'should produce value matching input str' do
50
+ n.should eq tgt
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ describe PolyphonicNoteNode do
58
+ {
59
+ '/2C2,D2,E2-F2' => Note.new(Rational(1,2),[C2,D2,E2],links:{E2=>Link::Legato.new(F2)}),
60
+ '4/2D#6,G4' => Note.new(Rational(4,2),[Eb6,G4]),
61
+ '28Eb7,D7,G7_' => Note.new(Rational(28,1),[Eb7,D7,G7],accent:Accent::Tenuto.new),
62
+ '56/33B1,B2,B3,B4,B5' => Note.new(Rational(56,33),[B1,B2,B3,B4,B5]),
63
+ }.each do |str,tgt|
64
+ res = NOTE_PARSER.parse(str)
65
+ context str do
66
+ it 'should parse as PolyphonicNoteNode' do
67
+ res.should be_a PolyphonicNoteNode
68
+ end
69
+
70
+ describe '#to_note' do
71
+ n = res.to_note
72
+ it 'should produce a Note' do
73
+ n.should be_a Note
74
+ end
75
+
76
+ it 'should produce value matching input str' do
77
+ n.should eq tgt
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,188 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe NoteParser do
4
+ before :all do
5
+ @parser = NoteParser.new
6
+ @valid = {
7
+ :numbers => [1,5,50,3999,01,0010,0000005050],
8
+ :links => ["=","-","~","/"],
9
+ :accents => [".","'",">","^","_",""],
10
+ :pitch_classes => ["A","B","C","D","E","F","G"],
11
+ :accidentals => ["\#","b",""],
12
+ :octaves => [0,1,2,3,4,5,6,7,8,9],
13
+ }
14
+
15
+ @invalid = {
16
+ :numbers => [0,00],
17
+ :links => ["{",";"],
18
+ :accents => ["*","&"],
19
+ :pitch_classes => ["H","Z"],
20
+ :accidentals => ["$"],
21
+ :octaves => [-1,10],
22
+ }
23
+
24
+ @valid[:pitches] = []
25
+ @valid[:pitch_classes].each do |pc|
26
+ @valid[:accidentals].each do |a|
27
+ @valid[:octaves].each do |o|
28
+ @valid[:pitches].push "#{pc}#{a}#{o}"
29
+ end
30
+ end
31
+ end
32
+
33
+ @invalid[:pitches] = []
34
+ @invalid[:pitch_classes].each do |pc|
35
+ @valid[:accidentals].each do |a|
36
+ @valid[:octaves].each do |o|
37
+ @invalid[:pitches].push "#{pc}#{a}#{o}"
38
+ end
39
+ end
40
+ end
41
+ @valid[:pitch_classes].each do |pc|
42
+ @invalid[:accidentals].each do |a|
43
+ @valid[:octaves].each do |o|
44
+ @invalid[:pitches].push "#{pc}#{a}#{o}"
45
+ end
46
+ end
47
+ end
48
+ @valid[:pitch_classes].each do |pc|
49
+ @valid[:accidentals].each do |a|
50
+ @invalid[:octaves].each do |o|
51
+ @invalid[:pitches].push "#{pc}#{a}#{o}"
52
+ end
53
+ end
54
+ end
55
+ @invalid[:pitch_classes].each do |pc|
56
+ @invalid[:accidentals].each do |a|
57
+ @valid[:octaves].each do |o|
58
+ @invalid[:pitches].push "#{pc}#{a}#{o}"
59
+ end
60
+ end
61
+ end
62
+ @invalid[:pitch_classes].each do |pc|
63
+ @valid[:accidentals].each do |a|
64
+ @invalid[:octaves].each do |o|
65
+ @invalid[:pitches].push "#{pc}#{a}#{o}"
66
+ end
67
+ end
68
+ end
69
+ @valid[:pitch_classes].each do |pc|
70
+ @invalid[:accidentals].each do |a|
71
+ @invalid[:octaves].each do |o|
72
+ @invalid[:pitches].push "#{pc}#{a}#{o}"
73
+ end
74
+ end
75
+ end
76
+ #puts @valid[:pitches]
77
+ end
78
+
79
+ context "note duration only" do
80
+ it 'should parse' do
81
+ @parser.parse("1/4").should_not be nil
82
+ end
83
+ end
84
+
85
+ context 'duration and one pitch' do
86
+ context 'valid pitch' do
87
+ context 'without link marker' do
88
+ it 'should parse' do
89
+ @valid[:pitches].each do |pitch|
90
+ str = "1/4#{pitch}"
91
+ @parser.parse(str).should_not be nil
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'with link marker' do
97
+ context 'link marker is valid' do
98
+ it 'should parse' do
99
+ @valid[:pitches].each do |pitch|
100
+ @valid[:links].each do |link|
101
+ str = "1/4#{pitch}#{link}#{pitch}"
102
+ @parser.parse(str).should_not be nil
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ context 'link marker is not valid' do
109
+ it 'should not parse' do
110
+ @valid[:pitches].each do |pitch|
111
+ @invalid[:links].each do |link|
112
+ str = "1/4#{pitch}#{link}#{pitch}"
113
+ @parser.parse(str).should be nil
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ context 'invalid pitch' do
122
+ context 'without link marker' do
123
+ it 'should parse' do
124
+ @invalid[:pitches].each do |pitch|
125
+ str = "1/4#{pitch}"
126
+ @parser.parse(str).should be nil
127
+ end
128
+ end
129
+ end
130
+
131
+ context 'with link marker' do
132
+ it 'should not parse' do
133
+ @invalid[:pitches].each do |pitch|
134
+ @valid[:links].each do |link|
135
+ str = "1/4#{pitch}#{link}#{pitch}"
136
+ @parser.parse(str).should be nil
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ context 'duration and multiple pitches' do
145
+ context 'all valid pitches' do
146
+ context 'without links' do
147
+ it 'should parse' do
148
+ str = "4/" + @valid[:pitches].join(",")
149
+ @parser.parse(str).should_not be nil
150
+ end
151
+ end
152
+
153
+ context 'with link marker' do
154
+ context 'link marker is valid' do
155
+ it 'should parse' do
156
+ @valid[:links].each do |link|
157
+ str = "4/" + @valid[:pitches].join("#{link}C4,")
158
+ @parser.parse(str).should_not be nil
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'link marker is not valid' do
164
+ it 'should not parse' do
165
+ @invalid[:links].each do |link|
166
+ str = "4/" + @valid[:pitches].join("#{link}C4,")
167
+ @parser.parse(str).should be nil
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ context 'one invalid pitch' do
175
+ it 'should not parse' do
176
+ str = "4/" + ([@invalid[:pitches][0]] + @valid[:pitches]).join(",")
177
+ @parser.parse(str).should be nil
178
+ end
179
+ end
180
+
181
+ context 'all invalid pitches' do
182
+ it 'should not parse' do
183
+ str = "4/" + @invalid[:pitches].join(",")
184
+ @parser.parse(str).should be nil
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,48 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe 'String#to_pitch' do
4
+ it 'should create a Pitch object that matches the musical note' do
5
+ {
6
+ "Ab2" => Pitch.new(octave: 2, semitone: 8),
7
+ "C0" => Pitch.new(octave: 0, semitone: 0),
8
+ "db4" => Pitch.new(octave: 4, semitone: 1),
9
+ "F#12" => Pitch.new(octave: 12, semitone: 6),
10
+ "E#7" => Pitch.new(octave: 7, semitone: 5),
11
+ "G9" => Pitch.new(octave: 9, semitone: 7),
12
+ "Bb10" => Pitch.new(octave: 10, semitone: 10),
13
+ }.each do |str, expected_pitch|
14
+ str.to_pitch.should eq(expected_pitch)
15
+ end
16
+ end
17
+ end
18
+
19
+ describe PitchNode do
20
+ parser = PitchParser.new
21
+
22
+ {
23
+ 'C4' => C4,
24
+ 'Db2' => Db2,
25
+ 'C#2' => Db2,
26
+ 'Db2' => Db2,
27
+ 'F7' => F7,
28
+ 'B1' => B1,
29
+ }.each do |str,tgt|
30
+ res = parser.parse(str)
31
+ context str do
32
+ it 'should parse as PitchNode' do
33
+ res.should be_a PitchNode
34
+ end
35
+
36
+ describe '#to_pitch' do
37
+ p = res.to_pitch
38
+ it 'should produce a Pitch object' do
39
+ p.should be_a Pitch
40
+ end
41
+
42
+ it 'should produce pitch matching input str' do
43
+ p.should eq tgt
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe PitchParser do
4
+ before :all do
5
+ @parser = PitchParser.new
6
+ end
7
+
8
+ it 'should parse "C4"' do
9
+ @parser.parse("C4").should_not be nil
10
+ end
11
+
12
+ it 'should parse "C#9"' do
13
+ @parser.parse("C#9").should_not be nil
14
+ end
15
+
16
+ it 'should parse "Ab0"' do
17
+ @parser.parse("Ab0").should_not be nil
18
+ end
19
+
20
+ it 'should parse "G#2"' do
21
+ @parser.parse("G#2").should_not be nil
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe NonnegativeIntegerParser do
4
+ parser = NonnegativeIntegerParser.new
5
+
6
+ ["1","50","05","502530","0"].each do |str|
7
+ it "should parse '#{str}'" do
8
+ parser.parse(str).should_not be nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe PositiveIntegerParser do
4
+ parser = PositiveIntegerParser.new
5
+
6
+ ["1","50","05","502530"].each do |str|
7
+ it "should parse '#{str}'" do
8
+ parser.parse(str).should_not be nil
9
+ end
10
+ end
11
+
12
+ ["0"].each do |str|
13
+ it "should not parse '#{str}'" do
14
+ parser.parse(str).should be nil
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,112 @@
1
+ require 'rspec'
2
+ require 'brevity'
3
+
4
+ include Brevity
5
+ include Music::Transcription
6
+ include Pitches
7
+
8
+ RSpec::Matchers.define :parse do |str|
9
+ match do |parser|
10
+ !parser.parse(str).nil?
11
+ end
12
+ end
13
+
14
+ RSpec::Matchers.define :parse_as do |str,nodeclass|
15
+ match do |parser|
16
+ parser.parse(str).is_a?(nodeclass)
17
+ end
18
+ end
19
+
20
+ LABELS = ["riff","GUITAR2","_my_part_"]
21
+ NOTES = {
22
+ '/2A7' => Note::Half.new([A7]),
23
+ '/4A3.' => Note::Quarter.new([A3],accent: Accent::Staccato.new),
24
+ '/2Bb2' => Note::Half.new([Bb2]),
25
+ '/2G2,C2-Eb3' => Note::Half.new([G2,C2],links: {C2=>Link::Legato.new(Eb3)}),
26
+ '1Eb3' => Note::Whole.new([Eb3]),
27
+ }
28
+ sequences = {}
29
+ [2,3].each do |n|
30
+ NOTES.entries.permutation(n).each do |perm|
31
+ strs, notes = perm.transpose
32
+ sequences[strs.join(" ")] = notes
33
+ end
34
+ end
35
+ SEQUENCES = sequences
36
+
37
+ MODIFIERS = {
38
+ :duplicate => {
39
+ ':1' => lambda {|primitives| primitives.clone },
40
+ ':2' => lambda {|primitives| primitives.clone + primitives.clone },
41
+ ':3' => lambda {|primitives| primitives.clone + primitives.clone + primitives.clone }
42
+ },
43
+ :stretch => {
44
+ '*1' => lambda {|primitives| primitives.clone },
45
+ '*3/2' => lambda do |primitives|
46
+ primitives.map {|p| p.respond_to?(:stretch) ? p.stretch('3/2'.to_r) : p }
47
+ end,
48
+ '*/4' => lambda do |primitives|
49
+ primitives.map {|p| p.respond_to?(:stretch) ? p.stretch('1/4'.to_r) : p }
50
+ end,
51
+ '=1' => lambda do |primitives|
52
+ duration = primitives.map do |p|
53
+ p.respond_to?(:duration) ? p.duration : 0
54
+ end.inject(0,:+)
55
+ ratio = 1 / duration
56
+ primitives.map {|p| p.respond_to?(:stretch) ? p.stretch(ratio) : p }
57
+ end,
58
+ '=/2' => lambda do |primitives|
59
+ duration = primitives.map do |p|
60
+ p.respond_to?(:duration) ? p.duration : 0
61
+ end.inject(0,:+)
62
+ ratio = Rational(1,2) / duration
63
+ primitives.map {|p| p.respond_to?(:stretch) ? p.stretch(ratio) : p }
64
+ end,
65
+ '=2/3' => lambda do |primitives|
66
+ duration = primitives.map do |p|
67
+ p.respond_to?(:duration) ? p.duration : 0
68
+ end.inject(0,:+)
69
+ ratio = Rational(2,3) / duration
70
+ primitives.map {|p| p.respond_to?(:stretch) ? p.stretch(ratio) : p }
71
+ end,
72
+ },
73
+ :transpose => {
74
+ '+0' => lambda {|primitives| primitives.clone },
75
+ '-0' => lambda {|primitives| primitives.clone },
76
+ '+4' => lambda do |primitives|
77
+ primitives.map {|p| p.respond_to?(:transpose) ? p.transpose(4) : p }
78
+ end,
79
+ '-4' => lambda do |primitives|
80
+ primitives.map {|p| p.respond_to?(:transpose) ? p.transpose(-4) : p }
81
+ end,
82
+ '+10' => lambda do |primitives|
83
+ primitives.map {|p| p.respond_to?(:transpose) ? p.transpose(10) : p }
84
+ end,
85
+ '-99' => lambda do |primitives|
86
+ primitives.map {|p| p.respond_to?(:transpose) ? p.transpose(-99) : p }
87
+ end
88
+ }
89
+ }
90
+
91
+ EXPR_PARSER = ExpressionParser.new
92
+ DUR_PARSER = DurationParser.new
93
+ NOTE_PARSER = NoteParser.new
94
+ LABEL_PARSER = LabelParser.new
95
+ COMMENT_PARSER = CommentParser.new
96
+ COMMAND_PARSER = CommandParser.new
97
+ FILE_PARSER = FileParser.new
98
+
99
+ class CommandTester
100
+ include Commands
101
+
102
+ attr_reader :env
103
+ def initialize
104
+ @env = {}
105
+ @env[ENV_START_TEMPO] = nil
106
+ @env[ENV_START_METER] = nil
107
+ @env[ENV_TEMPO_CHANGES] = {}
108
+ @env[ENV_METER_CHANGES] = {}
109
+ @env[ENV_EXPRS] = {}
110
+ @env[ENV_PARTS] = {}
111
+ end
112
+ end