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.
Files changed (110) hide show
  1. data/.yardopts +10 -0
  2. data/DEVELOPMENT_NOTES.md +115 -0
  3. data/INTRO.md +129 -0
  4. data/LICENSE.txt +27 -0
  5. data/README.md +50 -0
  6. data/Rakefile +102 -0
  7. data/bin/jmtk +250 -0
  8. data/bin/mtk +250 -0
  9. data/examples/crescendo.rb +20 -0
  10. data/examples/drum_pattern.rb +23 -0
  11. data/examples/dynamic_pattern.rb +36 -0
  12. data/examples/gets_and_play.rb +27 -0
  13. data/examples/notation.rb +22 -0
  14. data/examples/play_midi.rb +17 -0
  15. data/examples/print_midi.rb +13 -0
  16. data/examples/random_tone_row.rb +18 -0
  17. data/examples/syntax_to_midi.rb +28 -0
  18. data/examples/test_output.rb +7 -0
  19. data/examples/tone_row_melody.rb +23 -0
  20. data/lib/mtk.rb +76 -0
  21. data/lib/mtk/core/duration.rb +213 -0
  22. data/lib/mtk/core/intensity.rb +158 -0
  23. data/lib/mtk/core/interval.rb +157 -0
  24. data/lib/mtk/core/pitch.rb +154 -0
  25. data/lib/mtk/core/pitch_class.rb +194 -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/events/timeline.rb +232 -0
  30. data/lib/mtk/groups/chord.rb +56 -0
  31. data/lib/mtk/groups/collection.rb +196 -0
  32. data/lib/mtk/groups/melody.rb +96 -0
  33. data/lib/mtk/groups/pitch_class_set.rb +163 -0
  34. data/lib/mtk/groups/pitch_collection.rb +23 -0
  35. data/lib/mtk/io/dls_synth_device.rb +146 -0
  36. data/lib/mtk/io/dls_synth_output.rb +62 -0
  37. data/lib/mtk/io/jsound_input.rb +87 -0
  38. data/lib/mtk/io/jsound_output.rb +82 -0
  39. data/lib/mtk/io/midi_file.rb +209 -0
  40. data/lib/mtk/io/midi_input.rb +97 -0
  41. data/lib/mtk/io/midi_output.rb +195 -0
  42. data/lib/mtk/io/notation.rb +162 -0
  43. data/lib/mtk/io/unimidi_input.rb +117 -0
  44. data/lib/mtk/io/unimidi_output.rb +140 -0
  45. data/lib/mtk/lang/durations.rb +57 -0
  46. data/lib/mtk/lang/intensities.rb +61 -0
  47. data/lib/mtk/lang/intervals.rb +73 -0
  48. data/lib/mtk/lang/mtk_grammar.citrus +237 -0
  49. data/lib/mtk/lang/parser.rb +29 -0
  50. data/lib/mtk/lang/pitch_classes.rb +29 -0
  51. data/lib/mtk/lang/pitches.rb +52 -0
  52. data/lib/mtk/lang/pseudo_constants.rb +26 -0
  53. data/lib/mtk/lang/variable.rb +32 -0
  54. data/lib/mtk/numeric_extensions.rb +66 -0
  55. data/lib/mtk/patterns/chain.rb +49 -0
  56. data/lib/mtk/patterns/choice.rb +43 -0
  57. data/lib/mtk/patterns/cycle.rb +18 -0
  58. data/lib/mtk/patterns/for_each.rb +71 -0
  59. data/lib/mtk/patterns/function.rb +39 -0
  60. data/lib/mtk/patterns/lines.rb +54 -0
  61. data/lib/mtk/patterns/palindrome.rb +45 -0
  62. data/lib/mtk/patterns/pattern.rb +171 -0
  63. data/lib/mtk/patterns/sequence.rb +20 -0
  64. data/lib/mtk/sequencers/event_builder.rb +132 -0
  65. data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
  66. data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
  67. data/lib/mtk/sequencers/sequencer.rb +111 -0
  68. data/lib/mtk/sequencers/step_sequencer.rb +26 -0
  69. data/spec/mtk/core/duration_spec.rb +372 -0
  70. data/spec/mtk/core/intensity_spec.rb +289 -0
  71. data/spec/mtk/core/interval_spec.rb +265 -0
  72. data/spec/mtk/core/pitch_class_spec.rb +343 -0
  73. data/spec/mtk/core/pitch_spec.rb +297 -0
  74. data/spec/mtk/events/event_spec.rb +234 -0
  75. data/spec/mtk/events/note_spec.rb +174 -0
  76. data/spec/mtk/events/parameter_spec.rb +220 -0
  77. data/spec/mtk/events/timeline_spec.rb +430 -0
  78. data/spec/mtk/groups/chord_spec.rb +85 -0
  79. data/spec/mtk/groups/collection_spec.rb +374 -0
  80. data/spec/mtk/groups/melody_spec.rb +225 -0
  81. data/spec/mtk/groups/pitch_class_set_spec.rb +340 -0
  82. data/spec/mtk/io/midi_file_spec.rb +243 -0
  83. data/spec/mtk/io/midi_output_spec.rb +102 -0
  84. data/spec/mtk/lang/durations_spec.rb +89 -0
  85. data/spec/mtk/lang/intensities_spec.rb +101 -0
  86. data/spec/mtk/lang/intervals_spec.rb +143 -0
  87. data/spec/mtk/lang/parser_spec.rb +603 -0
  88. data/spec/mtk/lang/pitch_classes_spec.rb +62 -0
  89. data/spec/mtk/lang/pitches_spec.rb +56 -0
  90. data/spec/mtk/lang/pseudo_constants_spec.rb +20 -0
  91. data/spec/mtk/lang/variable_spec.rb +52 -0
  92. data/spec/mtk/numeric_extensions_spec.rb +83 -0
  93. data/spec/mtk/patterns/chain_spec.rb +110 -0
  94. data/spec/mtk/patterns/choice_spec.rb +97 -0
  95. data/spec/mtk/patterns/cycle_spec.rb +123 -0
  96. data/spec/mtk/patterns/for_each_spec.rb +136 -0
  97. data/spec/mtk/patterns/function_spec.rb +120 -0
  98. data/spec/mtk/patterns/lines_spec.rb +77 -0
  99. data/spec/mtk/patterns/palindrome_spec.rb +108 -0
  100. data/spec/mtk/patterns/pattern_spec.rb +132 -0
  101. data/spec/mtk/patterns/sequence_spec.rb +203 -0
  102. data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
  103. data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
  104. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
  105. data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
  106. data/spec/mtk/sequencers/step_sequencer_spec.rb +93 -0
  107. data/spec/spec_coverage.rb +2 -0
  108. data/spec/spec_helper.rb +12 -0
  109. data/spec/test.mid +0 -0
  110. 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