jmtk 0.0.3.3-java
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.
- 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
|
+
|