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,237 @@
1
+ grammar MTK_Grammar
2
+
3
+ rule root
4
+ ( space? root:(bare_sequencer | sequencer | timeline) space? ) {
5
+ root.value
6
+ }
7
+ end
8
+
9
+ rule bare_sequencer
10
+ ( pattern '' ) {
11
+ # it seems at least 2 elements are needed to access the pattern submatch,
12
+ # so using an empty string as a workaround
13
+ MTK::Sequencers.LegatoSequencer pattern.value
14
+ }
15
+ end
16
+
17
+ rule sequencer
18
+ ( left_curly bare_sequencer right_curly ) {
19
+ bare_sequencer.value
20
+ }
21
+ end
22
+
23
+ rule timeline
24
+ ( left_curly timepoint pattern (space timepoint pattern)* right_curly ) {
25
+ MTK::Events::Timeline.from_a values_of(:timepoint).zip(values_of :pattern)
26
+ }
27
+ end
28
+
29
+ rule pattern
30
+ ( pattern:(bare_choice | choice) '' ) {
31
+ val = first.value
32
+ if val.is_a? MTK::Patterns::Pattern then val else MTK::Patterns::Sequence.new [val] end
33
+ }
34
+ end
35
+
36
+
37
+ rule bare_choice
38
+ (
39
+ seq:(bare_sequence | sequence) (pipe seq:(bare_sequence | sequence))*
40
+ )
41
+ {
42
+ vals = values_of :seq
43
+ if vals.length==1 then vals[0] else MTK::Patterns.Choice(*vals) end
44
+ }
45
+ end
46
+
47
+
48
+ rule choice
49
+ (
50
+ left_angle bare_choice right_angle
51
+ )
52
+ {
53
+ bare_choice.value
54
+ }
55
+ end
56
+
57
+
58
+ rule bare_sequence
59
+ (
60
+ chain (space chain)*
61
+ )
62
+ {
63
+ vals = values_of :chain
64
+ if vals.length==1 then vals[0] else MTK::Patterns.Sequence(*vals) end
65
+ }
66
+ end
67
+
68
+
69
+ rule sequence
70
+ (
71
+ left_paren chain (space chain)* right_paren ('*' max_cycles:int)? ('&' element_count:int)?
72
+ )
73
+ {
74
+ chains = values_of(:chain)
75
+ options = {}
76
+ if element_count
77
+ options[:min_elements] = options[:max_elements] = element_count.value
78
+ end
79
+ options[:max_cycles] = max_cycles.value if max_cycles
80
+ if chains.length == 1 and options.empty?
81
+ chains[0] # Don't form a chain unnecessarily
82
+ else
83
+ MTK::Patterns::Sequence.new chains, options
84
+ end
85
+ }
86
+ end
87
+
88
+
89
+ rule foreach
90
+ (
91
+ sequence ('@' sequence)+
92
+ )
93
+ {
94
+ MTK::Patterns::ForEach.new(values_of :sequence)
95
+ }
96
+ end
97
+
98
+
99
+ rule chain
100
+ ( chainable (':' chainable)* ) {
101
+ vals = values_of(:chainable)
102
+ vals.length == 1 ? vals[0] : MTK::Patterns.Chain(*vals)
103
+ }
104
+ end
105
+
106
+ rule chainable
107
+ ( foreach | choice | sequence | element )
108
+ end
109
+
110
+ rule element
111
+ (
112
+ elem:( intensity | duration | interval | pitch | pitch_class | variable ) ('*' max_cycles:int)?
113
+ )
114
+ {
115
+ if max_cycles
116
+ MTK::Patterns::Sequence.new( [elem.value], max_cycles: max_cycles.value )
117
+ else
118
+ elem.value
119
+ end
120
+ }
121
+ end
122
+
123
+ # rule chord
124
+ # ( left_bracket pitch (space pitch)* right_bracket ) {
125
+ # MTK::Groups::Chord *values_of(:pitch)
126
+ # }
127
+ # end
128
+
129
+ rule pitch
130
+ ( pitch_class int ) {
131
+ MTK::Core::Pitch[pitch_class.value, int.value]
132
+ }
133
+ end
134
+
135
+ rule pitch_class
136
+ ( [A-Ga-g] [#b]*2 ) {
137
+ MTK::Core::PitchClass[to_s]
138
+ }
139
+ end
140
+
141
+ rule interval
142
+ ( [Pp] [1458] | ('maj'|'min'|[Mm]) [2367] | 'TT' | 'tt' ) {
143
+ MTK::Core::Interval.from_s(to_s)
144
+ }
145
+ end
146
+
147
+ rule intensity
148
+ ( ('p'1*3 | 'mp' | 'mf' | 'o' | 'f'2*3) ('+'|'-')? ) {
149
+ MTK::Core::Intensity.from_s(to_s)
150
+ }
151
+ end
152
+
153
+ rule duration
154
+ ( rest:'-'? multiplier:number? [whqisrx] ('.'|'t')* ) {
155
+ MTK::Core::Duration.from_s(to_s)
156
+ }
157
+ end
158
+
159
+ rule variable
160
+ ( '$'+ ) {
161
+ MTK::Lang::Variable.new(to_s)
162
+ }
163
+ end
164
+
165
+ rule timepoint
166
+ ( number right_arrow ) {
167
+ number.value
168
+ }
169
+ end
170
+
171
+ rule number
172
+ float | rational | int
173
+ end
174
+
175
+ rule float
176
+ ( '-'? [0-9]+ '.' [0-9]+ ) {
177
+ to_f
178
+ }
179
+ end
180
+
181
+ rule rational
182
+ ( numerator:int '/' denominator:[0-9]+ ) {
183
+ Rational(numerator.value, denominator.to_i)
184
+ }
185
+ end
186
+
187
+ rule int
188
+ ( '-'? [0-9]+ ) {
189
+ to_i
190
+ }
191
+ end
192
+
193
+ rule left_paren
194
+ ( '(' space? ) { nil }
195
+ end
196
+
197
+ rule right_paren
198
+ ( space? ')' ) { nil }
199
+ end
200
+
201
+ rule left_bracket
202
+ ( '[' space? ) { nil }
203
+ end
204
+
205
+ rule right_bracket
206
+ ( space? ']' ) { nil }
207
+ end
208
+
209
+ rule left_curly
210
+ ( '{' space? ) { nil }
211
+ end
212
+
213
+ rule right_curly
214
+ ( space? '}' ) { nil }
215
+ end
216
+
217
+ rule left_angle
218
+ ( '<' space? ) { nil }
219
+ end
220
+
221
+ rule right_angle
222
+ ( space? '>' ) { nil }
223
+ end
224
+
225
+ rule pipe
226
+ ( space? '|' space? ) { nil }
227
+ end
228
+
229
+ rule right_arrow
230
+ ( space? '=>' space? ) { nil }
231
+ end
232
+
233
+ rule space
234
+ [\s]+ { nil }
235
+ end
236
+
237
+ end
@@ -0,0 +1,29 @@
1
+ require 'citrus'
2
+
3
+ # @private
4
+ class Citrus::Match
5
+ def values_of(token_name)
6
+ captures[token_name].map{|token| token.value }
7
+ end
8
+ end
9
+
10
+ Citrus.load File.join(File.dirname(__FILE__),'mtk_grammar')
11
+
12
+ module MTK
13
+ module Lang
14
+
15
+ # Parser for the {file:lib/mtk/lang/mtk_grammar.citrus MTK grammar}
16
+ class Parser
17
+
18
+ def self.parse(syntax, root=:root, dump=false)
19
+ syntax = syntax.to_s.strip
20
+ return nil if syntax.empty?
21
+ matcher = ::MTK_Grammar.parse(syntax, :root => root)
22
+ puts matcher.dump if dump
23
+ matcher.value
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module MTK
2
+ module Lang
3
+
4
+ # Defines a constant for each {PitchClass} in the Western chromatic scale.
5
+ module PitchClasses
6
+
7
+ # The values of all constants defined in this module
8
+ PITCH_CLASSES = MTK::Core::PitchClass::PITCH_CLASSES
9
+
10
+ # The names of all constants defined in this module
11
+ PITCH_CLASS_NAMES = MTK::Core::PitchClass::NAMES
12
+
13
+ PITCH_CLASSES.each { |pc| const_set pc.name, pc }
14
+
15
+ # Lookup the value of an pitch class constant by name.
16
+ # @example lookup value of 'C'
17
+ # MTK::Core::PitchClasses['C']
18
+ # @see Groups::PitchClass.[]
19
+ def self.[](name)
20
+ begin
21
+ const_get name
22
+ rescue
23
+ nil
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,52 @@
1
+ module MTK
2
+ module Lang
3
+
4
+ # Defines a constants for each {Core::Pitch} in the standard MIDI range using scientific pitch notation.
5
+ #
6
+ # See http://en.wikipedia.org/wiki/Scientific_pitch_notation
7
+ #
8
+ # @note Because the character '#' cannot be used in the name of a constant,
9
+ # the "black key" pitches are all named as flats with 'b' (for example, Gb3 or Cb4)
10
+ # @note Because the character '-' (minus) cannot be used in the name of a constant,
11
+ # the low pitches use '_' (underscore) in place of '-' (minus) (for example C_1).
12
+ module Pitches
13
+
14
+ # The values of all constants defined in this module
15
+ PITCHES = []
16
+
17
+ # The names of all constants defined in this module
18
+ PITCH_NAMES = []
19
+
20
+ 128.times do |note_number|
21
+ pitch = MTK::Core::Pitch.from_i( note_number )
22
+ PITCHES << pitch
23
+
24
+ octave_str = pitch.octave.to_s.sub(/-/,'_') # '_1' for -1
25
+ name = "#{pitch.pitch_class}#{octave_str}"
26
+ PITCH_NAMES << name
27
+
28
+ # TODO? also define lower case pseudo constants for consistency with the grammar?
29
+
30
+ const_set name, pitch
31
+ end
32
+
33
+ PITCHES.freeze
34
+ PITCH_NAMES.freeze
35
+
36
+ # Lookup the value of an pitch constant by name.
37
+ # @example lookup value of 'C3'
38
+ # MTK::Core::Pitches['C3']
39
+ # @see Core::Pitch.from_s
40
+ # @note Unlike {Core::Pitch.from_s} this method will accept either '_' (underscore) or '-' (minus) and treat it like '-' (minus)
41
+ # @note Unlike {Core::Pitch.from_s} this method only accepts the accidental 'b'
42
+ def self.[](name)
43
+ begin
44
+ const_get name.sub('-','_')
45
+ rescue
46
+ nil
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,26 @@
1
+ module MTK
2
+ module Lang
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
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ module MTK::Lang
2
+
3
+ # A placeholder element for a variable value, for use within a {Patterns::Pattern} such as a {Patterns::ForEach} pattern.
4
+ # Will be evaluated to an actual value by the Pattern or Sequencer
5
+ #
6
+ class Variable
7
+
8
+ attr_reader :name
9
+
10
+ attr_accessor :value
11
+
12
+ def initialize name, value=nil
13
+ @name = name
14
+ @value = value
15
+ @implicit = !!(name =~ /^\$+$/)
16
+ end
17
+
18
+ # @return true when this variable has no specific value and references the implicit variable stack (such as in a {Patterns::ForEach})
19
+ def implicit?
20
+ @implicit
21
+ end
22
+
23
+ def == other
24
+ other.is_a? self.class and other.name == self.name
25
+ end
26
+
27
+ def to_s
28
+ "#{self.class}<#{name}#{'='+value.to_s if value}>"
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,66 @@
1
+ # Optional Numeric methods for converting to {MTK::Core} objects.
2
+ #
3
+ # @note you must require 'mtk/numeric_extensions' to use these methods.
4
+ #
5
+ class Numeric
6
+
7
+ # Convert a Numeric to a {MTK::Core::Pitch}
8
+ # @example 60.to_pitch => C4
9
+ def to_pitch
10
+ MTK::Core::Pitch.from_f(self)
11
+ end
12
+
13
+
14
+ # Convert a Numeric to a {MTK::Core::PitchClass}
15
+ # @example 2.to_pitch_class => D
16
+ def to_pitch_class
17
+ MTK::Core::PitchClass.from_f(self)
18
+ end
19
+
20
+
21
+ # Convert a Numeric to a {MTK::Core::Duration}
22
+ # @example 3.5.to_duration + 1.beat + 2.beats
23
+ def to_duration
24
+ MTK::Core::Duration.from_f(self)
25
+ end
26
+ alias beats to_duration
27
+ alias beat to_duration
28
+
29
+
30
+ # Convert a Numeric to a {MTK::Core::Intensity}
31
+ # @note The standard range of intensity values is from 0.0 - 1.0
32
+ # @example 1.to_pitch => fff
33
+ def to_intensity
34
+ MTK::Core::Intensity.from_f(self)
35
+ end
36
+
37
+ # Convert a Numeric percentage to a {MTK::Core::Intensity}
38
+ # @note The standard range of intensity percentages is from 0 - 100
39
+ # @example 100.percent_intensity => fff
40
+ def percent_intensity
41
+ MTK::Core::Intensity.from_f(self/100.0)
42
+ end
43
+
44
+ # Convert a Numeric to a {MTK::Core::Interval}
45
+ # @example 3.5.to_interval + 1.semitone + 2.semitones
46
+ def to_interval
47
+ MTK::Core::Interval.from_f(self)
48
+ end
49
+ alias semitones to_interval
50
+ alias semitone to_interval
51
+
52
+ # Convert a Numeric cents value to a {MTK::Core::Interval}
53
+ # @example 100.cents => 1.semitone
54
+ def cents
55
+ MTK::Core::Interval.from_f(self/100.0)
56
+ end
57
+ alias cent cents
58
+
59
+ # Convert a Numeric octaves value to a {MTK::Core::Interval}
60
+ # @example 1.octave => 12.semitones
61
+ def octaves
62
+ MTK::Core::Interval.from_f(self * 12)
63
+ end
64
+ alias octave octaves
65
+
66
+ end