mtk 0.0.2 → 0.0.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 (147) hide show
  1. data/.yardopts +3 -2
  2. data/DEVELOPMENT_NOTES.md +114 -0
  3. data/INTRO.md +64 -8
  4. data/LICENSE.txt +1 -1
  5. data/README.md +31 -102
  6. data/Rakefile +56 -18
  7. data/bin/mtk +215 -0
  8. data/examples/crescendo.rb +5 -5
  9. data/examples/drum_pattern1.rb +23 -0
  10. data/examples/dynamic_pattern.rb +8 -11
  11. data/examples/gets_and_play.rb +26 -0
  12. data/examples/notation.rb +22 -0
  13. data/examples/play_midi.rb +8 -10
  14. data/examples/random_tone_row.rb +2 -2
  15. data/examples/syntax_to_midi.rb +28 -0
  16. data/examples/test_output.rb +8 -0
  17. data/examples/tone_row_melody.rb +6 -6
  18. data/lib/mtk.rb +52 -40
  19. data/lib/mtk/chord.rb +55 -0
  20. data/lib/mtk/constants/durations.rb +57 -0
  21. data/lib/mtk/constants/intensities.rb +61 -0
  22. data/lib/mtk/constants/intervals.rb +73 -0
  23. data/lib/mtk/constants/pitch_classes.rb +29 -0
  24. data/lib/mtk/constants/pitches.rb +52 -0
  25. data/lib/mtk/duration.rb +211 -0
  26. data/lib/mtk/events/event.rb +119 -0
  27. data/lib/mtk/events/note.rb +112 -0
  28. data/lib/mtk/events/parameter.rb +54 -0
  29. data/lib/mtk/helpers/collection.rb +164 -0
  30. data/lib/mtk/helpers/convert.rb +36 -0
  31. data/lib/mtk/helpers/lilypond.rb +162 -0
  32. data/lib/mtk/helpers/output_selector.rb +67 -0
  33. data/lib/mtk/helpers/pitch_collection.rb +23 -0
  34. data/lib/mtk/helpers/pseudo_constants.rb +26 -0
  35. data/lib/mtk/intensity.rb +156 -0
  36. data/lib/mtk/interval.rb +155 -0
  37. data/lib/mtk/lang/mtk_grammar.citrus +190 -13
  38. data/lib/mtk/lang/parser.rb +29 -0
  39. data/lib/mtk/melody.rb +94 -0
  40. data/lib/mtk/midi/dls_synth_device.rb +144 -0
  41. data/lib/mtk/midi/dls_synth_output.rb +62 -0
  42. data/lib/mtk/midi/file.rb +67 -32
  43. data/lib/mtk/midi/input.rb +97 -0
  44. data/lib/mtk/midi/jsound_input.rb +36 -17
  45. data/lib/mtk/midi/jsound_output.rb +48 -46
  46. data/lib/mtk/midi/output.rb +195 -0
  47. data/lib/mtk/midi/unimidi_input.rb +117 -0
  48. data/lib/mtk/midi/unimidi_output.rb +121 -0
  49. data/lib/mtk/{_numeric_extensions.rb → numeric_extensions.rb} +12 -0
  50. data/lib/mtk/patterns/chain.rb +49 -0
  51. data/lib/mtk/{pattern → patterns}/choice.rb +14 -8
  52. data/lib/mtk/patterns/cycle.rb +18 -0
  53. data/lib/mtk/patterns/for_each.rb +71 -0
  54. data/lib/mtk/patterns/function.rb +39 -0
  55. data/lib/mtk/{pattern → patterns}/lines.rb +11 -17
  56. data/lib/mtk/{pattern → patterns}/palindrome.rb +11 -8
  57. data/lib/mtk/patterns/pattern.rb +171 -0
  58. data/lib/mtk/patterns/sequence.rb +20 -0
  59. data/lib/mtk/pitch.rb +7 -6
  60. data/lib/mtk/pitch_class.rb +124 -46
  61. data/lib/mtk/pitch_class_set.rb +58 -35
  62. data/lib/mtk/sequencers/event_builder.rb +131 -0
  63. data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
  64. data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
  65. data/lib/mtk/{sequencer/abstract_sequencer.rb → sequencers/sequencer.rb} +37 -11
  66. data/lib/mtk/{sequencer → sequencers}/step_sequencer.rb +4 -4
  67. data/lib/mtk/timeline.rb +39 -22
  68. data/lib/mtk/variable.rb +32 -0
  69. data/spec/mtk/chord_spec.rb +83 -0
  70. data/spec/mtk/{_constants → constants}/durations_spec.rb +12 -41
  71. data/spec/mtk/{_constants → constants}/intensities_spec.rb +13 -37
  72. data/spec/mtk/{_constants → constants}/intervals_spec.rb +14 -32
  73. data/spec/mtk/{_constants → constants}/pitch_classes_spec.rb +8 -4
  74. data/spec/mtk/{_constants → constants}/pitches_spec.rb +5 -1
  75. data/spec/mtk/duration_spec.rb +372 -0
  76. data/spec/mtk/events/event_spec.rb +234 -0
  77. data/spec/mtk/events/note_spec.rb +174 -0
  78. data/spec/mtk/events/parameter_spec.rb +220 -0
  79. data/spec/mtk/{helper → helpers}/collection_spec.rb +86 -3
  80. data/spec/mtk/{helper → helpers}/pseudo_constants_spec.rb +2 -2
  81. data/spec/mtk/intensity_spec.rb +289 -0
  82. data/spec/mtk/interval_spec.rb +265 -0
  83. data/spec/mtk/lang/parser_spec.rb +597 -0
  84. data/spec/mtk/melody_spec.rb +223 -0
  85. data/spec/mtk/midi/file_spec.rb +16 -16
  86. data/spec/mtk/midi/jsound_input_spec.rb +11 -0
  87. data/spec/mtk/midi/jsound_output_spec.rb +11 -0
  88. data/spec/mtk/midi/output_spec.rb +102 -0
  89. data/spec/mtk/midi/unimidi_input_spec.rb +11 -0
  90. data/spec/mtk/midi/unimidi_output_spec.rb +11 -0
  91. data/spec/mtk/{_numeric_extensions_spec.rb → numeric_extensions_spec.rb} +1 -0
  92. data/spec/mtk/patterns/chain_spec.rb +110 -0
  93. data/spec/mtk/{pattern → patterns}/choice_spec.rb +20 -30
  94. data/spec/mtk/{pattern → patterns}/cycle_spec.rb +25 -35
  95. data/spec/mtk/patterns/for_each_spec.rb +136 -0
  96. data/spec/mtk/{pattern → patterns}/function_spec.rb +17 -30
  97. data/spec/mtk/{pattern → patterns}/lines_spec.rb +11 -27
  98. data/spec/mtk/{pattern → patterns}/palindrome_spec.rb +13 -29
  99. data/spec/mtk/patterns/pattern_spec.rb +132 -0
  100. data/spec/mtk/patterns/sequence_spec.rb +203 -0
  101. data/spec/mtk/pitch_class_set_spec.rb +23 -21
  102. data/spec/mtk/pitch_class_spec.rb +151 -39
  103. data/spec/mtk/pitch_spec.rb +22 -1
  104. data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
  105. data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
  106. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
  107. data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
  108. data/spec/mtk/{sequencer → sequencers}/step_sequencer_spec.rb +35 -13
  109. data/spec/mtk/timeline_spec.rb +109 -16
  110. data/spec/mtk/variable_spec.rb +52 -0
  111. data/spec/spec_coverage.rb +2 -0
  112. data/spec/spec_helper.rb +3 -0
  113. metadata +188 -91
  114. data/lib/mtk/_constants/durations.rb +0 -80
  115. data/lib/mtk/_constants/intensities.rb +0 -81
  116. data/lib/mtk/_constants/intervals.rb +0 -85
  117. data/lib/mtk/_constants/pitch_classes.rb +0 -35
  118. data/lib/mtk/_constants/pitches.rb +0 -49
  119. data/lib/mtk/event.rb +0 -70
  120. data/lib/mtk/helper/collection.rb +0 -114
  121. data/lib/mtk/helper/event_builder.rb +0 -85
  122. data/lib/mtk/helper/pseudo_constants.rb +0 -26
  123. data/lib/mtk/lang/grammar.rb +0 -17
  124. data/lib/mtk/note.rb +0 -63
  125. data/lib/mtk/pattern/abstract_pattern.rb +0 -132
  126. data/lib/mtk/pattern/cycle.rb +0 -51
  127. data/lib/mtk/pattern/enumerator.rb +0 -26
  128. data/lib/mtk/pattern/function.rb +0 -46
  129. data/lib/mtk/pattern/sequence.rb +0 -30
  130. data/lib/mtk/pitch_set.rb +0 -84
  131. data/lib/mtk/sequencer/rhythmic_sequencer.rb +0 -29
  132. data/lib/mtk/transform/invertible.rb +0 -15
  133. data/lib/mtk/transform/mappable.rb +0 -18
  134. data/lib/mtk/transform/set_theory_operations.rb +0 -34
  135. data/lib/mtk/transform/transposable.rb +0 -14
  136. data/spec/mtk/event_spec.rb +0 -139
  137. data/spec/mtk/helper/event_builder_spec.rb +0 -92
  138. data/spec/mtk/lang/grammar_spec.rb +0 -100
  139. data/spec/mtk/note_spec.rb +0 -115
  140. data/spec/mtk/pattern/abstract_pattern_spec.rb +0 -45
  141. data/spec/mtk/pattern/note_cycle_spec.rb.bak +0 -116
  142. data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +0 -47
  143. data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +0 -37
  144. data/spec/mtk/pattern/sequence_spec.rb +0 -151
  145. data/spec/mtk/pitch_set_spec.rb +0 -198
  146. data/spec/mtk/sequencer/abstract_sequencer_spec.rb +0 -159
  147. data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +0 -49
@@ -1,44 +1,53 @@
1
1
  module MTK
2
2
 
3
- # An ordered Set of PitchClasses, for 12-tone set-theory pitch analysis and manipulations
3
+ # An ordered collection of {PitchClass}es.
4
+ #
5
+ # Unlike a mathematical Set, a PitchClassSet is ordered and may contain duplicates.
6
+ #
7
+ # @see Melody
8
+ # @see Chord
4
9
  #
5
10
  class PitchClassSet
6
-
7
- include Helper::Collection
8
- include Transform::Mappable
9
- include Transform::Transposable
10
- include Transform::Invertible
11
- include Transform::SetTheoryOperations
11
+ include Helpers::PitchCollection
12
12
 
13
13
  attr_reader :pitch_classes
14
14
 
15
15
  def self.random_row
16
- new(PitchClasses::PITCH_CLASSES.shuffle)
16
+ new(Constants::PitchClasses::PITCH_CLASSES.shuffle)
17
17
  end
18
18
 
19
19
  def self.all
20
- @all ||= new(PitchClasses::PITCH_CLASSES)
20
+ @all ||= new(Constants::PitchClasses::PITCH_CLASSES)
21
21
  end
22
22
 
23
+ # @param pitch_classes [#to_a] the collection of pitch classes
24
+ #
25
+ # @see MTK#PitchClassSet
26
+ #
23
27
  def initialize(pitch_classes)
24
- @pitch_classes = pitch_classes.to_a.uniq.freeze
28
+ @pitch_classes = pitch_classes.to_a.clone.freeze
25
29
  end
26
30
 
31
+ # @see Helper::Collection
27
32
  def elements
28
33
  @pitch_classes
29
34
  end
30
35
 
36
+ # Convert to an Array of pitch_classes.
37
+ # @note this returns a mutable copy the underlying @pitch_classes attribute, which is otherwise unmutable
38
+ alias :to_pitch_classes :to_a
39
+
31
40
  def self.from_a enumerable
32
41
  new enumerable
33
42
  end
34
43
 
35
44
  def normal_order
36
- ordering = Array.new(@pitch_classes.sort)
45
+ ordering = Array.new(@pitch_classes.uniq.sort)
37
46
  min_span, start_index_for_normal_order = nil, nil
38
47
 
39
48
  # check every rotation for the minimal span:
40
49
  size.times do |index|
41
- span = self.class.span_for ordering
50
+ span = self.class.span_between ordering.first, ordering.last
42
51
 
43
52
  if min_span.nil? or span < min_span
44
53
  # best so far
@@ -79,6 +88,31 @@ module MTK
79
88
  norder.map{|pitch_class| (pitch_class.to_i - first_pc_val) % 12 }
80
89
  end
81
90
 
91
+ # the collection of elements present in both sets
92
+ def intersection(other)
93
+ self.class.from_a(to_a & other.to_a)
94
+ end
95
+
96
+ # the collection of all elements present in either set
97
+ def union(other)
98
+ self.class.from_a(to_a | other.to_a)
99
+ end
100
+
101
+ # the collection of elements from this set with any elements from the other set removed
102
+ def difference(other)
103
+ self.class.from_a(to_a - other.to_a)
104
+ end
105
+
106
+ # the collection of elements that are members of exactly one of the sets
107
+ def symmetric_difference(other)
108
+ union(other).difference( intersection(other) )
109
+ end
110
+
111
+ # the collection of elements that are not members of this set
112
+ def complement
113
+ self.class.all.difference(self)
114
+ end
115
+
82
116
  # @param other [#pitch_classes, #to_a, Array]
83
117
  def == other
84
118
  if other.respond_to? :pitch_classes
@@ -90,19 +124,15 @@ module MTK
90
124
  end
91
125
  end
92
126
 
93
- # Compare for equality, ignoring order
94
- # @param other [#pitch_classes, #to_a, #sort, Array]
127
+ # Compare for equality, ignoring order and duplicates
128
+ # @param other [#pitch_classes, Array, #to_a]
95
129
  def =~ other
96
- if other.is_a? Array and other.frozen?
97
- @pitch_classes.sort == other
98
- elsif other.respond_to? :pitch_classes
99
- @pitch_classes.sort == other.pitch_classes.sort
100
- elsif other.respond_to? :to_a
101
- @pitch_classes.sort == other.to_a.sort
102
- elsif other.respond_to? :sort
103
- @pitch_classes.sort == other.sort
104
- else
105
- @pitch_classes.sort == other
130
+ @normalized_pitch_classes ||= @pitch_classes.uniq.sort
131
+ @normalized_pitch_classes == case
132
+ when other.respond_to?(:pitch_classes) then other.pitch_classes.uniq.sort
133
+ when (other.is_a? Array and other.frozen?) then other
134
+ when other.respond_to?(:to_a) then other.to_a.uniq.sort
135
+ else other
106
136
  end
107
137
  end
108
138
 
@@ -114,24 +144,17 @@ module MTK
114
144
  @pitch_classes.inspect
115
145
  end
116
146
 
117
- def self.span_for(pitch_classes)
118
- span_between pitch_classes.first, pitch_classes.last
119
- end
120
-
121
147
  def self.span_between(pc1, pc2)
122
148
  (pc2.to_i - pc1.to_i) % 12
123
149
  end
124
150
 
125
151
  end
126
152
 
127
- # Construct a {PitchClassSet} from any supported type
153
+
154
+ # Construct a {PitchClassSet}
155
+ # @see PitchClassSet#initialize
128
156
  def PitchClassSet(*anything)
129
- anything = anything.first if anything.size == 1
130
- case anything
131
- when Array then PitchClassSet.new(anything.map{|elem| PitchClass(elem) })
132
- when PitchClassSet then anything
133
- else PitchClassSet.new([PitchClass(anything)])
134
- end
157
+ PitchClassSet.new Helpers::Convert.to_pitch_classes(*anything)
135
158
  end
136
159
  module_function :PitchClassSet
137
160
 
@@ -0,0 +1,131 @@
1
+ module MTK
2
+ module Sequencers
3
+
4
+ # A special pattern that takes a list of event properties and/or patterns and emits lists of {Events::Event}s
5
+ class EventBuilder
6
+
7
+ DEFAULT_PITCH = ::MTK::Constants::Pitches::C4
8
+ DEFAULT_DURATION = ::MTK::Constants::Durations::q
9
+ DEFAULT_INTENSITY = ::MTK::Constants::Intensities::o
10
+
11
+ def initialize(patterns, options={})
12
+ @patterns = patterns
13
+ @options = options
14
+ @default_pitch = if options.has_key? :default_pitch then MTK::Pitch( options[:default_pitch]) else DEFAULT_PITCH end
15
+ @default_duration = if options.has_key? :default_duration then MTK::Duration( options[:default_duration]) else DEFAULT_DURATION end
16
+ @default_intensity = if options.has_key? :default_intensity then MTK::Intensity(options[:default_intensity]) else DEFAULT_INTENSITY end
17
+ @max_interval = options.fetch(:max_interval, 127)
18
+ rewind
19
+ end
20
+
21
+ # Build a list of events from the next element in each {Patterns::Pattern}
22
+ # @return [Array] an array of events
23
+ def next
24
+ pitches = []
25
+ intensities = []
26
+ duration = nil
27
+
28
+ @patterns.each do |pattern|
29
+ pattern_value = pattern.next
30
+
31
+ elements = pattern_value.is_a?(Enumerable) ? pattern_value : [pattern_value]
32
+ elements.each do |element|
33
+ return nil if element.nil? or element == :skip
34
+
35
+ case element
36
+ when ::MTK::Pitch then pitches << element
37
+ when ::MTK::PitchClass then pitches += pitches_for_pitch_classes([element], @previous_pitch)
38
+ when ::MTK::PitchClassSet then pitches += pitches_for_pitch_classes(element, @previous_pitch)
39
+ when ::MTK::Helpers::PitchCollection then pitches += element.pitches # this must be after the PitchClassSet case, because that is also a PitchCollection
40
+
41
+ when ::MTK::Duration
42
+ duration ||= 0
43
+ duration += element
44
+
45
+ when ::MTK::Intensity
46
+ intensities << element
47
+
48
+ when ::MTK::Interval
49
+ if @previous_pitches
50
+ pitches += @previous_pitches.map{|pitch| pitch + element }
51
+ else
52
+ pitches << (@previous_pitch + element)
53
+ end
54
+
55
+ # TODO? String/Symbols for special behaviors like :skip, or :break (something like StopIteration for the current Pattern?)
56
+
57
+ else STDERR.puts "#{self.class}#next: Unexpected type '#{element.class}'"
58
+ end
59
+
60
+ end
61
+ end
62
+
63
+ pitches << @previous_pitch if pitches.empty?
64
+ duration ||= @previous_duration
65
+
66
+ if intensities.empty?
67
+ intensity = @previous_intensity
68
+ else
69
+ intensity = MTK::Intensity[intensities.map{|i| i.to_f }.inject(:+)/intensities.length] # average the intensities
70
+ end
71
+
72
+ # Not using this yet, maybe later...
73
+ # return nil if duration==:skip or intensities.include? :skip or pitches.include? :skip
74
+
75
+ constrain_pitch(pitches)
76
+
77
+ @previous_pitch = pitches.last # Consider doing something different, maybe averaging?
78
+ @previous_pitches = pitches.length > 1 ? pitches : nil
79
+ @previous_intensity = intensity
80
+ @previous_duration = duration
81
+
82
+ pitches.map{|pitch| MTK::Events::Note.new(pitch,duration,intensity) }
83
+ end
84
+
85
+ # Reset the EventBuilder to its initial state
86
+ def rewind
87
+ @previous_pitch = @default_pitch
88
+ @previous_pitches = [@default_pitch]
89
+ @previous_intensity = @default_intensity
90
+ @previous_duration = @default_duration
91
+ @max_pitch = nil
92
+ @min_pitch = nil
93
+ @patterns.each{|pattern| pattern.rewind if pattern.is_a? MTK::Patterns::Pattern }
94
+ end
95
+
96
+ ########################
97
+ private
98
+
99
+ def pitches_for_pitch_classes(pitch_classes, previous_pitch)
100
+ pitch_classes.map{|pitch_class| previous_pitch.nearest(pitch_class) }
101
+ end
102
+
103
+ def constrain_pitch(pitches)
104
+ if @max_pitch.nil? or @min_pitch.nil?
105
+ first_pitch = pitches.first
106
+
107
+ @max_pitch = first_pitch + @max_interval
108
+ @max_pitch = 127 if @max_pitch > 127
109
+
110
+ @min_pitch = first_pitch - @max_interval
111
+ @min_pitch = 0 if @min_pitch < 0
112
+
113
+ @small_max_span = (@max_pitch - @min_pitch < 12)
114
+ end
115
+
116
+ pitches.map! do |pitch|
117
+ if @small_max_span
118
+ pitch = @max_pitch if pitch > @max_pitch
119
+ pitch = @min_pitch if pitch < @max_pitch
120
+ else
121
+ pitch -= 12 while pitch > @max_pitch
122
+ pitch += 12 while pitch < @min_pitch
123
+ end
124
+ pitch
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,24 @@
1
+ module MTK
2
+ module Sequencers
3
+
4
+ # A Sequencer which uses the longest duration of the events at each step to determine
5
+ # the delta times between entries in the {Timeline}.
6
+ class LegatoSequencer < Sequencer
7
+
8
+ # (see Sequencer#next)
9
+ def next
10
+ @previous_events = super
11
+ end
12
+
13
+ ########################
14
+ protected
15
+
16
+ # (see Sequencer#advance)
17
+ def advance
18
+ @time += @previous_events.map{|event| event.length }.max
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ module MTK
2
+ module Sequencers
3
+
4
+ # A Sequencer which uses a :rhythm type {Patterns::Pattern} to determine the delta times between entries in the {Timeline}.
5
+ class RhythmicSequencer < Sequencer
6
+
7
+ def initialize(patterns, options={})
8
+ super
9
+ @rhythm = options[:rhythm] or raise ArgumentError.new(":rhythm option is required")
10
+ end
11
+
12
+ def rewind
13
+ super
14
+ @rhythm.rewind if @rhythm
15
+ end
16
+
17
+ ########################
18
+ protected
19
+
20
+ # (see Sequencer#advance)
21
+ def advance
22
+ @time += @rhythm.next.length
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -1,11 +1,11 @@
1
1
  module MTK
2
- module Sequencer
2
+ module Sequencers
3
3
 
4
- # A Sequencer produces {Timeline}s from a collection of {Pattern}s.
4
+ # A Sequencer produces {Timeline}s from a collection of {Patterns::Pattern}s.
5
5
  #
6
6
  # @abstract Subclass and override {#advance} to implement a Sequencer.
7
7
  #
8
- class AbstractSequencer
8
+ class Sequencer
9
9
 
10
10
  # The maximum number of [time,event_list] entries that will be generated for the {Timeline}.
11
11
  # nil means no maximum (be careful of infinite loops!)
@@ -15,7 +15,7 @@ module MTK
15
15
  # nil means no maximum (be careful of infinite loops!)
16
16
  attr_accessor :max_time
17
17
 
18
- # Used by {#to_timeline} to builds event lists from the results of #{Pattern::Enumerator#next} for the {Pattern}s in this Sequencer.
18
+ # Used by {#to_timeline} to builds event lists from the results of {Patterns::Pattern#next} for the {Patterns::Pattern}s in this Sequencer.
19
19
  attr_reader :event_builder
20
20
 
21
21
  # The current time offset for the sequencer. Used for the {Timeline} times.
@@ -24,24 +24,37 @@ module MTK
24
24
  # The current sequencer step index (the number of times-1 that {#next} has been called), or -1 if the sequencer has not yet started.
25
25
  attr_reader :step
26
26
 
27
+ attr_reader :patterns
28
+
29
+ # @param patterns [Array] the list of patterns to be sequenced into a {Timeline}
30
+ # @param options [Hash] the options to create a message with.
31
+ # @option options [String] :max_steps set {#max_steps}
32
+ # @option options [String] :max_time set {#max_time}
33
+ # @option options [Proc] :filter a Proc that will replace the events generated by {#next} with the results of the Proc[events]
34
+ # @option options [Class] :event_builder replace the {Sequencers::EventBuilder} with a custom Event pattern
27
35
  def initialize(patterns, options={})
28
36
  @patterns = patterns
29
37
  @max_steps = options[:max_steps]
30
38
  @max_time = options[:max_time]
39
+ @filter = options[:filter]
31
40
 
32
- event_builder_class = options.fetch :event_builder, Helper::EventBuilder
41
+ event_builder_class = options.fetch :event_builder, ::MTK::Sequencers::EventBuilder
33
42
  @event_builder = event_builder_class.new(patterns, options)
43
+
34
44
  rewind
35
45
  end
36
46
 
37
47
 
38
- # Produce a {Timeline} from the {Pattern}s in this Sequencer.
48
+ # Produce a {Timeline} from the {Patterns::Pattern}s in this Sequencer.
39
49
  def to_timeline
40
50
  rewind
41
51
  timeline = Timeline.new
42
52
  loop do
43
53
  events = self.next
44
- timeline[@time] = events if events
54
+ if events
55
+ events = events.reject{|e| e.rest? }
56
+ timeline[@time] = events unless events.empty?
57
+ end
45
58
  end
46
59
  timeline
47
60
  end
@@ -52,12 +65,14 @@ module MTK
52
65
  # so you can ignore this method unless you want to hack on sequencers at a lower level.
53
66
  def next
54
67
  if @step >= 0
55
- advance!
68
+ advance
56
69
  raise StopIteration if @max_time and @time > @max_time
57
70
  end
58
71
  @step += 1
59
72
  raise StopIteration if @max_steps and @step >= @max_steps
60
- @event_builder.next
73
+ events = @event_builder.next
74
+ events = @filter[events] if @filter
75
+ events
61
76
  end
62
77
 
63
78
 
@@ -67,7 +82,7 @@ module MTK
67
82
  def rewind
68
83
  @time = 0
69
84
  @step = -1
70
- event_builder.rewind
85
+ @event_builder.rewind
71
86
  end
72
87
 
73
88
 
@@ -75,10 +90,21 @@ module MTK
75
90
  protected
76
91
 
77
92
  # Advance @time to the next time for the {Timeline} being produced by {#to_timeline}
78
- def advance!
93
+ def advance
79
94
  @time += 1 # default behavior simply advances one beat at a time
80
95
  end
81
96
 
97
+ def self.inherited(subclass)
98
+ # Define a convenience method like MTK::Patterns.Sequence()
99
+ # that can handle varargs or a single array argument, plus any Hash options
100
+ classname = subclass.name.sub /.*::/, '' # Strip off module prefixes
101
+ MTK::Sequencers.define_singleton_method classname do |*args|
102
+ options = (args[-1].is_a? Hash) ? args.pop : {}
103
+ args = args[0] if args.length == 1 and args[0].is_a? Array
104
+ subclass.new(args,options)
105
+ end
106
+ end
107
+
82
108
  end
83
109
 
84
110
  end