mtk 0.0.3.2 → 0.0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/.yardopts +2 -2
  2. data/DEVELOPMENT_NOTES.md +20 -0
  3. data/README.md +9 -3
  4. data/Rakefile +47 -13
  5. data/bin/mtk +55 -20
  6. data/examples/crescendo.rb +4 -4
  7. data/examples/{drum_pattern1.rb → drum_pattern.rb} +8 -8
  8. data/examples/dynamic_pattern.rb +5 -5
  9. data/examples/gets_and_play.rb +3 -2
  10. data/examples/notation.rb +3 -3
  11. data/examples/play_midi.rb +4 -4
  12. data/examples/print_midi.rb +2 -2
  13. data/examples/random_tone_row.rb +3 -3
  14. data/examples/syntax_to_midi.rb +2 -2
  15. data/examples/test_output.rb +4 -5
  16. data/examples/tone_row_melody.rb +7 -5
  17. data/lib/mtk/core/duration.rb +213 -0
  18. data/lib/mtk/core/intensity.rb +158 -0
  19. data/lib/mtk/core/interval.rb +157 -0
  20. data/lib/mtk/core/pitch.rb +154 -0
  21. data/lib/mtk/core/pitch_class.rb +194 -0
  22. data/lib/mtk/events/event.rb +4 -4
  23. data/lib/mtk/events/note.rb +12 -12
  24. data/lib/mtk/events/timeline.rb +232 -0
  25. data/lib/mtk/groups/chord.rb +56 -0
  26. data/lib/mtk/{helpers → groups}/collection.rb +33 -1
  27. data/lib/mtk/groups/melody.rb +96 -0
  28. data/lib/mtk/groups/pitch_class_set.rb +163 -0
  29. data/lib/mtk/{helpers → groups}/pitch_collection.rb +1 -1
  30. data/lib/mtk/{midi → io}/dls_synth_device.rb +3 -1
  31. data/lib/mtk/{midi → io}/dls_synth_output.rb +10 -10
  32. data/lib/mtk/{midi → io}/jsound_input.rb +2 -2
  33. data/lib/mtk/{midi → io}/jsound_output.rb +9 -9
  34. data/lib/mtk/{midi/file.rb → io/midi_file.rb} +13 -13
  35. data/lib/mtk/{midi/input.rb → io/midi_input.rb} +4 -4
  36. data/lib/mtk/{midi/output.rb → io/midi_output.rb} +8 -8
  37. data/lib/mtk/{helpers/lilypond.rb → io/notation.rb} +5 -5
  38. data/lib/mtk/{midi → io}/unimidi_input.rb +2 -2
  39. data/lib/mtk/{midi → io}/unimidi_output.rb +14 -9
  40. data/lib/mtk/{constants → lang}/durations.rb +11 -11
  41. data/lib/mtk/{constants → lang}/intensities.rb +11 -11
  42. data/lib/mtk/{constants → lang}/intervals.rb +17 -17
  43. data/lib/mtk/lang/mtk_grammar.citrus +9 -9
  44. data/lib/mtk/{constants → lang}/pitch_classes.rb +5 -5
  45. data/lib/mtk/{constants → lang}/pitches.rb +7 -7
  46. data/lib/mtk/{helpers → lang}/pseudo_constants.rb +1 -1
  47. data/lib/mtk/{variable.rb → lang/variable.rb} +1 -1
  48. data/lib/mtk/numeric_extensions.rb +40 -47
  49. data/lib/mtk/patterns/for_each.rb +1 -1
  50. data/lib/mtk/patterns/pattern.rb +3 -3
  51. data/lib/mtk/sequencers/event_builder.rb +16 -15
  52. data/lib/mtk/sequencers/legato_sequencer.rb +1 -1
  53. data/lib/mtk/sequencers/rhythmic_sequencer.rb +1 -1
  54. data/lib/mtk/sequencers/sequencer.rb +8 -8
  55. data/lib/mtk/sequencers/step_sequencer.rb +2 -2
  56. data/lib/mtk.rb +33 -39
  57. data/spec/mtk/{duration_spec.rb → core/duration_spec.rb} +3 -3
  58. data/spec/mtk/{intensity_spec.rb → core/intensity_spec.rb} +3 -3
  59. data/spec/mtk/{interval_spec.rb → core/interval_spec.rb} +1 -1
  60. data/spec/mtk/{pitch_class_spec.rb → core/pitch_class_spec.rb} +1 -1
  61. data/spec/mtk/{pitch_spec.rb → core/pitch_spec.rb} +8 -8
  62. data/spec/mtk/events/event_spec.rb +4 -4
  63. data/spec/mtk/events/note_spec.rb +8 -8
  64. data/spec/mtk/{timeline_spec.rb → events/timeline_spec.rb} +47 -47
  65. data/spec/mtk/{chord_spec.rb → groups/chord_spec.rb} +18 -16
  66. data/spec/mtk/{helpers → groups}/collection_spec.rb +3 -3
  67. data/spec/mtk/{melody_spec.rb → groups/melody_spec.rb} +36 -34
  68. data/spec/mtk/{pitch_class_set_spec.rb → groups/pitch_class_set_spec.rb} +57 -55
  69. data/spec/mtk/{midi/file_spec.rb → io/midi_file_spec.rb} +17 -17
  70. data/spec/mtk/{midi/output_spec.rb → io/midi_output_spec.rb} +6 -6
  71. data/spec/mtk/{constants → lang}/durations_spec.rb +1 -1
  72. data/spec/mtk/{constants → lang}/intensities_spec.rb +1 -1
  73. data/spec/mtk/{constants → lang}/intervals_spec.rb +1 -1
  74. data/spec/mtk/lang/parser_spec.rb +12 -6
  75. data/spec/mtk/{constants → lang}/pitch_classes_spec.rb +1 -1
  76. data/spec/mtk/{constants → lang}/pitches_spec.rb +1 -1
  77. data/spec/mtk/{helpers → lang}/pseudo_constants_spec.rb +2 -2
  78. data/spec/mtk/{variable_spec.rb → lang/variable_spec.rb} +4 -4
  79. data/spec/mtk/numeric_extensions_spec.rb +35 -55
  80. data/spec/mtk/patterns/for_each_spec.rb +1 -1
  81. data/spec/mtk/patterns/sequence_spec.rb +1 -1
  82. data/spec/mtk/sequencers/legato_sequencer_spec.rb +2 -2
  83. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +4 -4
  84. data/spec/mtk/sequencers/step_sequencer_spec.rb +5 -5
  85. data/spec/spec_helper.rb +7 -6
  86. metadata +75 -61
  87. data/ext/mkrf_conf.rb +0 -25
  88. data/lib/mtk/chord.rb +0 -55
  89. data/lib/mtk/duration.rb +0 -211
  90. data/lib/mtk/helpers/convert.rb +0 -36
  91. data/lib/mtk/helpers/output_selector.rb +0 -67
  92. data/lib/mtk/intensity.rb +0 -156
  93. data/lib/mtk/interval.rb +0 -155
  94. data/lib/mtk/melody.rb +0 -94
  95. data/lib/mtk/pitch.rb +0 -152
  96. data/lib/mtk/pitch_class.rb +0 -192
  97. data/lib/mtk/pitch_class_set.rb +0 -161
  98. data/lib/mtk/timeline.rb +0 -230
  99. data/spec/mtk/midi/jsound_input_spec.rb +0 -11
  100. data/spec/mtk/midi/jsound_output_spec.rb +0 -11
  101. data/spec/mtk/midi/unimidi_input_spec.rb +0 -11
  102. data/spec/mtk/midi/unimidi_output_spec.rb +0 -11
@@ -0,0 +1,96 @@
1
+ module MTK
2
+ module Groups
3
+
4
+ # An ordered collection of {Pitch}es.
5
+ #
6
+ # The "horizontal" (sequential) pitch collection.
7
+ #
8
+ # Unlike the strict definition of melody, this class is fairly abstract and only models a succession of pitches.
9
+ # To create a true, playable melody one must combine an MTK::Melody and rhythms into a {Events::Timeline}.
10
+ #
11
+ # @see Chord
12
+ #
13
+ class Melody
14
+ include PitchCollection
15
+
16
+ attr_reader :pitches
17
+
18
+ # @param pitches [#to_a] the collection of pitches
19
+ # @see MTK#Melody
20
+ #
21
+ def initialize(pitches)
22
+ @pitches = pitches.to_a.clone.freeze
23
+ end
24
+
25
+ def self.from_pitch_classes(pitch_classes, start=MTK::Lang::Pitches::C4, max_distance=12)
26
+ pitch = start
27
+ pitches = []
28
+ pitch_classes.each do |pitch_class|
29
+ pitch = pitch.nearest(pitch_class)
30
+ pitch -= 12 if pitch > start+max_distance # keep within max_distance of start (default is one octave)
31
+ pitch += 12 if pitch < start-max_distance
32
+ pitches << pitch
33
+ end
34
+ new pitches
35
+ end
36
+
37
+ # @see Helper::Collection
38
+ def elements
39
+ @pitches
40
+ end
41
+
42
+ # Convert to an Array of pitches.
43
+ # @note this returns a mutable copy the underlying @pitches attribute, which is otherwise unmutable
44
+ alias :to_pitches :to_a
45
+
46
+ def self.from_a enumerable
47
+ new enumerable
48
+ end
49
+
50
+ def to_pitch_class_set(remove_duplicates=true)
51
+ PitchClassSet.new(remove_duplicates ? pitch_classes.uniq : pitch_classes)
52
+ end
53
+
54
+ def pitch_classes
55
+ @pitch_classes ||= @pitches.map{|p| p.pitch_class }
56
+ end
57
+
58
+ # @param other [#pitches, Enumerable]
59
+ def == other
60
+ if other.respond_to? :pitches
61
+ @pitches == other.pitches
62
+ elsif other.is_a? Enumerable
63
+ @pitches == other.to_a
64
+ else
65
+ @pitches == other
66
+ end
67
+ end
68
+
69
+ # Compare for equality, ignoring order and duplicates
70
+ # @param other [#pitches, Array, #to_a]
71
+ def =~ other
72
+ @normalized_pitches ||= @pitches.uniq.sort
73
+ @normalized_pitches == case
74
+ when other.respond_to?(:pitches) then other.pitches.uniq.sort
75
+ when (other.is_a? Array and other.frozen?) then other
76
+ when other.respond_to?(:to_a) then other.to_a.uniq.sort
77
+ else other
78
+ end
79
+ end
80
+
81
+ def to_s
82
+ '[' + @pitches.map{|pitch| pitch.to_s}.join(', ') + ']'
83
+ end
84
+
85
+ end
86
+ end
87
+
88
+ # Construct an ordered {MTK::Groups::Melody} that allows duplicates
89
+ # @see #MTK::Groups::Melody
90
+ # @see #MTK::Groups::Chord
91
+ def Melody(*anything)
92
+ MTK::Groups::Melody.new MTK::Groups.to_pitches(*anything)
93
+ end
94
+ module_function :Melody
95
+
96
+ end
@@ -0,0 +1,163 @@
1
+ module MTK
2
+
3
+ module Groups
4
+
5
+ # An ordered collection of {PitchClass}es.
6
+ #
7
+ # Unlike a mathematical Set, a PitchClassSet is ordered and may contain duplicates.
8
+ #
9
+ # @see MTK::Groups::Melody
10
+ # @see MTK::Groups::Chord
11
+ #
12
+ class PitchClassSet
13
+ include PitchCollection
14
+
15
+ attr_reader :pitch_classes
16
+
17
+ def self.random_row
18
+ new(MTK::Lang::PitchClasses::PITCH_CLASSES.shuffle)
19
+ end
20
+
21
+ def self.all
22
+ @all ||= new(MTK::Lang::PitchClasses::PITCH_CLASSES)
23
+ end
24
+
25
+ # @param pitch_classes [#to_a] the collection of pitch classes
26
+ #
27
+ # @see MTK#PitchClassSet
28
+ #
29
+ def initialize(pitch_classes)
30
+ @pitch_classes = pitch_classes.to_a.clone.freeze
31
+ end
32
+
33
+ # @see Helper::Collection
34
+ def elements
35
+ @pitch_classes
36
+ end
37
+
38
+ # Convert to an Array of pitch_classes.
39
+ # @note this returns a mutable copy the underlying @pitch_classes attribute, which is otherwise unmutable
40
+ alias :to_pitch_classes :to_a
41
+
42
+ def self.from_a enumerable
43
+ new enumerable
44
+ end
45
+
46
+ def normal_order
47
+ ordering = Array.new(@pitch_classes.uniq.sort)
48
+ min_span, start_index_for_normal_order = nil, nil
49
+
50
+ # check every rotation for the minimal span:
51
+ size.times do |index|
52
+ span = self.class.span_between ordering.first, ordering.last
53
+
54
+ if min_span.nil? or span < min_span
55
+ # best so far
56
+ min_span = span
57
+ start_index_for_normal_order = index
58
+
59
+ elsif span == min_span
60
+ # handle ties, minimize distance between first and second-to-last note, then first and third-to-last, etc
61
+ span1, span2 = nil, nil
62
+ tie_breaker = 1
63
+ while span1 == span2 and tie_breaker < size
64
+ span1 = self.class.span_between( ordering[0], ordering[-1 - tie_breaker] )
65
+ span2 = self.class.span_between( ordering[start_index_for_normal_order], ordering[start_index_for_normal_order - tie_breaker] )
66
+ tie_breaker -= 1
67
+ end
68
+ if span1 != span2
69
+ # tie cannot be broken, pick the one starting with the lowest pitch class
70
+ if ordering[0].to_i < ordering[start_index_for_normal_order].to_i
71
+ start_index_for_normal_order = index
72
+ end
73
+ elsif span1 < span2
74
+ start_index_for_normal_order = index
75
+ end
76
+
77
+ end
78
+ ordering << ordering.shift # rotate
79
+ end
80
+
81
+ # we've rotated all the way around, so we now need to rotate back to the start index we just found:
82
+ start_index_for_normal_order.times{ ordering << ordering.shift }
83
+
84
+ ordering
85
+ end
86
+
87
+ def normal_form
88
+ norder = normal_order
89
+ first_pc_val = norder.first.to_i
90
+ norder.map{|pitch_class| (pitch_class.to_i - first_pc_val) % 12 }
91
+ end
92
+
93
+ # the collection of elements present in both sets
94
+ def intersection(other)
95
+ self.class.from_a(to_a & other.to_a)
96
+ end
97
+
98
+ # the collection of all elements present in either set
99
+ def union(other)
100
+ self.class.from_a(to_a | other.to_a)
101
+ end
102
+
103
+ # the collection of elements from this set with any elements from the other set removed
104
+ def difference(other)
105
+ self.class.from_a(to_a - other.to_a)
106
+ end
107
+
108
+ # the collection of elements that are members of exactly one of the sets
109
+ def symmetric_difference(other)
110
+ union(other).difference( intersection(other) )
111
+ end
112
+
113
+ # the collection of elements that are not members of this set
114
+ def complement
115
+ self.class.all.difference(self)
116
+ end
117
+
118
+ # @param other [#pitch_classes, #to_a, Array]
119
+ def == other
120
+ if other.respond_to? :pitch_classes
121
+ @pitch_classes == other.pitch_classes
122
+ elsif other.respond_to? :to_a
123
+ @pitch_classes == other.to_a
124
+ else
125
+ @pitch_classes == other
126
+ end
127
+ end
128
+
129
+ # Compare for equality, ignoring order and duplicates
130
+ # @param other [#pitch_classes, Array, #to_a]
131
+ def =~ other
132
+ @normalized_pitch_classes ||= @pitch_classes.uniq.sort
133
+ @normalized_pitch_classes == case
134
+ when other.respond_to?(:pitch_classes) then other.pitch_classes.uniq.sort
135
+ when (other.is_a? Array and other.frozen?) then other
136
+ when other.respond_to?(:to_a) then other.to_a.uniq.sort
137
+ else other
138
+ end
139
+ end
140
+
141
+ def to_s
142
+ @pitch_classes.join(' ')
143
+ end
144
+
145
+ def inspect
146
+ @pitch_classes.inspect
147
+ end
148
+
149
+ def self.span_between(pc1, pc2)
150
+ (pc2.to_i - pc1.to_i) % 12
151
+ end
152
+
153
+ end
154
+ end
155
+
156
+ # Construct a {Groups::PitchClassSet}
157
+ # @see Groups::PitchClassSet#initialize
158
+ def PitchClassSet(*anything)
159
+ MTK::Groups::PitchClassSet.new MTK::Groups.to_pitch_classes(*anything)
160
+ end
161
+ module_function :PitchClassSet
162
+
163
+ end
@@ -1,5 +1,5 @@
1
1
  module MTK
2
- module Helpers
2
+ module Groups
3
3
 
4
4
  # An extension to {Collection}, which provides additional transformations for pitch-like collections.
5
5
  #
@@ -1,16 +1,18 @@
1
1
  require 'ffi'
2
2
 
3
3
  module MTK
4
- module MIDI
4
+ module IO
5
5
 
6
6
  # An output device for Apple's built-in "DLS" synthesizer on OS X
7
7
  class DLSSynthDevice
8
8
 
9
+ # @private
9
10
  module AudioToolbox
10
11
  extend FFI::Library
11
12
  ffi_lib '/System/Library/Frameworks/AudioToolbox.framework/Versions/Current/AudioToolbox'
12
13
  ffi_lib '/System/Library/Frameworks/AudioUnit.framework/Versions/Current/AudioUnit'
13
14
 
15
+ # @private
14
16
  class ComponentDescription < FFI::Struct
15
17
  layout :componentType, :int,
16
18
  :componentSubType, :int,
@@ -1,12 +1,12 @@
1
- require 'mtk/midi/dls_synth_device'
1
+ require 'mtk/io/dls_synth_device'
2
2
 
3
3
  module MTK
4
- module MIDI
4
+ module IO
5
5
 
6
6
  # Provides realtime MIDI output on OS X to the built-in "DLS" Synthesizer
7
7
  # @note This class is optional and only available if you require 'mtk/midi/dls_synth_output'.
8
8
  # It depends on the 'gamelan' gem.
9
- class DLSSynthOutput < Output
9
+ class DLSSynthOutput < MIDIOutput
10
10
 
11
11
  public_class_method :new
12
12
 
@@ -22,37 +22,37 @@ module MTK
22
22
  ######################
23
23
  protected
24
24
 
25
- # (see Output#note_on)
25
+ # (see MIDIOutput#note_on)
26
26
  def note_on(pitch, velocity, channel)
27
27
  @device.message(0x90|channel, pitch, velocity)
28
28
  end
29
29
 
30
- # (see Output#note_off)
30
+ # (see MIDIOutput#note_off)
31
31
  def note_off(pitch, velocity, channel)
32
32
  @device.message(0x80|channel, pitch, velocity)
33
33
  end
34
34
 
35
- # (see Output#control)
35
+ # (see MIDIOutput#control)
36
36
  def control(number, midi_value, channel)
37
37
  @device.message(0xB0|channel, number, midi_value)
38
38
  end
39
39
 
40
- # (see Output#channel_pressure)
40
+ # (see MIDIOutput#channel_pressure)
41
41
  def channel_pressure(midi_value, channel)
42
42
  @device.message(0xD0|channel, midi_value, 0)
43
43
  end
44
44
 
45
- # (see Output#poly_pressure)
45
+ # (see MIDIOutput#poly_pressure)
46
46
  def poly_pressure(pitch, midi_value, channel)
47
47
  @device.message(0xA0|channel, pitch, midi_value)
48
48
  end
49
49
 
50
- # (see Output#bend)
50
+ # (see MIDIOutput#bend)
51
51
  def bend(midi_value, channel)
52
52
  @device.message(0xE0|channel, midi_value & 127, (midi_value >> 7) & 127)
53
53
  end
54
54
 
55
- # (see Output#program)
55
+ # (see MIDIOutput#program)
56
56
  def program(number, channel)
57
57
  @device.message(0xC0|channel, number, 0)
58
58
  end
@@ -1,12 +1,12 @@
1
1
  require 'jsound'
2
2
 
3
3
  module MTK
4
- module MIDI
4
+ module IO
5
5
 
6
6
  # Provides realtime MIDI input for JRuby via the jsound gem.
7
7
  # @note This class is optional and only available if you require 'mtk/midi/jsound_input'.
8
8
  # It depends on the 'jsound' gem.
9
- class JSoundInput < Input
9
+ class JSoundInput < MIDIInput
10
10
 
11
11
  public_class_method :new
12
12
 
@@ -1,12 +1,12 @@
1
1
  require 'jsound'
2
2
 
3
3
  module MTK
4
- module MIDI
4
+ module IO
5
5
 
6
6
  # Provides realtime MIDI output for JRuby via the jsound and gamelan gems.
7
7
  # @note This class is optional and only available if you require 'mtk/midi/jsound_output'.
8
8
  # It depends on the 'jsound' and 'gamelan' gems.
9
- class JSoundOutput < Output
9
+ class JSoundOutput < MIDIOutput
10
10
 
11
11
  public_class_method :new
12
12
 
@@ -41,37 +41,37 @@ module MTK
41
41
  ######################
42
42
  protected
43
43
 
44
- # (see Output#note_on)
44
+ # (see MIDIOutput#note_on)
45
45
  def note_on(pitch, velocity, channel)
46
46
  @generator.note_on(pitch, velocity, channel)
47
47
  end
48
48
 
49
- # (see Output#note_off)
49
+ # (see MIDIOutput#note_off)
50
50
  def note_off(pitch, velocity, channel)
51
51
  @generator.note_off(pitch, velocity, channel)
52
52
  end
53
53
 
54
- # (see Output#control)
54
+ # (see MIDIOutput#control)
55
55
  def control(number, midi_value, channel)
56
56
  @generator.control_change(number, midi_value, channel)
57
57
  end
58
58
 
59
- # (see Output#channel_pressure)
59
+ # (see MIDIOutput#channel_pressure)
60
60
  def channel_pressure(midi_value, channel)
61
61
  @generator.channel_pressure(midi_value, channel)
62
62
  end
63
63
 
64
- # (see Output#poly_pressure)
64
+ # (see MIDIOutput#poly_pressure)
65
65
  def poly_pressure(pitch, midi_value, channel)
66
66
  @generator.poly_pressure(pitch, midi_value, channel)
67
67
  end
68
68
 
69
- # (see Output#bend)
69
+ # (see MIDIOutput#bend)
70
70
  def bend(midi_value, channel)
71
71
  @generator.pitch_bend(midi_value, channel)
72
72
  end
73
73
 
74
- # (see Output#program)
74
+ # (see MIDIOutput#program)
75
75
  def program(number, channel)
76
76
  @generator.program_change(number, channel)
77
77
  end
@@ -1,12 +1,12 @@
1
1
  require 'midilib'
2
2
 
3
3
  module MTK
4
- module MIDI
4
+ module IO
5
5
 
6
- # MIDI file I/O: reads MIDI files into {Timeline}s and writes {Timeline}s to MIDI files.
6
+ # MIDI file I/O: reads MIDI files into {Events::Timeline}s and writes {Events::Timeline}s to MIDI files.
7
7
  # @note This class is optional and only available if you require 'mtk/midi/file'.
8
8
  # It depends on the 'midilib' gem.
9
- class File
9
+ class MIDIFile
10
10
  def initialize file
11
11
  if file.respond_to? :path
12
12
  @file = file.path
@@ -15,7 +15,7 @@ module MTK
15
15
  end
16
16
  end
17
17
 
18
- # Read a MIDI file into an Array of {Timeline}s
18
+ # Read a MIDI file into an Array of {Events::Timeline}s
19
19
  #
20
20
  # @return [Timeline]
21
21
  #
@@ -30,7 +30,7 @@ module MTK
30
30
 
31
31
  sequence.each do |track|
32
32
  track_idx += 1
33
- timeline = Timeline.new
33
+ timeline = MTK::Events::Timeline.new
34
34
  note_ons = {}
35
35
  #puts "TRACK #{track_idx}"
36
36
 
@@ -46,7 +46,7 @@ module MTK
46
46
  on_time,on_event = note_ons.delete(event.note)
47
47
  if on_event
48
48
  duration = time - on_time
49
- note = MTK::Events::Note.from_midi(event.note, on_event.velocity, duration)
49
+ note = MTK::Events::Note.from_midi(event.note, on_event.velocity, duration, event.channel)
50
50
  timeline.add on_time, note
51
51
  end
52
52
 
@@ -66,7 +66,7 @@ module MTK
66
66
 
67
67
  def write(anything)
68
68
  case anything
69
- when Timeline then write_timeline(anything)
69
+ when MTK::Events::Timeline then write_timeline(anything)
70
70
  when Enumerable then write_timelines(anything)
71
71
  else raise "#{self.class}#write doesn't understand #{anything.class}"
72
72
  end
@@ -92,7 +92,7 @@ module MTK
92
92
  events.each do |event|
93
93
  next if event.rest?
94
94
 
95
- channel = event.channel || 0
95
+ channel = (event.channel || 1) - 1 # midilib seems to count channels from 0, hence the -1
96
96
 
97
97
  case event.type
98
98
  when :note
@@ -132,7 +132,7 @@ module MTK
132
132
  private
133
133
 
134
134
  def write_to_disk(sequence)
135
- puts "Writing file #{@file}"
135
+ puts "Writing file #{@file}" unless $__RUNNING_RSPEC_TESTS__
136
136
  ::File.open(@file, 'wb') { |f| sequence.write f }
137
137
  end
138
138
 
@@ -198,12 +198,12 @@ module MTK
198
198
  end
199
199
  end
200
200
 
201
- # Shortcut for MTK::MIDI::File.new
201
+ # Shortcut for MTK::IO::MIDIFile.new
202
202
  # @note Only available if you require 'mtk/midi/file'
203
- def MIDI_File(f)
204
- MIDI::File.new(f)
203
+ def MIDIFile(f)
204
+ ::MTK::IO::MIDIFile.new(f)
205
205
  end
206
- module_function :MIDI_File
206
+ module_function :MIDIFile
207
207
 
208
208
  end
209
209
 
@@ -1,9 +1,9 @@
1
1
  module MTK
2
- module MIDI
2
+ module IO
3
3
 
4
4
  # Common behavior for realtime MIDI input.
5
5
  #
6
- class Input
6
+ class MIDIInput
7
7
 
8
8
  class << self
9
9
 
@@ -90,8 +90,8 @@ end
90
90
 
91
91
  unless $__RUNNING_RSPEC_TESTS__ # I can't get this working on Travis-CI, problem installing native dependencies
92
92
  if RUBY_PLATFORM == 'java'
93
- require 'mtk/midi/jsound_input'
93
+ require 'mtk/io/jsound_input'
94
94
  else
95
- require 'mtk/midi/unimidi_input'
95
+ require 'mtk/io/unimidi_input'
96
96
  end
97
97
  end
@@ -2,13 +2,13 @@ require 'rbconfig'
2
2
  require 'gamelan'
3
3
 
4
4
  module MTK
5
- module MIDI
5
+ module IO
6
6
 
7
7
  # Provides a scheduler and common behavior for realtime MIDI output, using the gamelan gem for scheduling.
8
8
  #
9
9
  # @abstract Subclasses must provide {.devices}, {.devices_by_name}, {#note_on}, {#note_off}, {#control}, {#channel_pressure}, {#poly_pressure}, {#bend}, and {#program} to implement a MIDI output.
10
10
  #
11
- class Output
11
+ class MIDIOutput
12
12
 
13
13
  class << self
14
14
 
@@ -80,9 +80,9 @@ module MTK
80
80
 
81
81
  def play(anything, options={})
82
82
  timeline = case anything
83
- when MTK::Timeline then anything
84
- when Hash then MTK::Timeline.from_hash anything
85
- when Enumerable,MTK::Events::Event then MTK::Timeline.from_hash(0 => anything)
83
+ when MTK::Events::Timeline then anything
84
+ when Hash then MTK::Events::Timeline.from_h anything
85
+ when Enumerable,MTK::Events::Event then MTK::Events::Timeline.from_h(0 => anything)
86
86
  else raise "#{self.class}.play() doesn't understand #{anything} (#{anything.class})"
87
87
  end
88
88
  timeline = timeline.flatten
@@ -184,12 +184,12 @@ end
184
184
  unless $__RUNNING_RSPEC_TESTS__ # I can't get this working on Travis-CI, problem installing native dependencies
185
185
  if RbConfig::CONFIG['host_os'] =~ /darwin/
186
186
  # We're running on OS X
187
- require 'mtk/midi/dls_synth_output'
187
+ require 'mtk/io/dls_synth_output'
188
188
  end
189
189
 
190
190
  if RUBY_PLATFORM == 'java'
191
- require 'mtk/midi/jsound_output'
191
+ require 'mtk/io/jsound_output'
192
192
  else
193
- require 'mtk/midi/unimidi_output'
193
+ require 'mtk/io/unimidi_output'
194
194
  end
195
195
  end
@@ -3,14 +3,14 @@ require 'tmpdir'
3
3
  require 'fileutils'
4
4
 
5
5
  module MTK
6
- module Helpers
6
+ module IO
7
7
 
8
- # Uses {Timeline}s to generates music notation graphics with {http://lilypond.org/ Lilypond}.
9
- # @note This class is optional and only available if you require 'mtk/helpers/lilypond'.
8
+ # Uses {Events::Timeline}s to generates music notation graphics with {http://lilypond.org/ Lilypond}.
9
+ # @note This class is optional and only available if you require 'mtk/io/lilypond'.
10
10
  # @note To make notation graphics, {http://lilypond.org/download.html Lilypond} must be installed
11
11
  # and you must follow the "Running on the command-line" instructions (found on the download page for
12
12
  # your operating system). If the lilypond command is not on your PATH, set the environment variable LILYPOND_PATH
13
- class Lilypond
13
+ class Notation
14
14
 
15
15
  LILYPOND_PATH = ENV['LILYPOND_PATH'] || 'lilypond'
16
16
 
@@ -150,7 +150,7 @@ module MTK
150
150
 
151
151
  def syntax_for_duration(duration)
152
152
  # TODO: handle dots, triplets, and ties of arbitrary durations
153
- duration = MTK::Timeline.quantize_time(duration.to_f.abs, QUANTIZATION_INTERVAL)
153
+ duration = MTK::Events::Timeline.quantize_time(duration.to_f.abs, QUANTIZATION_INTERVAL)
154
154
  syntax = (4.0/duration).round
155
155
  syntax = 1 if syntax < 1
156
156
  syntax.to_s
@@ -2,12 +2,12 @@ require 'unimidi'
2
2
  require 'ostruct'
3
3
 
4
4
  module MTK
5
- module MIDI
5
+ module IO
6
6
 
7
7
  # Provides realtime MIDI input for MRI/YARV Ruby via the unimidi gem.
8
8
  # @note This class is optional and only available if you require 'mtk/midi/unimidi_input'.
9
9
  # It depends on the 'unimidi' gem.
10
- class UniMIDIInput < Input
10
+ class UniMIDIInput < MIDIInput
11
11
 
12
12
  public_class_method :new
13
13