mtk 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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