jmtk 0.0.3.3-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +10 -0
- data/DEVELOPMENT_NOTES.md +115 -0
- data/INTRO.md +129 -0
- data/LICENSE.txt +27 -0
- data/README.md +50 -0
- data/Rakefile +102 -0
- data/bin/jmtk +250 -0
- data/bin/mtk +250 -0
- data/examples/crescendo.rb +20 -0
- data/examples/drum_pattern.rb +23 -0
- data/examples/dynamic_pattern.rb +36 -0
- data/examples/gets_and_play.rb +27 -0
- data/examples/notation.rb +22 -0
- data/examples/play_midi.rb +17 -0
- data/examples/print_midi.rb +13 -0
- data/examples/random_tone_row.rb +18 -0
- data/examples/syntax_to_midi.rb +28 -0
- data/examples/test_output.rb +7 -0
- data/examples/tone_row_melody.rb +23 -0
- data/lib/mtk.rb +76 -0
- data/lib/mtk/core/duration.rb +213 -0
- data/lib/mtk/core/intensity.rb +158 -0
- data/lib/mtk/core/interval.rb +157 -0
- data/lib/mtk/core/pitch.rb +154 -0
- data/lib/mtk/core/pitch_class.rb +194 -0
- data/lib/mtk/events/event.rb +119 -0
- data/lib/mtk/events/note.rb +112 -0
- data/lib/mtk/events/parameter.rb +54 -0
- data/lib/mtk/events/timeline.rb +232 -0
- data/lib/mtk/groups/chord.rb +56 -0
- data/lib/mtk/groups/collection.rb +196 -0
- data/lib/mtk/groups/melody.rb +96 -0
- data/lib/mtk/groups/pitch_class_set.rb +163 -0
- data/lib/mtk/groups/pitch_collection.rb +23 -0
- data/lib/mtk/io/dls_synth_device.rb +146 -0
- data/lib/mtk/io/dls_synth_output.rb +62 -0
- data/lib/mtk/io/jsound_input.rb +87 -0
- data/lib/mtk/io/jsound_output.rb +82 -0
- data/lib/mtk/io/midi_file.rb +209 -0
- data/lib/mtk/io/midi_input.rb +97 -0
- data/lib/mtk/io/midi_output.rb +195 -0
- data/lib/mtk/io/notation.rb +162 -0
- data/lib/mtk/io/unimidi_input.rb +117 -0
- data/lib/mtk/io/unimidi_output.rb +140 -0
- data/lib/mtk/lang/durations.rb +57 -0
- data/lib/mtk/lang/intensities.rb +61 -0
- data/lib/mtk/lang/intervals.rb +73 -0
- data/lib/mtk/lang/mtk_grammar.citrus +237 -0
- data/lib/mtk/lang/parser.rb +29 -0
- data/lib/mtk/lang/pitch_classes.rb +29 -0
- data/lib/mtk/lang/pitches.rb +52 -0
- data/lib/mtk/lang/pseudo_constants.rb +26 -0
- data/lib/mtk/lang/variable.rb +32 -0
- data/lib/mtk/numeric_extensions.rb +66 -0
- data/lib/mtk/patterns/chain.rb +49 -0
- data/lib/mtk/patterns/choice.rb +43 -0
- data/lib/mtk/patterns/cycle.rb +18 -0
- data/lib/mtk/patterns/for_each.rb +71 -0
- data/lib/mtk/patterns/function.rb +39 -0
- data/lib/mtk/patterns/lines.rb +54 -0
- data/lib/mtk/patterns/palindrome.rb +45 -0
- data/lib/mtk/patterns/pattern.rb +171 -0
- data/lib/mtk/patterns/sequence.rb +20 -0
- data/lib/mtk/sequencers/event_builder.rb +132 -0
- data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
- data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
- data/lib/mtk/sequencers/sequencer.rb +111 -0
- data/lib/mtk/sequencers/step_sequencer.rb +26 -0
- data/spec/mtk/core/duration_spec.rb +372 -0
- data/spec/mtk/core/intensity_spec.rb +289 -0
- data/spec/mtk/core/interval_spec.rb +265 -0
- data/spec/mtk/core/pitch_class_spec.rb +343 -0
- data/spec/mtk/core/pitch_spec.rb +297 -0
- data/spec/mtk/events/event_spec.rb +234 -0
- data/spec/mtk/events/note_spec.rb +174 -0
- data/spec/mtk/events/parameter_spec.rb +220 -0
- data/spec/mtk/events/timeline_spec.rb +430 -0
- data/spec/mtk/groups/chord_spec.rb +85 -0
- data/spec/mtk/groups/collection_spec.rb +374 -0
- data/spec/mtk/groups/melody_spec.rb +225 -0
- data/spec/mtk/groups/pitch_class_set_spec.rb +340 -0
- data/spec/mtk/io/midi_file_spec.rb +243 -0
- data/spec/mtk/io/midi_output_spec.rb +102 -0
- data/spec/mtk/lang/durations_spec.rb +89 -0
- data/spec/mtk/lang/intensities_spec.rb +101 -0
- data/spec/mtk/lang/intervals_spec.rb +143 -0
- data/spec/mtk/lang/parser_spec.rb +603 -0
- data/spec/mtk/lang/pitch_classes_spec.rb +62 -0
- data/spec/mtk/lang/pitches_spec.rb +56 -0
- data/spec/mtk/lang/pseudo_constants_spec.rb +20 -0
- data/spec/mtk/lang/variable_spec.rb +52 -0
- data/spec/mtk/numeric_extensions_spec.rb +83 -0
- data/spec/mtk/patterns/chain_spec.rb +110 -0
- data/spec/mtk/patterns/choice_spec.rb +97 -0
- data/spec/mtk/patterns/cycle_spec.rb +123 -0
- data/spec/mtk/patterns/for_each_spec.rb +136 -0
- data/spec/mtk/patterns/function_spec.rb +120 -0
- data/spec/mtk/patterns/lines_spec.rb +77 -0
- data/spec/mtk/patterns/palindrome_spec.rb +108 -0
- data/spec/mtk/patterns/pattern_spec.rb +132 -0
- data/spec/mtk/patterns/sequence_spec.rb +203 -0
- data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
- data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
- data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
- data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
- data/spec/mtk/sequencers/step_sequencer_spec.rb +93 -0
- data/spec/spec_coverage.rb +2 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/test.mid +0 -0
- metadata +226 -0
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MTK::Events::Event do
|
4
|
+
|
5
|
+
EVENT = MTK::Events::Event
|
6
|
+
|
7
|
+
let(:type) { :type }
|
8
|
+
let(:duration) { 2.5 }
|
9
|
+
let(:options) { {:number => 1, :value => 0.5, :duration => duration, :channel => 0} }
|
10
|
+
let(:event) { EVENT.new type, options }
|
11
|
+
let(:hash) { options.merge({:type => type}) }
|
12
|
+
|
13
|
+
|
14
|
+
describe "#type" do
|
15
|
+
it "is the first argument passed to AbstractEvent.new" do
|
16
|
+
event.type.should == type
|
17
|
+
end
|
18
|
+
|
19
|
+
it "is a read-only attribute" do
|
20
|
+
lambda{ event.type = :anything }.should raise_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#value" do
|
25
|
+
it "is the value of the :value key in the options hash passed to AbstractEvent.new" do
|
26
|
+
event.value.should == options[:value]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "defaults to nil" do
|
30
|
+
EVENT.new(type).value.should be_nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#value=" do
|
35
|
+
it "sets #value" do
|
36
|
+
event.value = 42
|
37
|
+
event.value.should == 42
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#duration" do
|
42
|
+
it "is the value of the :duration key in the options hash passed to AbstractEvent.new" do
|
43
|
+
event.duration.should == options[:duration]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "defaults to 0" do
|
47
|
+
EVENT.new(type).duration.should == 0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#duration=" do
|
52
|
+
it "sets #duration" do
|
53
|
+
event.duration = 42
|
54
|
+
event.duration.should == 42
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#number" do
|
59
|
+
it "is the value of the :number key in the options hash passed to AbstractEvent.new" do
|
60
|
+
event.number.should == options[:number]
|
61
|
+
end
|
62
|
+
|
63
|
+
it "defaults to nil" do
|
64
|
+
EVENT.new(type).number.should be_nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#number=" do
|
69
|
+
it "sets #number" do
|
70
|
+
event.number = 42
|
71
|
+
event.number.should == 42
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#channel" do
|
76
|
+
it "is the value of the :channel key in the options hash passed to AbstractEvent.new" do
|
77
|
+
event.channel.should == options[:channel]
|
78
|
+
end
|
79
|
+
|
80
|
+
it "defaults to nil" do
|
81
|
+
EVENT.new(type).channel.should be_nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#channel=" do
|
86
|
+
it "sets #channel" do
|
87
|
+
event.channel = 42
|
88
|
+
event.channel.should == 42
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#midi_value" do
|
93
|
+
it "is the @value * 127, rounded to the nearest integer" do
|
94
|
+
event.midi_value.should == (options[:value]*127).round
|
95
|
+
end
|
96
|
+
|
97
|
+
it "defaults to 0 when the @value is nil" do
|
98
|
+
EVENT.new(type).midi_value.should == 0
|
99
|
+
end
|
100
|
+
|
101
|
+
it "enforces a minimum of 0" do
|
102
|
+
event.value = -2
|
103
|
+
event.midi_value.should == 0
|
104
|
+
end
|
105
|
+
|
106
|
+
it "enforces a maximum of 127" do
|
107
|
+
event.value = 2
|
108
|
+
event.midi_value.should == 127
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#midi_value=" do
|
113
|
+
it "sets #value to the argument/127.0" do
|
114
|
+
event.midi_value = 100
|
115
|
+
event.value.should == 100/127.0
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#length" do
|
120
|
+
it "is the absolute value of duration" do
|
121
|
+
event.duration = -duration
|
122
|
+
event.length.should == duration
|
123
|
+
end
|
124
|
+
|
125
|
+
it "is 0 if the #duration is nil" do
|
126
|
+
event.duration = nil
|
127
|
+
event.length.should == 0
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "#rest?" do
|
132
|
+
it "is true when the duration is negative" do
|
133
|
+
event.duration = -duration
|
134
|
+
event.rest?.should be_true
|
135
|
+
end
|
136
|
+
|
137
|
+
it "is false when the duration is positive" do
|
138
|
+
event.rest?.should be_false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "#instantaneous?" do
|
143
|
+
it "is true when the duration is 0" do
|
144
|
+
event.duration = 0
|
145
|
+
event.instantaneous?.should be_true
|
146
|
+
end
|
147
|
+
|
148
|
+
it "is true when the duration is nil" do
|
149
|
+
event.duration = nil
|
150
|
+
event.instantaneous?.should be_true
|
151
|
+
end
|
152
|
+
|
153
|
+
it "is false when the duration is positive" do
|
154
|
+
event.instantaneous?.should be_false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "#duration_in_pulses" do
|
159
|
+
it "multiplies the #length times the argument and rounds to the nearest integer" do
|
160
|
+
event.duration_in_pulses(111).should == (event.length * 111).round
|
161
|
+
end
|
162
|
+
|
163
|
+
it "is 0 when the #duration is nil" do
|
164
|
+
event.duration = nil
|
165
|
+
event.duration_in_pulses(111).should == 0
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "from_h" do
|
170
|
+
it "constructs an Event using a hash" do
|
171
|
+
EVENT.from_h(hash).should == event
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#to_h" do
|
176
|
+
it "is a hash containing all the attributes of the Event" do
|
177
|
+
event.to_h.should == hash
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe "#duration_in_pulses" do
|
182
|
+
it "converts beats to pulses, given pulses_per_beat" do
|
183
|
+
EVENT.new(type, :duration => 1).duration_in_pulses(60).should == 60
|
184
|
+
end
|
185
|
+
|
186
|
+
it "rounds to the nearest pulse" do
|
187
|
+
EVENT.new(type, :duration => 1.5).duration_in_pulses(59).should == 89
|
188
|
+
end
|
189
|
+
|
190
|
+
it "is always positive (uses absolute value of the duration used to construct the Event)" do
|
191
|
+
EVENT.new(type, :duration => -1).duration_in_pulses(60).should == 60
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "#==" do
|
196
|
+
it "is true when the type, number, value, duration and channel are equal" do
|
197
|
+
event.should == EVENT.new(type, options)
|
198
|
+
end
|
199
|
+
it "is false when the types are not equal" do
|
200
|
+
event.should_not == EVENT.new(:another_type, options)
|
201
|
+
end
|
202
|
+
it "is false when the numbers are not equal" do
|
203
|
+
event.should_not == EVENT.new(type, options.merge({:number => event.number + 1}))
|
204
|
+
end
|
205
|
+
it "is false when the values are not equal" do
|
206
|
+
event.should_not == EVENT.new(type, options.merge({:value => event.value * 0.5}))
|
207
|
+
end
|
208
|
+
it "is false when the durations are not equal" do
|
209
|
+
event.should_not == EVENT.new(type, options.merge({:duration => event.duration * 2}))
|
210
|
+
end
|
211
|
+
it "is false when the channels are not equal" do
|
212
|
+
event.should_not == EVENT.new(type, options.merge({:channel => event.channel + 1}))
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "#to_s" do
|
217
|
+
it "has the value and duration to 2-decimal places" do
|
218
|
+
EVENT.new(type, :value => 0.454545, :duration => 0.789789).to_s.should == "Event(type, 0.45, 0.79)"
|
219
|
+
end
|
220
|
+
it "includes the #number when not nil" do
|
221
|
+
EVENT.new(type, :value => 0.454545, :duration => 0.789789, :number => 1).to_s.should == "Event(type[1], 0.45, 0.79)"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "#inspect" do
|
226
|
+
it "has the string values of value and duration" do
|
227
|
+
EVENT.new(:type, :value => 0.454545, :duration => 0.789789).inspect.should == "Event(type, 0.454545, 0.789789)"
|
228
|
+
end
|
229
|
+
it "includes the #number when not nil" do
|
230
|
+
EVENT.new(type, :value => 0.454545, :duration => 0.789789, :number => 1).inspect.should == "Event(type[1], 0.454545, 0.789789)"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MTK::Events::Note do
|
4
|
+
|
5
|
+
NOTE = Events::Note
|
6
|
+
|
7
|
+
let(:pitch) { C4 }
|
8
|
+
let(:intensity) { mf }
|
9
|
+
let(:duration) { Duration[2.5] }
|
10
|
+
let(:note) { NOTE.new(pitch, duration, intensity) }
|
11
|
+
|
12
|
+
describe "#pitch" do
|
13
|
+
it "is the pitch used to create the Note" do
|
14
|
+
note.pitch.should == pitch
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#pitch=" do
|
19
|
+
it "sets the pitch" do
|
20
|
+
note.pitch = D4
|
21
|
+
note.pitch.should == D4
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#intensity" do
|
26
|
+
it "is the intensity used to create the Event" do
|
27
|
+
note.intensity.should == intensity
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#intensity=" do
|
32
|
+
it "sets the intensity" do
|
33
|
+
note.intensity = fff
|
34
|
+
note.intensity.should == fff
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#velocity" do
|
39
|
+
it "converts intensities in the range 0.0-1.0 to a MIDI velocity in the range 0-127" do
|
40
|
+
NOTE.new(pitch, 0, 0).velocity.should == 0
|
41
|
+
NOTE.new(pitch, 0, 1).velocity.should == 127
|
42
|
+
end
|
43
|
+
it "rounds to the nearest MIDI velocity" do
|
44
|
+
NOTE.new(pitch, 0, 0.5).velocity.should == 64 # not be truncated to 63!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#velocity=" do
|
49
|
+
it "sets the velocity" do
|
50
|
+
note.velocity = 100
|
51
|
+
note.velocity.should == 100
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".from_h" do
|
56
|
+
it "constructs a Note using a hash" do
|
57
|
+
NOTE.from_h({ :pitch => pitch, :intensity => intensity, :duration => duration }).should == note
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '.from_midi' do
|
62
|
+
it "constructs a Note using a MIDI pitch and velocity" do
|
63
|
+
NOTE.from_midi(C4.to_i, mf.value*127, 2.5).should == note
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#to_h" do
|
68
|
+
it "is a hash containing all the attributes of the Note" do
|
69
|
+
hash = note.to_h
|
70
|
+
# hash includes some extra "baggage" for compatibility with AbstractEvent,
|
71
|
+
# so we'll just check the fields we care about:
|
72
|
+
hash[:pitch].should == pitch
|
73
|
+
hash[:intensity].should == intensity
|
74
|
+
hash[:duration].should == duration
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#transpose' do
|
79
|
+
it 'adds the given interval to the @pitch' do
|
80
|
+
(note.transpose 2).should == NOTE.new(D4, duration, intensity)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#invert" do
|
85
|
+
context 'higher center pitch' do
|
86
|
+
it 'inverts the pitch around the given center pitch' do
|
87
|
+
note.invert(Pitch 66).should == NOTE.new(Pitch(72), duration, intensity)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'lower center pitch' do
|
92
|
+
it 'inverts the pitch around the given center pitch' do
|
93
|
+
note.invert(Pitch 54).should == NOTE.new(Pitch(48), duration, intensity)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns the an equal note when given it's pitch as an argument" do
|
98
|
+
note.invert(note.pitch).should == note
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#==" do
|
103
|
+
it "is true when the pitches, intensities, and durations are equal" do
|
104
|
+
note.should == NOTE.new(pitch, duration, intensity)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "is false when the pitches are not equal" do
|
108
|
+
note.should_not == NOTE.new(pitch + 1, duration, intensity)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "is false when the intensities are not equal" do
|
112
|
+
note.should_not == NOTE.new(pitch, duration, intensity * 0.5)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "is false when the durations are not equal" do
|
116
|
+
note.should_not == NOTE.new(pitch, duration * 2, intensity)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#to_s" do
|
121
|
+
it "includes the #pitch, #intensity to 2 decimal places, and #duration to 2 decimal places" do
|
122
|
+
NOTE.new(C4, Duration(1/3.0), Intensity(1/3.0)).to_s.should == "Note(C4, 0.33 beat, 33%)"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#inspect" do
|
127
|
+
it 'is "#<MTK::Events::Note:{object_id} @pitch={pitch.inspect}, @duration={duration.inspect}, @intensity={intensity.inspect}>"' do
|
128
|
+
duration = MTK.Duration(1/8.0)
|
129
|
+
intensity = MTK.Intensity(1/8.0)
|
130
|
+
note = NOTE.new(C4, duration, intensity)
|
131
|
+
note.inspect.should == "#<MTK::Events::Note:#{note.object_id} @pitch=#{C4.inspect}, @duration=#{duration.inspect}, @intensity=#{intensity.inspect}>"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
describe MTK do
|
139
|
+
|
140
|
+
describe '#Note' do
|
141
|
+
|
142
|
+
it "acts like new for multiple arguments" do
|
143
|
+
Note(C4,q,mf).should == NOTE.new(C4,q,mf)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "acts like new for an Array of arguments by unpacking (splatting) them" do
|
147
|
+
Note([C4,q,mf]).should == NOTE.new(C4,q,mf)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "returns the argument if it's already a Note" do
|
151
|
+
note = NOTE.new(C4,q,mf)
|
152
|
+
Note(note).should be_equal(note)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "raises an error for types it doesn't understand" do
|
156
|
+
lambda{ Note({:not => :compatible}) }.should raise_error
|
157
|
+
end
|
158
|
+
|
159
|
+
it "handles out of order arguments for recognized types (Pitch, Duration, Intensity)" do
|
160
|
+
Note(q,mf,C4).should == NOTE.new(C4,q,mf)
|
161
|
+
end
|
162
|
+
|
163
|
+
it "fills in a missing duration type from an number" do
|
164
|
+
Note(C4,mf,5.25).should == NOTE.new(C4,MTK.Duration(5.25),mf)
|
165
|
+
end
|
166
|
+
|
167
|
+
it '' do
|
168
|
+
Note(MTK::Lang::Pitches::C4, MTK::Lang::Intensities::o, 5.25).should == Note(C4, 5.25, 0.75)
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MTK::Events::Parameter do
|
4
|
+
|
5
|
+
PARAMETER = MTK::Events::Parameter
|
6
|
+
|
7
|
+
|
8
|
+
describe ".from_midi" do
|
9
|
+
|
10
|
+
context "poly pressure events" do
|
11
|
+
|
12
|
+
context "first arg is the status byte" do
|
13
|
+
it "parses :pressure #type on channel 0" do
|
14
|
+
PARAMETER.from_midi(0xA0, 0, 0).type.should == :pressure
|
15
|
+
end
|
16
|
+
it "parses :pressure #type on other channels" do
|
17
|
+
PARAMETER.from_midi(0xA0 | 5, 0, 0).type.should == :pressure
|
18
|
+
end
|
19
|
+
it "parses the #channel" do
|
20
|
+
PARAMETER.from_midi(0xA0 | 5, 0, 0).channel.should == 5
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "first arg is an array of [type,channel]" do
|
25
|
+
it "converts :poly_pressure to :pressure #type" do
|
26
|
+
PARAMETER.from_midi([:poly_pressure, 0], 0, 0).type.should == :pressure
|
27
|
+
end
|
28
|
+
it "extracts the #channel" do
|
29
|
+
PARAMETER.from_midi([:poly_pressure, 5], 0, 0).channel.should == 5
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "sets #number (pitch) to the data1 byte" do
|
34
|
+
PARAMETER.from_midi(0xA0, 100, 50).number.should == 100
|
35
|
+
end
|
36
|
+
|
37
|
+
it "set #value to the data2 byte / 127.0" do
|
38
|
+
PARAMETER.from_midi(0xA0, 100, 50).value.should == 50/127.0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
context "control change events" do
|
44
|
+
|
45
|
+
context "first arg is the status byte" do
|
46
|
+
it "parses :control #type on channel 0" do
|
47
|
+
PARAMETER.from_midi(0xB0, 0, 0).type.should == :control
|
48
|
+
end
|
49
|
+
it "parses :control #type on other channels" do
|
50
|
+
PARAMETER.from_midi(0xB0 | 5, 0, 0).type.should == :control
|
51
|
+
end
|
52
|
+
it "parses the #channel" do
|
53
|
+
PARAMETER.from_midi(0xB0 | 5, 0, 0).channel.should == 5
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "first arg is an array of [type,channel]" do
|
58
|
+
it "converts :control_change :control #type" do
|
59
|
+
PARAMETER.from_midi([:control_change, 0], 0, 0).type.should == :control
|
60
|
+
end
|
61
|
+
it "extracts the #channel" do
|
62
|
+
PARAMETER.from_midi([:control_change, 5], 0, 0).channel.should == 5
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it "sets #number to the data1 byte" do
|
67
|
+
PARAMETER.from_midi(0xB0, 100, 50).number.should == 100
|
68
|
+
end
|
69
|
+
|
70
|
+
it "set #value to the data2 byte / 127.0" do
|
71
|
+
PARAMETER.from_midi(0xB0, 100, 50).value.should == 50/127.0
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
context "program change events" do
|
77
|
+
|
78
|
+
context "first arg is the status byte" do
|
79
|
+
it "parses :program #type on channel 0" do
|
80
|
+
PARAMETER.from_midi(0xC0, 0, 0).type.should == :program
|
81
|
+
end
|
82
|
+
it "parses :program #type on other channels" do
|
83
|
+
PARAMETER.from_midi(0xC0 | 5, 0, 0).type.should == :program
|
84
|
+
end
|
85
|
+
it "parses the #channel" do
|
86
|
+
PARAMETER.from_midi(0xC0 | 5, 0, 0).channel.should == 5
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "first arg is an array of [type,channel]" do
|
91
|
+
it "converts :program_change :program #type" do
|
92
|
+
PARAMETER.from_midi([:program_change, 0], 0, 0).type.should == :program
|
93
|
+
end
|
94
|
+
it "extracts the #channel" do
|
95
|
+
PARAMETER.from_midi([:program_change, 5], 0, 0).channel.should == 5
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "sets #number to the data1 byte" do
|
100
|
+
PARAMETER.from_midi(0xC0, 100, 0).number.should == 100
|
101
|
+
end
|
102
|
+
|
103
|
+
it "does not set #value" do
|
104
|
+
PARAMETER.from_midi(0xC0, 100, 0).value.should be_nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
context "channel pressure events" do
|
110
|
+
|
111
|
+
context "first arg is the status byte" do
|
112
|
+
it "parses :pressure #type on channel 0" do
|
113
|
+
PARAMETER.from_midi(0xD0, 0, 0).type.should == :pressure
|
114
|
+
end
|
115
|
+
it "parses :pressure #type on other channels" do
|
116
|
+
PARAMETER.from_midi(0xD0 | 5, 0, 0).type.should == :pressure
|
117
|
+
end
|
118
|
+
it "parses the #channel" do
|
119
|
+
PARAMETER.from_midi(0xD0 | 5, 0, 0).channel.should == 5
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "first arg is an array of [type,channel]" do
|
124
|
+
it "converts :channel_pressure to :pressure #type" do
|
125
|
+
PARAMETER.from_midi([:channel_pressure, 0], 0, 0).type.should == :pressure
|
126
|
+
end
|
127
|
+
it "extracts the #channel" do
|
128
|
+
PARAMETER.from_midi([:channel_pressure, 5], 0, 0).channel.should == 5
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "does not set #number" do
|
133
|
+
PARAMETER.from_midi(0xD0, 50, 0).number.should be_nil
|
134
|
+
end
|
135
|
+
|
136
|
+
it "set #value to the data1 byte / 127.0" do
|
137
|
+
PARAMETER.from_midi(0xD0, 50, 0).value.should == 50/127.0
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
context "pitch bend events" do
|
143
|
+
|
144
|
+
context "first arg is the status byte" do
|
145
|
+
it "parses :bend #type on channel 0" do
|
146
|
+
PARAMETER.from_midi(0xE0, 0, 0).type.should == :bend
|
147
|
+
end
|
148
|
+
it "parses :bend #type on other channels" do
|
149
|
+
PARAMETER.from_midi(0xE0 | 5, 0, 0).type.should == :bend
|
150
|
+
end
|
151
|
+
it "parses the #channel" do
|
152
|
+
PARAMETER.from_midi(0xE0 | 5, 0, 0).channel.should == 5
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "first arg is an array of [type,channel]" do
|
157
|
+
it "converts :pitch_bend :bend #type" do
|
158
|
+
PARAMETER.from_midi([:pitch_bend, 0], 0, 0).type.should == :bend
|
159
|
+
end
|
160
|
+
it "extracts the #channel" do
|
161
|
+
PARAMETER.from_midi([:pitch_bend, 5], 0, 0).channel.should == 5
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it "does not set #number" do
|
166
|
+
PARAMETER.from_midi(0xE0, 50, 0).number.should be_nil
|
167
|
+
end
|
168
|
+
|
169
|
+
it "sets #value by combining the data1 (lsb) and data2 (msb) bytes into a 14-bit int and then mapping to the range -1.0..1.0" do
|
170
|
+
PARAMETER.from_midi(0xE0, 0, 0).value.should == -1.0
|
171
|
+
PARAMETER.from_midi(0xE0, 0, 64).value.should == 0
|
172
|
+
PARAMETER.from_midi(0xE0, 127, 127).value.should == 1.0
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
context "unknown events" do
|
178
|
+
it "sets #type to :unknown" do
|
179
|
+
PARAMETER.from_midi(0xF0, 50, 100).type.should == :unknown
|
180
|
+
end
|
181
|
+
|
182
|
+
it "sets #number to the data1 byte" do
|
183
|
+
PARAMETER.from_midi(0xF0, 50, 100).number.should == 50
|
184
|
+
end
|
185
|
+
|
186
|
+
it "sets #value to the data2 byte / 127.0" do
|
187
|
+
PARAMETER.from_midi(0xF0, 50, 100).value.should == 100/127.0
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
describe "#midi_value" do
|
195
|
+
it "special cases :bend type events to map the range -1.0..1.0 back to a 14-bit int" do
|
196
|
+
PARAMETER.from_midi([:pitch_bend,0], 0, 0).midi_value.should == 0
|
197
|
+
PARAMETER.from_midi([:pitch_bend,0], 0, 64).midi_value.should == 8192
|
198
|
+
PARAMETER.from_midi([:pitch_bend,0], 127, 127).midi_value.should == 16383
|
199
|
+
end
|
200
|
+
|
201
|
+
it "maps back to the original midi value for other cases" do
|
202
|
+
PARAMETER.from_midi([:control_change,0], 0, 50).midi_value.should == 50
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
describe "#to_s" do
|
208
|
+
it "includes the #type, #number, and #value to 2 decimal places" do
|
209
|
+
PARAMETER.from_midi([:control_change,0], 50, 100).to_s.should == "Parameter(control[50], 0.79)"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "#inspect" do
|
214
|
+
it "includes the #type, #number, and #value.to_s" do
|
215
|
+
PARAMETER.from_midi([:control_change,0], 50, 100).inspect.should == "Parameter(control[50], #{100/127.0})"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|