mtk 0.0.3.2 → 0.0.3.3

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 (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