brevity 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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