jmtk 0.0.3.3-java

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