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,49 @@
|
|
1
|
+
module MTK
|
2
|
+
module Patterns
|
3
|
+
|
4
|
+
# A pattern that takes a list of patterns and combines their values into a list of event properties.
|
5
|
+
class Chain < Pattern
|
6
|
+
|
7
|
+
def initialize(elements, options={})
|
8
|
+
super
|
9
|
+
@stop_after_first = true # assume everything's an element until we inspect them in the loop below
|
10
|
+
@stop_after_first = true if @elements.all?{|element| not element.is_a? ::MTK::Patterns::Pattern }
|
11
|
+
end
|
12
|
+
|
13
|
+
###################
|
14
|
+
protected
|
15
|
+
|
16
|
+
# (see Pattern#rewind_or_cycle)
|
17
|
+
def rewind_or_cycle(is_cycling=false)
|
18
|
+
@is_element_done = Array.new(elements.size)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
# (see Pattern#advance)
|
23
|
+
def advance
|
24
|
+
@current = @elements.map.with_index do |element,index|
|
25
|
+
if element.is_a? ::MTK::Patterns::Pattern
|
26
|
+
begin
|
27
|
+
element.next
|
28
|
+
rescue StopIteration
|
29
|
+
raise StopIteration if element.max_elements_exceeded?
|
30
|
+
@is_element_done[index] = true
|
31
|
+
element.rewind
|
32
|
+
element.next
|
33
|
+
end
|
34
|
+
else
|
35
|
+
element
|
36
|
+
end
|
37
|
+
end.flatten
|
38
|
+
|
39
|
+
raise StopIteration if @is_element_done.all?{|done| done }
|
40
|
+
|
41
|
+
@elements.each.with_index do |element,index|
|
42
|
+
@is_element_done[index] = true unless element.is_a? ::MTK::Patterns::Pattern
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module MTK
|
2
|
+
module Patterns
|
3
|
+
|
4
|
+
# Randomly choose from a list of elements.
|
5
|
+
#
|
6
|
+
# Supports giving different weights to different choices.
|
7
|
+
# Default is to weight all choices equally.
|
8
|
+
#
|
9
|
+
class Choice < Pattern
|
10
|
+
|
11
|
+
# @param (see Pattern#initialize)
|
12
|
+
# @option (see Pattern#initialize)
|
13
|
+
# @option options [Array] :weights a list of chances that each corresponding element will be selected (normalized against the total weight)
|
14
|
+
# @example choose the second element twice as often as the first or third:
|
15
|
+
# MTK::Pattern::Choice.new [:first,:second,:third], :weights => [1,2,1]
|
16
|
+
def initialize(elements, options={})
|
17
|
+
super
|
18
|
+
@weights = options.fetch :weights, Array.new(@elements.length, 1)
|
19
|
+
@total_weight = @weights.inject(:+).to_f
|
20
|
+
end
|
21
|
+
|
22
|
+
#####################
|
23
|
+
protected
|
24
|
+
|
25
|
+
def advance
|
26
|
+
@index += 1
|
27
|
+
raise StopIteration if @index > 0
|
28
|
+
|
29
|
+
target = rand * @total_weight
|
30
|
+
@weights.each_with_index do |weight,index|
|
31
|
+
if target < weight
|
32
|
+
@current = @elements[index]
|
33
|
+
break
|
34
|
+
else
|
35
|
+
target -= weight
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MTK
|
2
|
+
module Patterns
|
3
|
+
|
4
|
+
# An endless enumerator that outputs an element one at a time from a list of elements,
|
5
|
+
# looping back to the beginning when elements run out.
|
6
|
+
# This is the same as a Sequence but by default has unlimited @max_cycles
|
7
|
+
class Cycle < Sequence
|
8
|
+
|
9
|
+
def initialize(elements, options={})
|
10
|
+
super
|
11
|
+
# Base Pattern & Sequence default to 1 max_cycle, this defaults to nil which is unlimited cycles
|
12
|
+
@max_cycles = options[:max_cycles]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module MTK
|
2
|
+
module Patterns
|
3
|
+
|
4
|
+
# For each value in the first sub-pattern, iterate over the second sub-pattern and chain the resulting values.
|
5
|
+
#
|
6
|
+
class ForEach < Pattern
|
7
|
+
|
8
|
+
# (see Pattern#next)
|
9
|
+
def next
|
10
|
+
@index = 0 if @index < 0
|
11
|
+
|
12
|
+
last_index = @elements.length-1
|
13
|
+
while @index <= last_index
|
14
|
+
# assume all elements are Patterns, otherwise this construct doesn't really have a point...
|
15
|
+
pattern = @elements[@index]
|
16
|
+
begin
|
17
|
+
element = pattern.next
|
18
|
+
value = evaluate_variables(element)
|
19
|
+
|
20
|
+
if @index == last_index # then emit values
|
21
|
+
@current = value
|
22
|
+
return emit(value)
|
23
|
+
|
24
|
+
else # not last element, so store variables
|
25
|
+
@vars.push value
|
26
|
+
@index += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
rescue StopIteration
|
30
|
+
if @index==0
|
31
|
+
raise # We're done when the first pattern is done
|
32
|
+
else
|
33
|
+
pattern.rewind
|
34
|
+
@vars.pop
|
35
|
+
@index -= 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
###################
|
43
|
+
protected
|
44
|
+
|
45
|
+
# (see Pattern#rewind_or_cycle)
|
46
|
+
def rewind_or_cycle(is_cycling=false)
|
47
|
+
@vars = []
|
48
|
+
@elements.each{|elem| elem.rewind }
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
####################
|
54
|
+
private
|
55
|
+
|
56
|
+
def evaluate_variables(element)
|
57
|
+
case element
|
58
|
+
when ::MTK::Lang::Variable
|
59
|
+
if element.implicit?
|
60
|
+
return @vars[-element.name.length] # '$' is most recently pushed value, $$' goes back 2 levels, '$$$' goes back 3, etc
|
61
|
+
end
|
62
|
+
when Array
|
63
|
+
return element.map{|e| evaluate_variables(e) }
|
64
|
+
end
|
65
|
+
return element
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module MTK
|
2
|
+
module Patterns
|
3
|
+
|
4
|
+
# An arbitrary function that dynamically generates elements.
|
5
|
+
#
|
6
|
+
class Function < Pattern
|
7
|
+
|
8
|
+
attr_reader :function
|
9
|
+
|
10
|
+
def initialize(elements, options={})
|
11
|
+
# unpack from the varargs Array that may be passed in from the "convenience constructor methods" defined in MTK::Pattern \
|
12
|
+
@function = if elements.is_a? Enumerable then elements.first else elements end
|
13
|
+
super [@function], options
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
###################
|
18
|
+
protected
|
19
|
+
|
20
|
+
# (see Pattern#rewind_or_cycle)
|
21
|
+
def rewind_or_cycle(is_cycling=false)
|
22
|
+
@function_call_count = -1
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def advance
|
27
|
+
@function_call_count += 1
|
28
|
+
@current = case @function.arity
|
29
|
+
when 0 then @function.call
|
30
|
+
when 1 then @function.call(@current)
|
31
|
+
when 2 then @function.call(@current, @function_call_count)
|
32
|
+
else @function.call(@current, @function_call_count, @element_count)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module MTK
|
2
|
+
module Patterns
|
3
|
+
|
4
|
+
# A piecewise linear function (see {http://en.wikipedia.org/wiki/File:PiecewiseLinear.png}) defined in terms
|
5
|
+
# of [value, steps_to_reach_value] pairs.
|
6
|
+
#
|
7
|
+
# The "steps_to_reach_value" for the first element is ignored and may be omitted, since it takes 0 steps to start.
|
8
|
+
class Lines < Pattern
|
9
|
+
|
10
|
+
###################
|
11
|
+
protected
|
12
|
+
|
13
|
+
# (see Pattern#rewind_or_cycle)
|
14
|
+
def rewind_or_cycle(is_cycling=false)
|
15
|
+
@steps = -1
|
16
|
+
@step_count = -1
|
17
|
+
@prev = nil
|
18
|
+
@next = nil
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
# (see Pattern#advance)
|
23
|
+
def advance
|
24
|
+
while @step_count >= @steps
|
25
|
+
@step_count = 0
|
26
|
+
|
27
|
+
@index += 1
|
28
|
+
raise StopIteration if @index >= @elements.length
|
29
|
+
|
30
|
+
@prev = @next
|
31
|
+
next_elem = @elements[@index]
|
32
|
+
if next_elem.is_a? Array
|
33
|
+
@next = next_elem.first
|
34
|
+
@steps = next_elem.last.to_f
|
35
|
+
else
|
36
|
+
@next = next_elem
|
37
|
+
@steps = 1.0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@step_count += 1
|
42
|
+
|
43
|
+
if @prev and @next
|
44
|
+
# linear interpolation
|
45
|
+
@current = @prev + (@next - @prev)*@step_count/@steps
|
46
|
+
else
|
47
|
+
@current = @next
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module MTK
|
2
|
+
module Patterns
|
3
|
+
|
4
|
+
# An endless enumerator that outputs an element one at a time from a list of elements,
|
5
|
+
# looping back to the beginning when elements run out.
|
6
|
+
class Palindrome < Cycle
|
7
|
+
|
8
|
+
# true if the first/last element are repeated when the ends are reached, else false
|
9
|
+
def repeat_ends?
|
10
|
+
@repeat_ends ||= @options.fetch :repeat_ends, false
|
11
|
+
end
|
12
|
+
|
13
|
+
##############
|
14
|
+
protected
|
15
|
+
|
16
|
+
# (see Pattern#rewind_or_cycle)
|
17
|
+
def rewind_or_cycle(is_cycling=false)
|
18
|
+
@direction = 1
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
# (see Pattern#advance)
|
23
|
+
def advance
|
24
|
+
raise StopIteration if @elements.nil? or @elements.empty? # prevent infinite loops
|
25
|
+
|
26
|
+
@index += @direction
|
27
|
+
|
28
|
+
if @index >= @elements.length
|
29
|
+
@direction = -1
|
30
|
+
@index = @elements.length - 1
|
31
|
+
@index -= 1 unless repeat_ends? or @elements.length == 1
|
32
|
+
|
33
|
+
elsif @index < 0
|
34
|
+
@direction = 1
|
35
|
+
@index = 0
|
36
|
+
@index += 1 unless repeat_ends? or @elements.length == 1
|
37
|
+
end
|
38
|
+
|
39
|
+
@current = @elements[@index]
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
module MTK
|
2
|
+
module Patterns
|
3
|
+
|
4
|
+
# A pattern of elements that can be emitted one element at a time via calls to {#next}.
|
5
|
+
#
|
6
|
+
# Patterns can be reset to the beginning via {#rewind}.
|
7
|
+
#
|
8
|
+
# @abstract Subclass and override {#advance} to implement a Pattern.
|
9
|
+
#
|
10
|
+
class Pattern
|
11
|
+
include MTK::Groups::Collection
|
12
|
+
|
13
|
+
# The elements in the pattern
|
14
|
+
attr_reader :elements
|
15
|
+
|
16
|
+
attr_reader :options
|
17
|
+
|
18
|
+
# The number of elements emitted since the last {#rewind}
|
19
|
+
attr_reader :element_count
|
20
|
+
|
21
|
+
# The minimum number of elements this Pattern will emit before a StopIteration exception
|
22
|
+
# This overrides any conflicting max_cycles setting.
|
23
|
+
attr_reader :min_elements
|
24
|
+
|
25
|
+
# The maximum number of elements this Pattern will emit before a StopIteration exception
|
26
|
+
# A nil value means infinite elements.
|
27
|
+
# @note {#max_cycles} may cause this Pattern to end before max_elements are emitted.
|
28
|
+
# If this is undesirable then use min_elements to override max_cycles.
|
29
|
+
attr_reader :max_elements
|
30
|
+
|
31
|
+
# The number of cycles emitted (1 cycle == all elements emitted) since the last {#rewind}
|
32
|
+
attr_reader :cycle_count
|
33
|
+
|
34
|
+
# The maximum number of cycles this Pattern will emit before a StopIteration exception.
|
35
|
+
# A nil value means inifinite cycles.
|
36
|
+
attr_reader :max_cycles
|
37
|
+
|
38
|
+
# @param elements [Enumerable] the list of elements in the pattern
|
39
|
+
# @param options [Hash] the pattern options
|
40
|
+
# @option options [Fixnum] :max_elements the {#max_elements} (default is nil, which means unlimited)
|
41
|
+
# @option options [Fixnum] :max_cycles the {#max_cycles} (default is 1)
|
42
|
+
def initialize(elements, options={})
|
43
|
+
elements = elements.to_a if elements.is_a? Enumerable
|
44
|
+
@elements = elements
|
45
|
+
@options = options
|
46
|
+
@min_elements = options[:min_elements]
|
47
|
+
@max_elements = options[:max_elements]
|
48
|
+
@max_cycles = options.fetch(:max_cycles, 1)
|
49
|
+
rewind
|
50
|
+
end
|
51
|
+
|
52
|
+
# Construct a pattern from an Array.
|
53
|
+
# @param (see #initialize)
|
54
|
+
# @option (see #initialize)
|
55
|
+
# @see #initialize
|
56
|
+
def self.from_a(elements, options={})
|
57
|
+
new(elements, options)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Reset the pattern to the beginning
|
61
|
+
def rewind
|
62
|
+
rewind_or_cycle
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
# Emit the next element in the pattern
|
67
|
+
# @raise StopIteration when the pattern has emitted all values, or has hit the {#max_elements} limit.
|
68
|
+
def next
|
69
|
+
raise StopIteration if empty?
|
70
|
+
|
71
|
+
if @current.is_a? Pattern
|
72
|
+
begin
|
73
|
+
return emit(@current.next)
|
74
|
+
rescue StopIteration
|
75
|
+
raise if max_elements_exceeded?
|
76
|
+
# else fall through and continue with normal behavior
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
advance
|
82
|
+
rescue StopIteration
|
83
|
+
rewind_or_cycle(true)
|
84
|
+
return self.next
|
85
|
+
end
|
86
|
+
|
87
|
+
if @current.kind_of? Pattern
|
88
|
+
@current.rewind # ensure nested patterns start from the beginning each time they are encountered
|
89
|
+
return self.next
|
90
|
+
end
|
91
|
+
|
92
|
+
emit(@current)
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def min_elements_unmet?
|
97
|
+
@min_elements and @element_count < @min_elements
|
98
|
+
end
|
99
|
+
|
100
|
+
def max_elements_exceeded?
|
101
|
+
@max_elements and @element_count >= @max_elements
|
102
|
+
end
|
103
|
+
|
104
|
+
def max_cycles_exceeded?
|
105
|
+
@max_cycles and @cycle_count >= @max_cycles
|
106
|
+
end
|
107
|
+
|
108
|
+
def empty?
|
109
|
+
@elements.nil? or @elements.empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
##################
|
113
|
+
protected
|
114
|
+
|
115
|
+
# Reset the pattern to the beginning
|
116
|
+
# @param is_cycling [Boolean] true when #next is performing a cycle back to the beginning of the Pattern. false for a normal #rewind
|
117
|
+
def rewind_or_cycle(is_cycling=false)
|
118
|
+
@current = nil
|
119
|
+
@index = -1
|
120
|
+
|
121
|
+
# and rewind child patterns
|
122
|
+
@elements.each{|element| element.rewind if element.is_a? Pattern }
|
123
|
+
|
124
|
+
if is_cycling
|
125
|
+
@cycle_count += 1
|
126
|
+
raise StopIteration if max_cycles_exceeded? and not min_elements_unmet?
|
127
|
+
else
|
128
|
+
@element_count = 0
|
129
|
+
@cycle_count = 0
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Update internal state (index, etc) and set @current to the next element.
|
134
|
+
def advance
|
135
|
+
@current = elements[0]
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
##################
|
140
|
+
private
|
141
|
+
|
142
|
+
def emit element
|
143
|
+
raise StopIteration if (max_elements_exceeded? or max_cycles_exceeded?) and not min_elements_unmet?
|
144
|
+
@element_count += 1
|
145
|
+
element
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.inherited(subclass)
|
149
|
+
# Define a convenience method like MTK::Patterns.Sequence()
|
150
|
+
# that can handle varargs or a single array argument, plus any Hash options
|
151
|
+
classname = subclass.name.sub /.*::/, '' # Strip off module prefixes
|
152
|
+
MTK::Patterns.define_singleton_method classname do |*args|
|
153
|
+
options = (args[-1].is_a? Hash) ? args.pop : {}
|
154
|
+
args = args[0] if args.length == 1 and args[0].is_a? Array
|
155
|
+
subclass.new(args,options)
|
156
|
+
end
|
157
|
+
|
158
|
+
%w(Pitch PitchClass Intensity Duration Interval Rhythm).each do |type|
|
159
|
+
MTK::Patterns.define_singleton_method "#{type}#{classname}" do |*args|
|
160
|
+
options = (args[-1].is_a? Hash) ? args.pop : {}
|
161
|
+
args = args[0] if args.length == 1 and args[0].is_a? Array
|
162
|
+
constructor_for_type = (type == 'Rhythm') ? 'Duration' : type
|
163
|
+
args = args.map{|arg| (arg.nil? or arg.is_a? Proc) ? arg : MTK.send(constructor_for_type, arg) } # coerce to the given type (or Duration for rhythm type)
|
164
|
+
subclass.new(args,options)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|