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,85 +0,0 @@
1
- module MTK
2
- module Helper
3
-
4
- # A helper class for {Sequencer}s.
5
- # Takes a list of patterns and constructs a list of {Event}s from the next elements in each pattern.
6
- class EventBuilder
7
-
8
- DEFAULT_PITCH = MTK::Pitches::C4
9
- DEFAULT_INTENSITY = MTK::Intensities::f
10
- DEFAULT_DURATION = 1
11
-
12
- def initialize(patterns, options={})
13
- @patterns = patterns
14
- @options = options
15
- @max_interval = options.fetch :max_interval, 12
16
- rewind
17
- end
18
-
19
- # Build a list of events from the next element in each {Pattern}
20
- # @return [Array] an array of events
21
- def next
22
- pitches = []
23
- intensity = @default_intensity
24
- duration = @default_duration
25
-
26
- for pattern in @patterns
27
- element = pattern.next
28
- case element
29
- when Pitch then pitches << element
30
- when PitchSet then pitches += element.pitches
31
- when PitchClass then pitches += pitches_for_pitch_classes([element], @previous_pitch || @default_pitch)
32
- when PitchClassSet then pitches += pitches_for_pitch_classes(element, @previous_pitch || @default_pitch)
33
- else case pattern.type
34
- when :pitch
35
- if element.is_a? Numeric
36
- # pitch interval case
37
- if @previous_pitches
38
- pitches += @previous_pitches.map{|pitch| pitch + element }
39
- else
40
- pitches << ((@previous_pitch || @default_pitch) + element)
41
- end
42
- end
43
- when :intensity then intensity = element
44
- when :duration then duration = element
45
- end
46
- end
47
- end
48
-
49
- if not pitches.empty?
50
- @previous_pitch = pitches.last
51
- @previous_pitches = pitches.length > 1 ? pitches : nil
52
- pitches.map{|pitch| Note(pitch,intensity,duration) }
53
- else
54
- nil
55
- end
56
- end
57
-
58
- # Reset the EventBuilder to its initial state
59
- def rewind
60
- @default_pitch = @options.fetch :default_pitch, DEFAULT_PITCH
61
- @default_intensity = @options.fetch :default_intensity, DEFAULT_INTENSITY
62
- @default_duration = @options.fetch :default_duration, DEFAULT_DURATION
63
- @previous_pitch = nil
64
- @previous_pitches = nil
65
- @patterns.each{|pattern| pattern.rewind }
66
- end
67
-
68
- ########################
69
- private
70
-
71
- def pitches_for_pitch_classes(pitch_classes, previous_pitch)
72
- pitches = []
73
- for pitch_class in pitch_classes
74
- pitch = previous_pitch.nearest(pitch_class)
75
- pitch -= 12 if pitch > @default_pitch+@max_interval # keep within max_distance of start (default is one octave)
76
- pitch += 12 if pitch < @default_pitch-@max_interval
77
- pitches << pitch
78
- end
79
- pitches
80
- end
81
-
82
- end
83
-
84
- end
85
- end
@@ -1,26 +0,0 @@
1
- module MTK::Helper
2
-
3
-
4
- # Extension for modules that want to define pseudo-constants (constant-like values with lower-case names)
5
- module PseudoConstants
6
-
7
- # Define a module method and module function (available both through the module namespace and as a mixin method),
8
- # to provide a constant with a lower-case name.
9
- #
10
- # @param name [Symbol] the name of the pseudo-constant
11
- # @param value [Object] the value of the pseudo-constant
12
- # @return [nil]
13
- def define_constant name, value
14
- if name[0..0] =~ /[A-Z]/
15
- const_set name, value # it's just a normal constant
16
- else
17
- # the pseudo-constant definition is the combination of a method and module_function:
18
- define_method(name) { value }
19
- module_function name
20
- end
21
- nil
22
- end
23
-
24
- end
25
-
26
- end
@@ -1,17 +0,0 @@
1
- require 'citrus'
2
- Citrus.load File.join(File.dirname(__FILE__),'mtk_grammar')
3
-
4
- module MTK
5
- module Lang
6
-
7
- # Parser for the {file:lib/mtk/lang/mtk_grammar.citrus MTK grammar}
8
- class Grammar
9
-
10
- def self.parse(syntax, root=:pitch)
11
- MTK_Grammar.parse(syntax, :root => root).value
12
- end
13
-
14
- end
15
-
16
- end
17
- end
data/lib/mtk/note.rb DELETED
@@ -1,63 +0,0 @@
1
- module MTK
2
-
3
- # A musical {Event} defined by a {Pitch}, intensity, and duration
4
- class Note < Event
5
-
6
- # frequency of the note as a Pitch
7
- attr_reader :pitch
8
-
9
- def initialize(pitch, intensity, duration)
10
- @pitch = pitch
11
- super(intensity, duration)
12
- end
13
-
14
- def self.from_hash(hash)
15
- new hash[:pitch], hash[:intensity], hash[:duration]
16
- end
17
-
18
- def to_hash
19
- super.merge({ :pitch => @pitch })
20
- end
21
-
22
- def self.from_midi(pitch, velocity, beats)
23
- new Pitches::PITCHES[pitch], velocity/127.0, beats
24
- end
25
-
26
- def to_midi
27
- [pitch.to_i, velocity, duration]
28
- end
29
-
30
- def transpose(interval)
31
- self.class.new(@pitch+interval, @intensity, @duration)
32
- end
33
-
34
- def invert(around_pitch)
35
- self.class.new(@pitch.invert(around_pitch), @intensity, @duration)
36
- end
37
-
38
- def == other
39
- super and other.respond_to? :pitch and @pitch == other.pitch
40
- end
41
-
42
- def to_s
43
- "Note(#{pitch}, #{super})"
44
- end
45
-
46
- def inspect
47
- "Note(#{pitch}, #{super})"
48
- end
49
-
50
- end
51
-
52
- # Construct a {Note} from any supported type
53
- def Note(*anything)
54
- anything = anything.first if anything.size == 1
55
- case anything
56
- when Array then Note.new(*anything)
57
- when Note then anything
58
- else raise "Note doesn't understand #{anything.class}"
59
- end
60
- end
61
- module_function :Note
62
-
63
- end
@@ -1,132 +0,0 @@
1
- module MTK
2
- module Pattern
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!} and {#current} to implement a Pattern
9
- #
10
- class AbstractPattern
11
- include Helper::Collection
12
- include Enumerator
13
-
14
- # The elements in the pattern
15
- attr_reader :elements
16
-
17
- attr_reader :options
18
-
19
- # The type of elements in the pattern, such as :pitch, :intensity, or :duration
20
- #
21
- # This is often needed by {Sequencer} classes to interpret the pattern elements.
22
- attr_reader :type
23
-
24
- # The number of elements emitted since the last {#rewind}
25
- attr_reader :element_count
26
-
27
- # The maximum number of elements this Pattern will emit before a StopIteration exception
28
- attr_reader :max_elements
29
-
30
- # @param elements [Enumerable, #to_a] the list of elements in the pattern
31
- # @param options [Hash] the pattern options
32
- # @option options [String] :type the pattern {#type}
33
- # @option options [Fixnum] :max_elements the {#max_elements}
34
- def initialize(elements, options={})
35
- elements = elements.to_a if elements.respond_to? :to_a and not elements.is_a? Proc # Proc check prevents warnings in Ruby 1.8
36
- @elements = elements
37
- @options = options
38
- @type = options[:type]
39
- @max_elements = options[:max_elements]
40
- rewind
41
- end
42
-
43
- # Construct a pattern from an Array.
44
- # @param (see #initialize)
45
- # @option (see #initialize)
46
- # @see #initialize
47
- def self.from_a(elements, options={})
48
- new(elements, options)
49
- end
50
-
51
- # Reset the pattern to the beginning
52
- def rewind
53
- @current = nil
54
- @element_count = 0
55
- self
56
- end
57
-
58
- # Emit the next element in the pattern
59
- # @raise StopIteration when the pattern has emitted all values, or has hit the {#max_elements} limit.
60
- def next
61
- if @current.is_a? Enumerator
62
- begin
63
- subpattern_next = @current.next
64
- subpattern_has_next = true
65
- rescue StopIteration
66
- subpattern_has_next = false
67
- end
68
-
69
- return emit subpattern_next if subpattern_has_next
70
- # else fall through and continue with normal behavior
71
- end
72
-
73
- begin
74
- advance!
75
- rescue StopIteration
76
- @current = nil
77
- raise
78
- end
79
-
80
- @current = current
81
- if @current.is_a? Enumerator
82
- @current.rewind # start over, in case we already enumerated this element and then did a rewind
83
- return self.next
84
- end
85
-
86
- emit @current
87
- end
88
-
89
-
90
- ##################
91
- protected
92
-
93
- # Update internal state (index, etc) so that {#current} will refer to the next element.
94
- # @note Override this method in a subclass to define a custom Pattern.
95
- # @raise StopIteration if there are no more elements
96
- def advance!
97
- raise StopIteration if @elements.nil? or @elements.empty?
98
- end
99
-
100
- # The current element in the pattern, which will be returned by {#next} (after a call to {#advance!}).
101
- # @note Override this method in a subclass to define a custom Pattern.
102
- def current
103
- @elements[0]
104
- end
105
-
106
-
107
- ##################
108
- private
109
-
110
- def emit element
111
- @element_count += 1
112
- raise StopIteration if @max_elements and @element_count > @max_elements
113
- element
114
- end
115
- end
116
-
117
- # Build any "TypedPattern" (like PitchCycle or DurationPalindrome) or even just Pattern
118
- def method_missing(method, *args, &block)
119
- # Assuming we get something like PitchCycle, split into 'Pitch' and 'Cycle'
120
- camel_case_words = method.to_s.gsub(/([a-z])([A-Z])/,'\1 \2').split(' ')
121
- pattern = MTK::Pattern.const_get camel_case_words.last
122
- if camel_case_words.length > 1
123
- type = camel_case_words.first.downcase.to_sym
124
- pattern.new(args, :type => type)
125
- else
126
- pattern.new(args)
127
- end
128
- end
129
- module_function :method_missing
130
-
131
- end
132
- end
@@ -1,51 +0,0 @@
1
- module MTK
2
- module Pattern
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 Cycle < AbstractPattern
7
-
8
- # The number of cycles emitted (1 cycle == all elements emitted) since the last {#rewind}
9
- attr_reader :cycle_count
10
-
11
- # The maximum number of cycles this Pattern will emit before a StopIteration exception
12
- attr_reader :max_cycles
13
-
14
- def initialize(elements, options={})
15
- super
16
- @max_cycles = options[:max_cycles]
17
- end
18
-
19
- # Reset the sequence to the beginning
20
- def rewind
21
- @index = -1
22
- @cycle_count = 0
23
- super
24
- end
25
-
26
- ###################
27
- protected
28
-
29
- # (see AbstractPattern#advance!)
30
- def advance!
31
- super # base advance!() implementation prevents infinite loops with empty patterns
32
- @index += 1
33
- if @index >= @elements.length
34
- @cycle_count += 1
35
- if @max_cycles and @cycle_count >= @max_cycles
36
- raise StopIteration
37
- end
38
- @index = -1
39
- advance!
40
- end
41
- end
42
-
43
- # (see AbstractPattern#current)
44
- def current
45
- @elements[@index]
46
- end
47
-
48
- end
49
-
50
- end
51
- end
@@ -1,26 +0,0 @@
1
- module MTK
2
- module Pattern
3
-
4
- # The core interface for all Patterns.
5
- #
6
- # This module doesn't provide any useful default functionality.
7
- # It only indicates that a class is compatible with MTK's pattern enumerator interface.
8
- #
9
- module Enumerator
10
-
11
- # Return the next element in the enumerator
12
- # @raise StopIteration when no more elements are available
13
- def next
14
- raise StopIteration
15
- end
16
-
17
- # Reset the enumerator to the beginning
18
- # @return self
19
- def rewind
20
- self
21
- end
22
-
23
- end
24
-
25
- end
26
- end
@@ -1,46 +0,0 @@
1
- module MTK
2
- module Pattern
3
-
4
- # An arbitrary function that dynamically generates elements.
5
- #
6
- class Function < AbstractPattern
7
-
8
- attr_reader :function
9
-
10
- def initialize(elements, options={})
11
- super
12
- @function = @elements
13
- # unpack from the varargs Array that may be passed in from the "convenience constructor methods" defined in MTK::Pattern \
14
- @function = @function.first if @function.is_a? Enumerable
15
- end
16
-
17
- # Reset the sequence to the beginning
18
- def rewind
19
- @prev = nil
20
- @function_call_count = -1
21
- super
22
- end
23
-
24
- ###################
25
- protected
26
-
27
- # (see AbstractPattern#advance!)
28
- def advance!
29
- raise StopIteration if @elements.nil?
30
- end
31
-
32
- # (see AbstractPattern#current)
33
- def current
34
- @function_call_count += 1
35
- @prev = case @function.arity
36
- when 0 then @function.call
37
- when 1 then @function.call(@prev)
38
- when 2 then @function.call(@prev, @function_call_count)
39
- else @function.call(@prev, @function_call_count, @element_count)
40
- end
41
- end
42
-
43
- end
44
-
45
- end
46
- end