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,7 @@
1
+ require 'mtk'
2
+ require_relative 'helpers/output_selector'
3
+ include MTK::Lang::Pitches
4
+
5
+ output = OutputSelector.ensure_output ARGV[0]
6
+
7
+ output.play MTK.Note(C4,2,1)
@@ -0,0 +1,23 @@
1
+ # Generate a MIDI file using a tone row, repeated 3 times using random rhythms
2
+ #
3
+ # NOTE: this blindly overwrites any existing MTK-tone_row_melody.mid file, unless an argument is provided
4
+
5
+ require 'mtk'
6
+ require 'mtk/io/midi_file'
7
+ include MTK
8
+ include MTK::Lang::PitchClasses
9
+ include MTK::Lang::Durations
10
+
11
+ file = ARGV[0] || 'MTK-tone_row_melody.mid'
12
+
13
+ row = PitchClassSet Db, G, Ab, F, Eb, E, D, C, B, Gb, A, Bb
14
+ pitch_pattern = Patterns.Cycle *row
15
+ rhythm_pattern = Patterns.Choice s, i, i+s, q # choose between sixteenth, eighth, dotted eighth, and quarter
16
+
17
+ chain = Patterns.Chain pitch_pattern, rhythm_pattern, min_elements: 36, max_elements: 36
18
+
19
+ sequencer = Sequencers.LegatoSequencer chain
20
+ timeline = sequencer.to_timeline
21
+
22
+ MIDIFile(file).write timeline
23
+
@@ -0,0 +1,76 @@
1
+ # Description of modules for documentation:
2
+
3
+ # The top level module for this library
4
+ module MTK
5
+
6
+ # Core data types
7
+ module Core
8
+ end
9
+
10
+ # Musical events, such as {MTK::Events::Note}s and {MTK::Events::Parameter} changes, that are arranged in time via a {MTK::Events::Timeline}.
11
+ module Events
12
+ end
13
+
14
+ # Collections of {MTK::Core} objects
15
+ module Groups
16
+ end
17
+
18
+ # Optional classes for MIDI {MTK::IO::File} and realtime MIDI {MTK::IO::Input} and {MTK::IO::Output}.
19
+ module IO
20
+ end
21
+
22
+ # Optional classes for the "MTK language", which let's you compose music via MTK without writing any Ruby code
23
+ module Lang
24
+ end
25
+
26
+ # Classes that emit elements one at a time. Used by {MTK::Sequencers::Sequencer}s to construct {MTK::Events::Timeline}s.
27
+ module Patterns
28
+ end
29
+
30
+ # Classes that assemble {MTK::Patterns::Pattern}s into {MTK::Events::Timeline}s.
31
+ module Sequencers
32
+ end
33
+
34
+ end
35
+
36
+ require 'mtk/core/pitch_class'
37
+ require 'mtk/core/pitch'
38
+ require 'mtk/core/duration'
39
+ require 'mtk/core/intensity'
40
+ require 'mtk/core/interval'
41
+
42
+ require 'mtk/lang/pseudo_constants'
43
+ require 'mtk/lang/pitch_classes'
44
+ require 'mtk/lang/pitches'
45
+ require 'mtk/lang/intervals'
46
+ require 'mtk/lang/intensities'
47
+ require 'mtk/lang/durations'
48
+ require 'mtk/lang/variable'
49
+ require 'mtk/lang/parser'
50
+
51
+ require 'mtk/groups/collection'
52
+ require 'mtk/groups/pitch_collection'
53
+ require 'mtk/groups/pitch_class_set'
54
+ require 'mtk/groups/melody'
55
+ require 'mtk/groups/chord'
56
+
57
+ require 'mtk/events/event'
58
+ require 'mtk/events/note'
59
+ require 'mtk/events/parameter'
60
+ require 'mtk/events/timeline'
61
+
62
+ require 'mtk/patterns/pattern'
63
+ require 'mtk/patterns/sequence'
64
+ require 'mtk/patterns/cycle'
65
+ require 'mtk/patterns/choice'
66
+ require 'mtk/patterns/function'
67
+ require 'mtk/patterns/lines'
68
+ require 'mtk/patterns/palindrome'
69
+ require 'mtk/patterns/chain'
70
+ require 'mtk/patterns/for_each'
71
+
72
+ require 'mtk/sequencers/event_builder'
73
+ require 'mtk/sequencers/sequencer'
74
+ require 'mtk/sequencers/step_sequencer'
75
+ require 'mtk/sequencers/rhythmic_sequencer'
76
+ require 'mtk/sequencers/legato_sequencer'
@@ -0,0 +1,213 @@
1
+ module MTK
2
+ module Core
3
+
4
+ # A measure of time in musical beats.
5
+ # May be negative to indicate a rest, which uses the absolute value for the effective duration.
6
+ class Duration
7
+
8
+ include Comparable
9
+
10
+ # The names of the base durations. See {MTK::Lang::Durations} for more info.
11
+ NAMES = %w[w h q i s r x].freeze
12
+
13
+ VALUES_BY_NAME = {
14
+ 'w' => 4,
15
+ 'h' => 2,
16
+ 'q' => 1,
17
+ 'i' => Rational(1,2),
18
+ 's' => Rational(1,4),
19
+ 'r' => Rational(1,8),
20
+ 'x' => Rational(1,16)
21
+ }
22
+
23
+ @flyweight = {}
24
+
25
+ # The number of beats, typically represented as a Rational
26
+ attr_reader :value
27
+
28
+ def initialize( length_in_beats )
29
+ @value = length_in_beats
30
+ end
31
+
32
+ # Return a duration, only constructing a new instance when not already in the flyweight cache
33
+ def self.[](length_in_beats)
34
+ if length_in_beats.is_a? Fixnum
35
+ value = length_in_beats
36
+ else
37
+ value = Rational(length_in_beats)
38
+ end
39
+ @flyweight[value] ||= new(value)
40
+ end
41
+
42
+ class << self
43
+ alias :from_f :[]
44
+ alias :from_i :[]
45
+ end
46
+
47
+ # Lookup a duration by name.
48
+ # This method supports appending any combination of '.' and 't' for more fine-grained values.
49
+ # each '.' multiplies by 3/2, and each 't' multiplies by 2/3.
50
+ # You may use the prefix '-' to negate the duration (which turns it into a rest of the same length).
51
+ # You may also prefix (after the '-' if present) the base duration name with an integer, float, or rational number
52
+ # to multiply the base duration value. Rationals are in the form "#!{numerator_integer}/#!{denominator_integer}".
53
+ # @example lookup value of 'q.', which is 1.5 times a quarter note (1.5 beats):
54
+ # MTK::Core::Duration.from_s('q.')
55
+ # @example lookup the value of 3/4w, which three-quarters of a whole note (3 beats):
56
+ # MTK::Core::Duration.from_s('3/4w')
57
+ def self.from_s(s)
58
+ if s =~ /^(-)?(\d+([\.\/]\d+)?)?([whqisrx])((\.|t)*)$/i
59
+ name = $4.downcase
60
+ modifier = $5.downcase
61
+ modifier << $1 if $1 # negation
62
+ multiplier = $2
63
+ else
64
+ raise ArgumentError.new("Invalid Duration string '#{s}'")
65
+ end
66
+
67
+ value = VALUES_BY_NAME[name]
68
+ modifier.each_char do |mod|
69
+ case mod
70
+ when '-' then value *= -1
71
+ when '.' then value *= Rational(3,2)
72
+ when 't' then value *= Rational(2,3)
73
+ end
74
+ end
75
+
76
+ if multiplier
77
+ case multiplier
78
+ when /\./
79
+ value *= multiplier.to_f
80
+ when /\//
81
+ numerator, denominator = multiplier.split('/')
82
+ value *= Rational(numerator.to_i, denominator.to_i)
83
+ else
84
+ value *= multiplier.to_i
85
+ end
86
+ end
87
+
88
+ self[value]
89
+ end
90
+
91
+ class << self
92
+ alias :from_name :from_s
93
+ end
94
+
95
+ # The magnitude (absolute value) of the duration.
96
+ # This is the actual duration for rests.
97
+ # @see #rest?
98
+ def length
99
+ @value < 0 ? -@value : @value
100
+ end
101
+
102
+ # Durations with negative values are rests.
103
+ # @see #length
104
+ # @see #-@
105
+ def rest?
106
+ @value < 0
107
+ end
108
+
109
+ # The number of beats as a floating point number
110
+ def to_f
111
+ @value.to_f
112
+ end
113
+
114
+ # The numerical value for the nearest whole number of beats
115
+ def to_i
116
+ @value.round
117
+ end
118
+
119
+ def to_s
120
+ value = @value.to_s
121
+ value = sprintf '%.2f', @value if value.length > 6 # threshold is 6 for no particular reason...
122
+ "#{value} #{@value.abs > 1 || @value==0 ? 'beats' : 'beat'}"
123
+ end
124
+
125
+ def inspect
126
+ "#<#{self.class}:#{object_id} @value=#{@value}>"
127
+ end
128
+
129
+ def ==( other )
130
+ if other.is_a? MTK::Core::Duration
131
+ other.value == @value
132
+ else
133
+ other == @value
134
+ end
135
+ end
136
+
137
+ def <=> other
138
+ if other.respond_to? :value
139
+ @value <=> other.value
140
+ else
141
+ @value <=> other
142
+ end
143
+ end
144
+
145
+ # Add this duration to another.
146
+ # @return a new Duration that has a value of the sum of the arguments.
147
+ def + duration
148
+ if duration.is_a? MTK::Core::Duration
149
+ MTK::Core::Duration[@value + duration.value]
150
+ else
151
+ MTK::Core::Duration[@value + duration]
152
+ end
153
+ end
154
+
155
+ # Subtract another duration from this one.
156
+ # @return a new Duration that has a value of the difference of the arguments.
157
+ def - duration
158
+ if duration.is_a? MTK::Core::Duration
159
+ MTK::Core::Duration[@value - duration.value]
160
+ else
161
+ MTK::Core::Duration[@value - duration]
162
+ end
163
+ end
164
+
165
+ # Multiply this duration with another.
166
+ # @return a new Duration that has a value of the product of the arguments.
167
+ def * duration
168
+ if duration.is_a? MTK::Core::Duration
169
+ MTK::Core::Duration[@value * duration.value]
170
+ else
171
+ MTK::Core::Duration[@value * duration]
172
+ end
173
+ end
174
+
175
+ # Divide this duration with another.
176
+ # @return a new Duration that has a value of the division of the arguments.
177
+ def / duration
178
+ if duration.is_a? MTK::Core::Duration
179
+ MTK::Core::Duration[to_f / duration.value]
180
+ else
181
+ MTK::Core::Duration[to_f / duration]
182
+ end
183
+ end
184
+
185
+ # Negate the duration value.
186
+ # Turns normal durations into rests and vice versa.
187
+ # @return a new Duration that has a negated value.
188
+ # @see #rest?
189
+ def -@
190
+ MTK::Core::Duration[@value * -1]
191
+ end
192
+
193
+ # Allow basic math operations with Numeric objects.
194
+ def coerce(other)
195
+ return MTK::Core::Duration[other], self
196
+ end
197
+
198
+ end
199
+ end
200
+
201
+ # Construct a {Duration} from any supported type
202
+ def Duration(*anything)
203
+ anything = anything.first if anything.length == 1
204
+ case anything
205
+ when Numeric then MTK::Core::Duration[anything]
206
+ when String, Symbol then MTK::Core::Duration.from_s(anything)
207
+ when Duration then anything
208
+ else raise "Duration doesn't understand #{anything.class}"
209
+ end
210
+ end
211
+ module_function :Duration
212
+
213
+ end
@@ -0,0 +1,158 @@
1
+ module MTK
2
+ module Core
3
+
4
+ # A measure of intensity, using an underlying value in the range 0.0-1.0
5
+ class Intensity
6
+
7
+ include Comparable
8
+
9
+ # The names of the base intensities. See {}MTK::Lang::Intensities} for more info.
10
+ NAMES = %w[ppp pp p mp mf o ff fff].freeze
11
+
12
+ VALUES_BY_NAME = {
13
+ 'ppp' => 0.125,
14
+ 'pp' => 0.25,
15
+ 'p' => 0.375,
16
+ 'mp' => 0.5,
17
+ 'mf' => 0.625,
18
+ 'o' => 0.75,
19
+ 'ff' => 0.875,
20
+ 'fff' => 1.0
21
+ }
22
+
23
+ @flyweight = {}
24
+
25
+ # The number of beats, typically representation as a Rational
26
+ attr_reader :value
27
+
28
+ def initialize(value)
29
+ @value = value
30
+ end
31
+
32
+ # Return an Intensity, only constructing a new instance when not already in the flyweight cache
33
+ def self.[](value)
34
+ value = value.to_f
35
+ @flyweight[value] ||= new(value)
36
+ end
37
+
38
+ class << self
39
+ alias :from_f :[]
40
+ alias :from_i :[]
41
+ end
42
+
43
+ # Lookup an intensity by name.
44
+ # This method supports appending '-' or '+' for more fine-grained values.
45
+ def self.from_s(s)
46
+ return self[1.0] if s == 'fff+' # special case because "fff" is already the maximum
47
+
48
+ name = nil
49
+ modifier = nil
50
+ if s =~ /^(\w+)([+-])?$/
51
+ name = $1
52
+ modifier = $2
53
+ end
54
+
55
+ value = VALUES_BY_NAME[name]
56
+ raise ArgumentError.new("Invalid Intensity string '#{s}'") unless value
57
+
58
+ value += 1.0/24 if modifier == '+'
59
+ value -= 1.0/24 if modifier == '-'
60
+
61
+ self[value]
62
+ end
63
+
64
+ class << self
65
+ alias :from_name :from_s
66
+ end
67
+
68
+ # The number of beats as a floating point number
69
+ def to_f
70
+ @value.to_f
71
+ end
72
+
73
+ # The numerical value for the nearest whole number of beats
74
+ def to_i
75
+ @value.round
76
+ end
77
+
78
+ def to_midi
79
+ (to_f * 127).round
80
+ end
81
+
82
+ def to_percent
83
+ (@value * 100).round
84
+ end
85
+
86
+ def to_s
87
+ "#{to_percent}% intensity"
88
+ end
89
+
90
+ def inspect
91
+ "#<#{self.class}:#{object_id} @value=#{@value}>"
92
+ end
93
+
94
+ def ==( other )
95
+ other.is_a? MTK::Core::Intensity and other.value == @value
96
+ end
97
+
98
+ def <=> other
99
+ if other.respond_to? :value
100
+ @value <=> other.value
101
+ else
102
+ @value <=> other
103
+ end
104
+
105
+ end
106
+
107
+ def + intensity
108
+ if intensity.is_a? MTK::Core::Intensity
109
+ MTK::Core::Intensity[@value + intensity.value]
110
+ else
111
+ MTK::Core::Intensity[@value + intensity]
112
+ end
113
+ end
114
+
115
+ def -intensity
116
+ if intensity.is_a? MTK::Core::Intensity
117
+ MTK::Core::Intensity[@value - intensity.value]
118
+ else
119
+ MTK::Core::Intensity[@value - intensity]
120
+ end
121
+ end
122
+
123
+ def * intensity
124
+ if intensity.is_a? MTK::Core::Intensity
125
+ MTK::Core::Intensity[@value * intensity.value]
126
+ else
127
+ MTK::Core::Intensity[@value * intensity]
128
+ end
129
+ end
130
+
131
+ def / intensity
132
+ if intensity.is_a? MTK::Core::Intensity
133
+ MTK::Core::Intensity[to_f / intensity.value]
134
+ else
135
+ MTK::Core::Intensity[to_f / intensity]
136
+ end
137
+ end
138
+
139
+ def coerce(other)
140
+ return MTK::Core::Intensity[other], self
141
+ end
142
+
143
+ end
144
+ end
145
+
146
+ # Construct a {Duration} from any supported type
147
+ def Intensity(*anything)
148
+ anything = anything.first if anything.length == 1
149
+ case anything
150
+ when Numeric then MTK::Core::Intensity[anything]
151
+ when String, Symbol then MTK::Core::Intensity.from_s(anything)
152
+ when Intensity then anything
153
+ else raise "Intensity doesn't understand #{anything.class}"
154
+ end
155
+ end
156
+ module_function :Intensity
157
+
158
+ end