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,117 @@
1
+ require 'unimidi'
2
+ require 'ostruct'
3
+
4
+ module MTK
5
+ module IO
6
+
7
+ # Provides realtime MIDI input for MRI/YARV Ruby via the unimidi gem.
8
+ # @note This class is optional and only available if you require 'mtk/midi/unimidi_input'.
9
+ # It depends on the 'unimidi' gem.
10
+ class UniMIDIInput < MIDIInput
11
+
12
+ public_class_method :new
13
+
14
+ def self.devices
15
+ @devices ||= ::UniMIDI::Input.all.reject{|d| d.name.strip.empty? }
16
+ end
17
+
18
+ def self.devices_by_name
19
+ @devices_by_name ||= devices.each_with_object( Hash.new ){|device,hash| hash[device.name] = device }
20
+ end
21
+
22
+
23
+ attr_reader :device, :recording, :thread
24
+
25
+ def initialize(input_device, options={})
26
+ super
27
+ @open_time = Time.now.to_f
28
+ end
29
+
30
+ def record(options={})
31
+ @recording = [] unless options[:append] and @recording
32
+ monitor = options[:monitor]
33
+
34
+ stop
35
+ @thread = Thread.new do
36
+ @start_time = Time.now.to_f
37
+ loop do
38
+ @device.gets.each do |data|
39
+ puts data if monitor
40
+ record_raw_data data
41
+ end
42
+ sleep 0.001
43
+ end
44
+ end
45
+
46
+ time_limit = options[:time_limit]
47
+ if time_limit
48
+ puts "Blocking current thread for #{time_limit} seconds to record MIDI input."
49
+ @thread.join(time_limit)
50
+ end
51
+ end
52
+
53
+ def stop
54
+ Thread.kill @thread if @thread
55
+ end
56
+
57
+ def to_timeline(options={})
58
+ return nil if not @recording
59
+
60
+ bpm = options.fetch :bmp, 120
61
+ beats_per_second = bpm.to_f/60
62
+ timeline = Timeline.new
63
+ note_ons = {}
64
+ start = nil
65
+
66
+ @recording.each do |message, time|
67
+ start ||= time
68
+ time -= start
69
+ time /= beats_per_second
70
+
71
+ if message.is_a? MTK::Events::Event
72
+ timeline.add time,message
73
+ else
74
+ case message.type
75
+ when :note_on
76
+ pitch = message.pitch
77
+ note_ons[pitch] = [message,time]
78
+
79
+ when :note_off
80
+ pitch = message.pitch
81
+ if note_ons.has_key? pitch
82
+ note_on, start_time = note_ons.delete(pitch)
83
+ duration = time - start_time
84
+ note = MTK::Events::Note.from_midi pitch, note_on.velocity, duration
85
+ timeline.add time,note
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ timeline.quantize! options[:quantize] if options.key? :quantize
92
+ timeline.shift_to! options[:shift_to] if options.key? :shift_to
93
+
94
+ timeline
95
+ end
96
+
97
+
98
+ #######################
99
+ private
100
+
101
+ def record_raw_data raw
102
+ status, data1, data2 = *raw[:data] # the 3 bytes of raw message data
103
+ message = (
104
+ case status & 0xF0
105
+ when 0x80 then OpenStruct.new({:type => :note_off, :pitch => data1, :velocity => data2})
106
+ when 0x90 then OpenStruct.new({:type => :note_on, :pitch => data1, :velocity => data2})
107
+ else MTK::Events::Parameter.from_midi(status,data1,data2)
108
+ end
109
+ )
110
+ time = raw[:timestamp]/1000 - (@start_time - @open_time)
111
+ @recording << [message, time]
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+
@@ -0,0 +1,140 @@
1
+ require 'unimidi'
2
+
3
+ module MTK
4
+ module IO
5
+
6
+ # Provides realtime MIDI output for "standard" Ruby (MRI) via the unimidi and gamelan gems.
7
+ # @note This class is optional and only available if you require 'mtk/midi/unimidi_output'.
8
+ # It depends on the 'unimidi' and 'gamelan' gems.
9
+ class UniMIDIOutput < MIDIOutput
10
+
11
+ public_class_method :new
12
+
13
+ def self.devices
14
+ @devices ||= ::UniMIDI::Output.all.reject{|d| d.name.strip.empty? }
15
+ end
16
+
17
+ def self.devices_by_name
18
+ @devices_by_name ||= devices.each_with_object( Hash.new ){|device,hash| hash[device.name] = device }
19
+ end
20
+
21
+
22
+ ######################
23
+ protected
24
+
25
+ # (see MIDIOutput#note_on)
26
+ def note_on(pitch, velocity, channel)
27
+ @device.puts(0x90|channel, pitch, velocity)
28
+ end
29
+
30
+ # (see MIDIOutput#note_off)
31
+ def note_off(pitch, velocity, channel)
32
+ @device.puts(0x80|channel, pitch, velocity)
33
+ end
34
+
35
+ # (see MIDIOutput#control)
36
+ def control(number, midi_value, channel)
37
+ @device.puts(0xB0|channel, number, midi_value)
38
+ end
39
+
40
+ # (see MIDIOutput#channel_pressure)
41
+ def channel_pressure(midi_value, channel)
42
+ @device.puts(0xD0|channel, midi_value, 0)
43
+ end
44
+
45
+ # (see MIDIOutput#poly_pressure)
46
+ def poly_pressure(pitch, midi_value, channel)
47
+ @device.puts(0xA0|channel, pitch, midi_value)
48
+ end
49
+
50
+ # (see MIDIOutput#bend)
51
+ def bend(midi_value, channel)
52
+ @device.puts(0xE0|channel, midi_value & 127, (midi_value >> 7) & 127)
53
+ end
54
+
55
+ # (see MIDIOutput#program)
56
+ def program(number, channel)
57
+ @device.puts(0xC0|channel, number, 0)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+
64
+
65
+ #####################################################################
66
+ # MONKEY PATCHING for https://github.com/arirusso/ffi-coremidi/pull/2
67
+ # This can be removed once that pull request is released.
68
+
69
+ if RbConfig::CONFIG['host_os'] =~ /darwin/
70
+ # We're running on OS X
71
+
72
+ begin
73
+ ffi_coremidi_exists = !CoreMIDI::Map::CF.nil?
74
+ rescue NameError
75
+ ffi_coremidi_exists = false
76
+ end
77
+
78
+ if ffi_coremidi_exists
79
+
80
+ # @private
81
+ module CoreMIDI
82
+
83
+ # @private
84
+ class Device
85
+ def initialize(id, device_pointer, options = {})
86
+ include_if_offline = options[:include_offline] || false
87
+ @id = id
88
+ @resource = device_pointer
89
+ @entities = []
90
+
91
+ prop = Map::CF.CFStringCreateWithCString( nil, "name", 0 )
92
+ begin
93
+ name_ptr = FFI::MemoryPointer.new(:pointer)
94
+ Map::MIDIObjectGetStringProperty(@resource, prop, name_ptr)
95
+ name = name_ptr.read_pointer
96
+ len = Map::CF.CFStringGetMaximumSizeForEncoding(Map::CF.CFStringGetLength(name), :kCFStringEncodingUTF8)
97
+ bytes = FFI::MemoryPointer.new(len + 1)
98
+ raise RuntimeError.new("CFStringGetCString") unless Map::CF.CFStringGetCString(name, bytes, len, :kCFStringEncodingUTF8)
99
+ @name = bytes.read_string
100
+ ensure
101
+ Map::CF.CFRelease(name) unless name.nil? || name.null?
102
+ Map::CF.CFRelease(prop) unless prop.null?
103
+ end
104
+ populate_entities(:include_offline => include_if_offline)
105
+ end
106
+
107
+ end
108
+
109
+ # @private
110
+ module Map
111
+
112
+ # @private
113
+ module CF
114
+
115
+ extend FFI::Library
116
+ ffi_lib '/System/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation'
117
+
118
+ typedef :pointer, :CFStringRef
119
+ typedef :long, :CFIndex
120
+ enum :CFStringEncoding, [ :kCFStringEncodingUTF8, 0x08000100 ]
121
+
122
+ # CFString* CFStringCreateWithCString( ?, CString, encoding)
123
+ attach_function :CFStringCreateWithCString, [:pointer, :string, :int], :pointer
124
+ # CString* CFStringGetCStringPtr(CFString*, encoding)
125
+ attach_function :CFStringGetCStringPtr, [:pointer, :int], :pointer
126
+
127
+ attach_function :CFStringGetLength, [ :CFStringRef ], :CFIndex
128
+
129
+ attach_function :CFStringGetMaximumSizeForEncoding, [ :CFIndex, :CFStringEncoding ], :long
130
+
131
+ attach_function :CFStringGetCString, [ :CFStringRef, :pointer, :CFIndex, :CFStringEncoding ], :bool
132
+
133
+ attach_function :CFRelease, [ :pointer ], :void
134
+
135
+ end
136
+ end
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,57 @@
1
+ require 'rational'
2
+
3
+ module MTK
4
+ module Lang
5
+
6
+ # Defines duration constants using abbreviations for standard rhythm values ('w' for whole note, 'h' for half note, etc).
7
+ #
8
+ # In order to avoid conflict with pitch class 'e', the constant for eighth note is 'i'
9
+ #
10
+ # These can be thought of like constants, but they
11
+ # use lower-case names and therefore define them as "pseudo constant" methods.
12
+ # The methods are available either through the module (MTK::Core::Durations::e) or via mixin (include MTK::Core::Durations; q)
13
+ #
14
+ # These values assume the quarter note is one beat (1.0), so they work best with 4/4 and other */4 time signatures.
15
+ #
16
+ # @note Including this module defines a bunch of single-character variables, which may shadow existing variable names.
17
+ # Just be mindful of what is defined in this module when including it.
18
+ #
19
+ # @see Note
20
+ module Durations
21
+ extend MTK::Lang::PseudoConstants
22
+
23
+ # NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
24
+
25
+ # whole note
26
+ # @macro [attach] durations.define_constant
27
+ # @attribute [r]
28
+ # @return [$2] number of beats for $1
29
+ define_constant 'w', MTK::Core::Duration[4]
30
+
31
+ # half note
32
+ define_constant 'h', MTK::Core::Duration[2]
33
+
34
+ # quarter note
35
+ define_constant 'q', MTK::Core::Duration[1]
36
+
37
+ # eight note
38
+ define_constant 'i', MTK::Core::Duration[Rational(1,2)]
39
+
40
+ # sixteenth note
41
+ define_constant 's', MTK::Core::Duration[Rational(1,4)]
42
+
43
+ # thirty-second note
44
+ define_constant 'r', MTK::Core::Duration[Rational(1,8)]
45
+
46
+ # sixty-fourth note
47
+ define_constant 'x', MTK::Core::Duration[Rational(1,16)]
48
+
49
+ # The values of all "psuedo constants" defined in this module
50
+ DURATIONS = [w, h, q, i, s, r, x].freeze
51
+
52
+ # The names of all "psuedo constants" defined in this module
53
+ DURATION_NAMES = MTK::Core::Duration::NAMES
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,61 @@
1
+ module MTK
2
+ module Lang
3
+
4
+ # Defines intensity constants using standard dynamic symbols.
5
+ #
6
+ # These can be thought of like constants, but in order to distinguish 'f' (forte) from the {PitchClass} 'F'
7
+ # it was necessary to use lower-case names and therefore define them as "pseudo constant" methods.
8
+ # The methods are available either through the module (MTK::Intensities::f) or via mixin (include MTK::Intensities; f)
9
+ #
10
+ # These values are intensities in the range 0.125 - 1.0 (in increments of 1/8), so they can be easily scaled (unlike MIDI velocities).
11
+ #
12
+ # It is also possible to retrieve values in increments of 1/24 by using the '+' and '-' suffix when looking
13
+ # up values via the {.[]} method.
14
+ #
15
+ # @note Including this module shadows Ruby's built-in p() method.
16
+ # If you include this module, you can access the built-in p() method via Kernel.p()
17
+ #
18
+ # @see Note
19
+ module Intensities
20
+ extend MTK::Lang::PseudoConstants
21
+
22
+ # NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
23
+
24
+ # pianississimo
25
+ # @macro [attach] intensities.define_constant
26
+ # @attribute [r]
27
+ # @return [$2] intensity value for $1
28
+ define_constant 'ppp', MTK::Core::Intensity[0.125]
29
+
30
+ # pianissimo
31
+ define_constant 'pp', MTK::Core::Intensity[0.25]
32
+
33
+ # piano
34
+ # @note Including this module shadows Ruby's built-in p() method.
35
+ # If you include this module, you can access the built-in p() method via Kernel.p()
36
+ define_constant 'p', MTK::Core::Intensity[0.375]
37
+
38
+ # mezzo-piano
39
+ define_constant 'mp', MTK::Core::Intensity[0.5]
40
+
41
+ # mezzo-forte
42
+ define_constant 'mf', MTK::Core::Intensity[0.625]
43
+
44
+ # forte
45
+ define_constant 'o', MTK::Core::Intensity[0.75]
46
+
47
+ # fortissimo
48
+ define_constant 'ff', MTK::Core::Intensity[0.875]
49
+
50
+ # fortississimo
51
+ define_constant 'fff', MTK::Core::Intensity[1.0]
52
+
53
+ # The values of all "psuedo constants" defined in this module
54
+ INTENSITIES = [ppp, pp, p, mp, mf, o, ff, fff].freeze
55
+
56
+ # The names of all "psuedo constants" defined in this module
57
+ INTENSITY_NAMES = MTK::Core::Intensity::NAMES
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,73 @@
1
+ module MTK
2
+ module Lang
3
+
4
+ # Defines a constant for intervals up to an octave using diatonic naming conventions (see http://en.wikipedia.org/wiki/Interval_(music)#Main_intervals)
5
+ #
6
+ # Naming conventions
7
+ # P#: perfect interval
8
+ # M#: major interval
9
+ # m#: minor interval
10
+ # TT: tritone (AKA augmented 4th or diminished 5th)
11
+ #
12
+ # These can be thought of like constants, but in order to succinctly distinguish 'm2' (minor) from 'M2' (major),
13
+ # it was necessary to use lower-case names for some of the values and therefore define them as "pseudo constant" methods.
14
+ # The methods are available either through the module (MTK::Core::Intervals::m2) or via mixin (include MTK::Core::Intervals; m2)
15
+ module Intervals
16
+ extend MTK::Lang::PseudoConstants
17
+
18
+ # NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
19
+
20
+ # perfect unison
21
+ # @macro [attach] interval.define_constant
22
+ # @attribute [r]
23
+ # @return [$2] number of semitones in the interval $1
24
+ define_constant 'P1', MTK::Core::Interval[0]
25
+
26
+ # minor second
27
+ # @macro [attach] interval.define_constant
28
+ # @attribute [r]
29
+ # @return [$2] number of semitones in the interval $1
30
+ define_constant 'm2', MTK::Core::Interval[1]
31
+
32
+ # major second
33
+ define_constant 'M2', MTK::Core::Interval[2]
34
+
35
+ # minor third
36
+ define_constant 'm3', MTK::Core::Interval[3]
37
+
38
+ # major third
39
+ define_constant 'M3', MTK::Core::Interval[4]
40
+
41
+ # pefect fourth
42
+ define_constant 'P4', MTK::Core::Interval[5]
43
+
44
+ # tritone (AKA augmented fourth or diminished fifth)
45
+ define_constant 'TT', MTK::Core::Interval[6]
46
+
47
+ # perfect fifth
48
+ define_constant 'P5', MTK::Core::Interval[7]
49
+
50
+ # minor sixth
51
+ define_constant 'm6', MTK::Core::Interval[8]
52
+
53
+ # major sixth
54
+ define_constant 'M6', MTK::Core::Interval[9]
55
+
56
+ # minor seventh
57
+ define_constant 'm7', MTK::Core::Interval[10]
58
+
59
+ # major seventh
60
+ define_constant 'M7', MTK::Core::Interval[11]
61
+
62
+ # pefect octave
63
+ define_constant 'P8', MTK::Core::Interval[12]
64
+
65
+ # The values of all "psuedo constants" defined in this module
66
+ INTERVALS = [P1, m2, M2, m3, M3, P4, TT, P5, m6, M6, m7, M7, P8].freeze
67
+
68
+ # The names of all "psuedo constants" defined in this module
69
+ INTERVAL_NAMES = MTK::Core::Interval::NAMES
70
+
71
+ end
72
+ end
73
+ end