midi-jruby 0.0.12 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3556d36dc4530a743c39ee52843ac49c6106d7d1
4
+ data.tar.gz: 1f9d70df4153c5a152e34260da63f408cabb71d2
5
+ SHA512:
6
+ metadata.gz: c3adf3f2a801d5c7a56db5f64260d1868ca59ce10da867b4d27f970dbc3cfb891dae40905728afdcb16547491b4d5365f4e25e5dc19e5212cefbedc91b572122
7
+ data.tar.gz: ab5f514ffd17459df997e5be3a84b58696193c8803b4e89d28a99ef519179e15c0b1442b5b8cd57aa99c0bdc3d2e62920d62397fe67a920495fe36cd1ae1cbed
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2010-2011 Ari Russo
1
+ Copyright 2011-2014 Ari Russo
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
@@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
10
10
  distributed under the License is distributed on an "AS IS" BASIS,
11
11
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  See the License for the specific language governing permissions and
13
- limitations under the License.
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # midi-jruby
2
+
3
+ Realtime MIDI IO with JRuby using the javax.sound.midi API.
4
+
5
+ In the interest of allowing people on other platforms to utilize your code, you should consider using [unimidi](http://github.com/arirusso/unimidi). Unimidi is a platform independent wrapper that implements midi-jruby and has a similar API.
6
+
7
+ ## Features
8
+
9
+ * Simplified API
10
+ * Input and output on multiple devices concurrently
11
+ * Generalized handling of different MIDI Message types (including SysEx)
12
+ * Timestamped input events
13
+
14
+ ## Install
15
+
16
+ If you're using Bundler, add this line to your application's Gemfile:
17
+
18
+ `gem "midi-jruby"`
19
+
20
+ Otherwise
21
+
22
+ `gem install midi-jruby`
23
+
24
+ ## Examples
25
+
26
+ * [Input](http://github.com/arirusso/midi-jruby/blob/master/examples/input.rb)
27
+ * [Output](http://github.com/arirusso/midi-jruby/blob/master/examples/output.rb)
28
+
29
+ ## Issues
30
+
31
+ There is [an issue](http://stackoverflow.com/questions/8148898/java-midi-in-mac-osx-broken) that causes javax.sound.midi not to be able to send SysEx messages in some versions.
32
+
33
+ ## Documentation
34
+
35
+ * [rdoc](http://rdoc.info/gems/midi-jruby)
36
+
37
+ ## Author
38
+
39
+ [Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
40
+
41
+ ## License
42
+
43
+ Apache 2.0, See the file LICENSE
44
+
45
+ Copyright (c) 2011-2014 Ari Russo
@@ -0,0 +1,172 @@
1
+ module MIDIJRuby
2
+
3
+ # Access to javax.sound.midi
4
+ module API
5
+
6
+ import javax.sound.midi.MidiSystem
7
+ import javax.sound.midi.MidiDevice
8
+ import javax.sound.midi.MidiEvent
9
+ import javax.sound.midi.Receiver
10
+ import javax.sound.midi.ShortMessage
11
+ import javax.sound.midi.SysexMessage
12
+ import javax.sound.midi.Transmitter
13
+
14
+ extend self
15
+
16
+ # Get all MIDI devices that are available via javax.sound.midi
17
+ # @return [Array<Hash>] A set of hashes for each available device
18
+ def get_devices
19
+ MidiSystem.get_midi_device_info.map do |info|
20
+ jdevice = MidiSystem.get_midi_device(info)
21
+ {
22
+ :device => jdevice,
23
+ :id => get_uuid,
24
+ :name => info.get_name,
25
+ :description => info.get_description,
26
+ :vendor => info.get_vendor
27
+ }
28
+ end
29
+ end
30
+
31
+ # Get all MIDI inputs that are available via javax.sound.midi
32
+ # @return [Array<Input>]
33
+ def get_inputs
34
+ jinputs = get_devices.select { |device| !device[:device].get_max_transmitters.zero? }
35
+ jinputs.map { |jinput| Input.new(jinput[:id], jinput[:device], jinput) }
36
+ end
37
+
38
+ # Get all MIDI outputs that are available via javax.sound.midi
39
+ # @return [Array<Output>]
40
+ def get_outputs
41
+ joutputs = get_devices.select { |device| !device[:device].get_max_receivers.zero? }
42
+ joutputs.map { |joutput| Output.new(joutput[:id], joutput[:device], joutput) }
43
+ end
44
+
45
+ # Enable the given input device to receive MIDI messages
46
+ # @param [Java::ComSunMediaSound::MidiInDevice] device
47
+ # @return [Boolean]
48
+ def enable_input(device)
49
+ device.open
50
+ @transmitter ||= {}
51
+ @transmitter[device] = device.get_transmitter
52
+ @transmitter[device].set_receiver(InputReceiver.new)
53
+ true
54
+ end
55
+
56
+ # Enable the given output to emit MIDI messages
57
+ # @param [Java::ComSunMediaSound::MidiOutDevice] device
58
+ # @return [Boolean]
59
+ def enable_output(device)
60
+ @receiver ||= {}
61
+ @receiver[device] = device.get_receiver
62
+ device.open
63
+ true
64
+ end
65
+
66
+ # Close the given output device
67
+ # @param [Java::ComSunMediaSound::MidiOutDevice] device
68
+ # @return [Boolean]
69
+ def close_output(device)
70
+ @receiver[device].close
71
+ @receiver.delete(device)
72
+ device.close
73
+ true
74
+ end
75
+
76
+ # Close the given input device
77
+ # @param [Java::ComSunMediaSound::MidiInDevice] device
78
+ # @return [Boolean]
79
+ def close_input(device)
80
+ # http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4914667
81
+ # @transmitter[device].close
82
+ # device.close
83
+ @transmitter.delete(device)
84
+ true
85
+ end
86
+
87
+ # Read any new MIDI messages from the given input device
88
+ # @param [Java::ComSunMediaSound::MidiInDevice] device
89
+ # @return [Array<Array<Fixnum>>]
90
+ def read_input(device)
91
+ @transmitter[device].get_receiver.read
92
+ end
93
+
94
+ # Write the given MIDI message to the given output device
95
+ # @param [Java::ComSunMediaSound::MidiOutDevice] device
96
+ # @param [Array<Fixnum>] data
97
+ # @return [Boolean]
98
+ def write_output(device, data)
99
+ bytes = Java::byte[data.size].new
100
+ data.each_with_index { |byte, i| bytes.ubyte_set(i, byte) }
101
+ message = data.first.eql?(0xF0) ? SysexMessage.new : ShortMessage.new
102
+ message.set_message(bytes, data.length.to_java(:int))
103
+ @receiver[device].send(message, 0)
104
+ true
105
+ end
106
+
107
+ private
108
+
109
+ # Generate a uuid for a MIDI device
110
+ # @return [Fixnum]
111
+ def get_uuid
112
+ @id ||= -1
113
+ @id += 1
114
+ end
115
+
116
+ # Input event handler class
117
+ class InputReceiver
118
+
119
+ include javax.sound.midi.Receiver
120
+
121
+ attr_reader :stream
122
+
123
+ def initialize
124
+ @buffer = []
125
+ end
126
+
127
+ # Pluck messages from the buffer
128
+ # @return [Array<Array<Fixnum>>]
129
+ def read
130
+ messages = @buffer.dup
131
+ @buffer.clear
132
+ messages
133
+ end
134
+
135
+ # Add a new message to the buffer
136
+ # @param [Array<Fixnum>] message
137
+ # @param [Fixnum] timestamp
138
+ # @return [Array<Array<Fixnum>>]
139
+ def send(message, timestamp = -1)
140
+ bytes = if message.respond_to?(:get_packed_message)
141
+ packed = message.get_packed_message
142
+ unpack(packed)
143
+ else
144
+ string = String.from_java_bytes(message.get_message)
145
+ string.unpack("C" * string.length)
146
+ end
147
+ @buffer << bytes
148
+ end
149
+
150
+ def close
151
+ end
152
+
153
+ private
154
+
155
+ # @param [String]
156
+ # @return [Array<Fixnum>]
157
+ def unpack(message)
158
+ bytes = []
159
+ string = message.to_s(16)
160
+ string = "0#{s}" if string.length.divmod(2).last > 0
161
+ while string.length > 0
162
+ string_byte = string.slice!(0,2)
163
+ bytes << string_byte.hex
164
+ end
165
+ bytes.reverse
166
+ end
167
+
168
+ end
169
+
170
+ end
171
+
172
+ end
@@ -1,73 +1,70 @@
1
1
  module MIDIJRuby
2
2
 
3
- #
4
- # Module containing methods used by both input and output devices
5
- #
3
+ # Common methods used by both input and output devices
6
4
  module Device
7
5
 
8
- import javax.sound.midi.MidiSystem
9
- import javax.sound.midi.MidiDevice
10
- import javax.sound.midi.MidiEvent
11
- import javax.sound.midi.Receiver
12
-
13
- # has the device been initialized?
14
- attr_reader :enabled,
15
- # unique int id
16
- :id,
17
- # name property from javax.sound.midi.MidiDevice.Info
18
- :name,
19
- # description property from javax.sound.midi.MidiDevice.Info
20
- :description,
21
- # vendor property from javax.sound.midi.MidiDevice.Info
22
- :vendor,
23
- # :input or :output
24
- :type
6
+ attr_reader :enabled, # has the device been initialized?
7
+ :id, # unique int id
8
+ :name, # name property from javax.sound.midi.MidiDevice.Info
9
+ :description, # description property from javax.sound.midi.MidiDevice.Info
10
+ :vendor, # vendor property from javax.sound.midi.MidiDevice.Info
11
+ :type # :input or :output
25
12
 
26
13
  alias_method :enabled?, :enabled
27
14
 
28
- def initialize(id, device, options = {}, &block)
15
+ # @param [Fixnum] The uuid for the given device
16
+ # @param [Java::ComSunMediaSound::MidiInDevice, Java::ComSunMediaSound::MidiOutDevice] device The underlying Java device object
17
+ # @param [Hash] options
18
+ # @option options [String] :description
19
+ # @option options [String] :name
20
+ # @option options [String] :vendor
21
+ def initialize(id, device, options = {})
29
22
  @name = options[:name]
30
23
  @description = options[:description]
31
24
  @vendor = options[:vendor]
32
25
  @id = id
33
26
  @device = device
34
27
 
35
- # cache the type name so that inspecting the class isn't necessary each time
36
- @type = self.class.name.split('::').last.downcase.to_sym
37
-
28
+ @type = get_type
38
29
  @enabled = false
39
30
  end
40
31
 
41
- # select the first device of type <em>type</em>
42
- def self.first(type)
43
- all_by_type[type].first
32
+ # Select the first device of the given direction
33
+ # @param [Symbol] direction
34
+ # @return [Input, Output]
35
+ def self.first(direction)
36
+ all_by_type[direction].first
44
37
  end
45
38
 
46
- # select the last device of type <em>type</em>
47
- def self.last(type)
48
- all_by_type[type].last
39
+ # Select the last device of the given direction
40
+ # @param [Symbol] direction
41
+ # @return [Input, Output]
42
+ def self.last(direction)
43
+ all_by_type[direction].last
49
44
  end
50
45
 
51
- # a Hash of :input and :output devices
46
+ # A hash of :input and :output devices
47
+ # @return [Hash]
52
48
  def self.all_by_type
53
- available_devices = { :input => [], :output => [] }
54
- count = -1
55
- MidiSystem.get_midi_device_info.each do |info|
56
- device = MidiSystem.get_midi_device(info)
57
- opts = { :name => info.get_name,
58
- :description => info.get_description,
59
- :vendor => info.get_vendor }
60
- available_devices[:output] << Output.new(count += 1, device, opts) unless device.get_max_receivers.zero?
61
- available_devices[:input] << Input.new(count += 1, device, opts) unless device.get_max_transmitters.zero?
62
- end
63
- available_devices
49
+ @devices ||= {
50
+ :input => API.get_inputs,
51
+ :output => API.get_outputs
52
+ }
64
53
  end
65
54
 
66
- # all devices of both types
55
+ # All devices of both directions
56
+ # @return [Array<Input, Output>]
67
57
  def self.all
68
58
  all_by_type.values.flatten
69
59
  end
70
60
 
61
+ private
62
+
63
+ # @return [String]
64
+ def get_type
65
+ self.class.name.split('::').last.downcase.to_sym
66
+ end
67
+
71
68
  end
72
69
 
73
- end
70
+ end
@@ -1,108 +1,58 @@
1
1
  module MIDIJRuby
2
2
 
3
- #
4
3
  # Input device class
5
- #
6
4
  class Input
7
5
 
8
- import javax.sound.midi.Transmitter
9
-
10
6
  include Device
11
7
 
12
8
  attr_reader :buffer
13
-
14
- class InputReceiver
15
-
16
- include javax.sound.midi.Receiver
17
- extend Forwardable
18
-
19
- attr_reader :stream
20
-
21
- def initialize
22
- @buf = []
23
- end
24
-
25
- def read
26
- to_return = @buf.dup
27
- @buf.clear
28
- to_return
29
- end
30
-
31
- def send(msg, timestamp = -1)
32
- if msg.respond_to?(:get_packed_msg)
33
- m = msg.get_packed_msg
34
- @buf << unpack(m)
35
- else
36
- str = String.from_java_bytes(msg.get_data)
37
- arr = str.unpack("C" * str.length)
38
- arr.insert(0, msg.get_status)
39
- @buf << arr
40
- end
41
- end
42
-
43
- private
44
-
45
- def unpack(msg)
46
- # there's probably a better way of doing this
47
- o = []
48
- s = msg.to_s(16)
49
- s = "0" + s if s.length.divmod(2).last > 0
50
- while s.length > 0
51
- o << s.slice!(0,2).hex
52
- end
53
- o.reverse
54
- end
55
-
56
- end
57
-
9
+
58
10
  #
59
- # returns an array of MIDI event hashes as such:
11
+ # An array of MIDI event hashes as such:
60
12
  # [
61
13
  # { :data => [144, 60, 100], :timestamp => 1024 },
62
14
  # { :data => [128, 60, 100], :timestamp => 1100 },
63
15
  # { :data => [144, 40, 120], :timestamp => 1200 }
64
16
  # ]
65
17
  #
66
- # the data is an array of Numeric bytes
67
- # the timestamp is the number of millis since this input was enabled
18
+ # The data is an array of numeric bytes
19
+ # The timestamp is the number of millis since this input was enabled
68
20
  #
21
+ # @return [Array<Hash>]
69
22
  def gets
70
- until queued_messages?
71
- end
72
- msgs = queued_messages
23
+ loop until queued_messages?
24
+ messages = queued_messages
73
25
  @pointer = @buffer.length
74
- msgs
26
+ messages
75
27
  end
76
28
  alias_method :read, :gets
77
29
 
78
- # same as gets but returns message data as string of hex digits as such:
30
+ # Same as Input#gets but returns message data as string of hex digits:
79
31
  # [
80
32
  # { :data => "904060", :timestamp => 904 },
81
33
  # { :data => "804060", :timestamp => 1150 },
82
34
  # { :data => "90447F", :timestamp => 1300 }
83
35
  # ]
84
36
  #
85
- #
37
+ # @return [Array<Hash>]
86
38
  def gets_s
87
- msgs = gets
88
- msgs.each { |msg| msg[:data] = numeric_bytes_to_hex_string(msg[:data]) }
89
- msgs
39
+ messages = gets
40
+ messages.each { |message| message[:data] = numeric_bytes_to_hex_string(message[:data]) }
41
+ messages
90
42
  end
91
43
  alias_method :gets_bytestr, :gets_s
92
44
  alias_method :gets_hex, :gets_s
93
45
 
94
- # enable this the input for use; can be passed a block
46
+ # Enable this the input for use; can be passed a block
47
+ # @param [Hash] options
48
+ # @param [Proc] block
49
+ # @return [Input] self
95
50
  def enable(options = {}, &block)
96
- @device.open
97
- @transmitter = @device.get_transmitter
98
- @transmitter.set_receiver(InputReceiver.new)
99
- initialize_buffer
100
- @start_time = Time.now.to_f
101
- spawn_listener!
51
+ initialize_input
102
52
  @enabled = true
103
53
  if block_given?
104
54
  begin
105
- block.call(self)
55
+ yield(self)
106
56
  ensure
107
57
  close
108
58
  end
@@ -113,28 +63,46 @@ module MIDIJRuby
113
63
  alias_method :open, :enable
114
64
  alias_method :start, :enable
115
65
 
116
- # close this input
66
+ # Close this input
67
+ # @return [Boolean]
117
68
  def close
118
69
  @listener.kill
119
- @transmitter.close
120
- @device.close
70
+ API.close_input(@device)
121
71
  @enabled = false
122
72
  end
123
73
 
74
+ # Select the first input
75
+ # @return [Input]
124
76
  def self.first
125
77
  Device.first(:input)
126
78
  end
127
79
 
80
+ # Select the last input
81
+ # @return [Input]
128
82
  def self.last
129
83
  Device.last(:input)
130
84
  end
131
85
 
86
+ # All inputs
87
+ # @return [Array<Input>]
132
88
  def self.all
133
89
  Device.all_by_type[:input]
134
90
  end
135
91
 
136
92
  private
93
+
94
+ # Initialize the input components
95
+ # @return [Boolean]
96
+ def initialize_input
97
+ initialize_buffer
98
+ API.enable_input(@device)
99
+ @start_time = Time.now.to_f
100
+ initialize_listener
101
+ true
102
+ end
137
103
 
104
+ # Initialize the input buffer
105
+ # @return [Boolean]
138
106
  def initialize_buffer
139
107
  @buffer = []
140
108
  @pointer = 0
@@ -144,47 +112,75 @@ module MIDIJRuby
144
112
  end
145
113
  end
146
114
 
115
+ # Get a timestamp for the current time
116
+ # @return [Fixnum]
147
117
  def now
148
- ((Time.now.to_f - @start_time) * 1000)
118
+ now = Time.now.to_f - @start_time
119
+ now * 1000
149
120
  end
150
121
 
151
- # give a message its timestamp and package it in a Hash
152
- def get_message_formatted(raw, time)
153
- { :data => raw, :timestamp => time }
122
+ # A hash of a MIDI message and corresponding timestamp
123
+ # @param [Array<Fixnum>] message
124
+ # @param [Fixnum] timestamp
125
+ # @return [Hash]
126
+ def get_message_formatted(message, timestamp)
127
+ {
128
+ :data => message,
129
+ :timestamp => timestamp
130
+ }
154
131
  end
155
132
 
133
+ # Messages in the buffer
134
+ # @return [Array<Array<Fixnum>>]
156
135
  def queued_messages
157
136
  @buffer.slice(@pointer, @buffer.length - @pointer)
158
137
  end
159
138
 
139
+ # Are there any new messages in the buffer?
140
+ # @return [Boolean]
160
141
  def queued_messages?
161
142
  @pointer < @buffer.length
162
143
  end
163
144
 
164
- # launch a background thread that collects messages
165
- def spawn_listener!
166
- @listener = Thread.fork do
167
- while true
168
- while (msgs = poll_system_buffer).empty?
169
- sleep(1.0/1000)
145
+ # Launch a background thread that collects messages
146
+ # @return [Thread]
147
+ def initialize_listener
148
+ @listener = Thread.new do
149
+ begin
150
+ loop do
151
+ while (messages = API.read_input(@device)).empty?
152
+ sleep(1.0/1000)
153
+ end
154
+ add_to_buffer(messages) unless messages.empty?
170
155
  end
171
- populate_local_buffer(msgs) unless msgs.empty?
156
+ rescue Exception => exception
157
+ Thread.main.raise(exception)
172
158
  end
173
159
  end
160
+ @listener.abort_on_exception = true
161
+ @listener
162
+ end
163
+
164
+ # @param [Array<Array<Fixnum>>]
165
+ # @return [Array<Array<Fixnum>>]
166
+ def add_to_buffer(messages)
167
+ @buffer += messages.compact.map do |message|
168
+ get_message_formatted(message, now)
169
+ end
174
170
  end
175
-
176
- def poll_system_buffer
177
- @transmitter.get_receiver.read
178
- end
179
-
180
- def populate_local_buffer(msgs)
181
- msgs.each { |raw| @buffer << get_message_formatted(raw, now) unless raw.nil? }
182
- end
183
-
171
+
172
+ # Convert an array of numeric bytes to a hex string (eg [0x90, 0x40, 0x40] -> "904040")
173
+ # @param [Array<Fixnum>] bytes
174
+ # @return [String]
184
175
  def numeric_bytes_to_hex_string(bytes)
185
- bytes.map { |b| s = b.to_s(16).upcase; b < 16 ? s = "0" + s : s; s }.join
186
- end
176
+ string_bytes = bytes.map do |byte|
177
+ string = byte.to_s(16).upcase
178
+ string = "0#{string}" if byte < 0x10
179
+ string
180
+ end
181
+ string_bytes.join
182
+ end
187
183
 
188
184
  end
189
185
 
190
- end
186
+ end
@@ -1,61 +1,56 @@
1
1
  module MIDIJRuby
2
2
 
3
- #
4
3
  # Output device class
5
- #
6
4
  class Output
7
- import javax.sound.midi.ShortMessage
8
- import javax.sound.midi.SysexMessage
9
5
 
10
6
  include Device
11
7
 
12
- # close this output
8
+ # Close this output
9
+ # @return [Boolean]
13
10
  def close
14
- @device.close
11
+ API.close_output(@device)
15
12
  @enabled = false
16
13
  end
17
14
 
18
- # sends a MIDI message comprised of a String of hex digits
15
+ # Output the given MIDI message
16
+ # @param [String] data A MIDI message expressed as a string of hex digits
17
+ # @return [Boolean]
19
18
  def puts_s(data)
20
- data = data.dup
21
- output = []
22
- until (str = data.slice!(0,2)).eql?("")
23
- output << str.hex
24
- end
25
- puts_bytes(*output)
19
+ bytes = hex_string_to_numeric_bytes(data)
20
+ puts_bytes(*bytes)
26
21
  end
27
22
  alias_method :puts_bytestr, :puts_s
28
23
  alias_method :puts_hex, :puts_s
29
24
 
30
- # sends a MIDI messages comprised of Numeric bytes
25
+ # Output the given MIDI message
26
+ # @param [*Fixnum] data A MIDI messages expressed as Numeric bytes
27
+ # @return [Boolean]
31
28
  def puts_bytes(*data)
32
- if data.first.eql?(0xF0)
33
- msg = SysexMessage.new
34
- msg.set_message(data.to_java(:byte), data.length)
35
- else
36
- msg = ShortMessage.new
37
- msg.set_message(*data)
38
- end
39
- @device.get_receiver.send(msg, 0)
29
+ API.write_output(@device, data)
40
30
  end
41
31
 
42
- # send a MIDI message of an indeterminant type
43
- def puts(*a)
44
- case a.first
45
- when Array then puts_bytes(*a.first)
46
- when Numeric then puts_bytes(*a)
47
- when String then puts_bytestr(*a)
32
+ # Output the given MIDI message
33
+ # @param [*Fixnum, *String] args
34
+ # @return [Boolean]
35
+ def puts(*args)
36
+ case args.first
37
+ when Array then puts_bytes(*args.first)
38
+ when Numeric then puts_bytes(*args)
39
+ when String then puts_bytestr(*args)
48
40
  end
49
41
  end
50
42
  alias_method :write, :puts
51
43
 
52
- # enable this device; also takes a block
44
+ # Enable this device; also takes a block
45
+ # @param [Hash] options
46
+ # @param [Proc] block
47
+ # @return [Output]
53
48
  def enable(options = {}, &block)
54
- @device.open
49
+ API.enable_output(@device)
55
50
  @enabled = true
56
- unless block.nil?
51
+ if block_given?
57
52
  begin
58
- block.call(self)
53
+ yield(self)
59
54
  ensure
60
55
  close
61
56
  end
@@ -66,17 +61,38 @@ module MIDIJRuby
66
61
  alias_method :open, :enable
67
62
  alias_method :start, :enable
68
63
 
64
+ # Select the first output
65
+ # @return [Output]
69
66
  def self.first
70
67
  Device.first(:output)
71
68
  end
72
69
 
70
+ # Select the last output
71
+ # @return [Output]
73
72
  def self.last
74
73
  Device.last(:output)
75
74
  end
76
75
 
76
+ # All outputs
77
+ # @return [Array<Output>]
77
78
  def self.all
78
79
  Device.all_by_type[:output]
79
80
  end
81
+
82
+ private
83
+
84
+ # Convert a hex string to numeric bytes (eg "904040" -> [0x90, 0x40, 0x40])
85
+ # @param [String] string
86
+ # @return [Array<Fixnum>]
87
+ def hex_string_to_numeric_bytes(string)
88
+ string = string.dup
89
+ bytes = []
90
+ until (string_byte = string.slice!(0,2)) == ""
91
+ bytes << string_byte.hex
92
+ end
93
+ bytes
94
+ end
95
+
80
96
  end
81
97
 
82
- end
98
+ end
data/lib/midi-jruby.rb CHANGED
@@ -1,15 +1,23 @@
1
- require 'java'
2
- require 'forwardable'
3
-
4
- #
5
- # Set of modules and classes for interacting with javax.sound.midi
1
+ # Realtime MIDI IO in JRuby using the javax.sound.midi API
6
2
  #
3
+ # Ari Russo
4
+ # (c) 2011-2014
5
+ # Licensed under Apache 2.0
6
+
7
+ # libs
8
+ require "java"
9
+ require "forwardable"
10
+
11
+ # modules
12
+ require "midi-jruby/api"
13
+ require "midi-jruby/device"
14
+
15
+ # classes
16
+ require "midi-jruby/input"
17
+ require "midi-jruby/output"
18
+
7
19
  module MIDIJRuby
8
20
 
9
- VERSION = "0.0.12"
21
+ VERSION = "0.1.1"
10
22
 
11
23
  end
12
-
13
- require 'midi-jruby/device'
14
- require 'midi-jruby/input'
15
- require 'midi-jruby/output'
data/test/helper.rb ADDED
@@ -0,0 +1,60 @@
1
+ dir = File.dirname(File.expand_path(__FILE__))
2
+ $LOAD_PATH.unshift dir + "/../lib"
3
+
4
+ require "test/unit"
5
+ require "mocha/test_unit"
6
+ require "shoulda-context"
7
+ require "midi-jruby"
8
+
9
+ module TestHelper
10
+
11
+ extend self
12
+
13
+ # http://stackoverflow.com/questions/8148898/java-midi-in-mac-osx-broken
14
+ def sysex_ok?
15
+ ENV["_system_name"] != "OSX"
16
+ end
17
+
18
+ def bytestrs_to_ints(arr)
19
+ data = arr.map { |m| m[:data] }.join
20
+ output = []
21
+ until (bytestr = data.slice!(0,2)).eql?("")
22
+ output << bytestr.hex
23
+ end
24
+ output
25
+ end
26
+
27
+ def numeric_messages
28
+ messages = [
29
+ [0x90, 100, 100], # note on
30
+ [0x90, 43, 100], # note on
31
+ [0x90, 76, 100], # note on
32
+ [0x90, 60, 100], # note on
33
+ [0x80, 100, 100] # note off
34
+ ]
35
+ if sysex_ok?
36
+ messages << [0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7]
37
+ end
38
+ messages
39
+ end
40
+
41
+ def string_messages
42
+ messages = [
43
+ "906440", # note on
44
+ "804340" # note off
45
+ ]
46
+ if sysex_ok?
47
+ messages << "F04110421240007F0041F7"
48
+ end
49
+ messages
50
+ end
51
+
52
+ def input
53
+ MIDIJRuby::Input.first
54
+ end
55
+
56
+ def output
57
+ MIDIJRuby::Output.first
58
+ end
59
+
60
+ end
@@ -0,0 +1,46 @@
1
+ require "helper"
2
+
3
+ class MIDIJRuby::InputBufferTest < Test::Unit::TestCase
4
+
5
+ context "MIDIJRuby" do
6
+
7
+ setup do
8
+ @input = TestHelper.input.open
9
+ @output = TestHelper.output.open
10
+ @input.buffer.clear
11
+ @pointer = 0
12
+ end
13
+
14
+ teardown do
15
+ @input.close
16
+ @output.close
17
+ end
18
+
19
+ context "Source#buffer" do
20
+
21
+ setup do
22
+ @messages = TestHelper.numeric_messages
23
+ @messages_arr = @messages.inject(&:+).flatten
24
+ @received_arr = []
25
+ end
26
+
27
+ should "have the correct messages in the buffer" do
28
+ bytes = []
29
+ @messages.each do |message|
30
+ p "sending: #{message}"
31
+ @output.puts(message)
32
+ bytes += message
33
+
34
+ sleep(1)
35
+
36
+ buffer = @input.buffer.map { |m| m[:data] }.flatten
37
+ p "received: #{buffer.to_s}"
38
+ assert_equal(bytes, buffer)
39
+ end
40
+ assert_equal(bytes.length, @input.buffer.map { |m| m[:data] }.flatten.length)
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
data/test/io_test.rb ADDED
@@ -0,0 +1,80 @@
1
+ require "helper"
2
+
3
+ class MIDIJRuby::IOTest < Test::Unit::TestCase
4
+
5
+ # These tests assume that TestOutput is connected to TestInput
6
+ context "MIDIJRuby" do
7
+
8
+ setup do
9
+ @input = TestHelper.input.open
10
+ @output = TestHelper.output.open
11
+ @input.buffer.clear
12
+ @pointer = 0
13
+ end
14
+
15
+ teardown do
16
+ @input.close
17
+ @output.close
18
+ end
19
+
20
+ context "full IO" do
21
+
22
+ context "using Arrays" do
23
+
24
+ setup do
25
+ @messages = TestHelper.numeric_messages
26
+ @messages_arr = @messages.inject(&:+).flatten
27
+ @received_arr = []
28
+ end
29
+
30
+ should "do IO" do
31
+ @messages.each do |message|
32
+
33
+ p "sending: #{message}"
34
+
35
+ @output.puts(message)
36
+ sleep(1)
37
+ received = @input.gets.map { |m| m[:data] }.flatten
38
+
39
+ p "received: #{received}"
40
+
41
+ assert_equal(@messages_arr.slice(@pointer, received.length), received)
42
+ @pointer += received.length
43
+ @received_arr += received
44
+ end
45
+ assert_equal(@messages_arr.length, @received_arr.length)
46
+ end
47
+ end
48
+
49
+ context "using byte Strings" do
50
+
51
+ setup do
52
+ @messages = TestHelper.string_messages
53
+ @messages_str = @messages.join
54
+ @received_str = ""
55
+ end
56
+
57
+ should "do IO" do
58
+ @messages.each do |message|
59
+
60
+ p "sending: #{message}"
61
+
62
+ @output.puts(message)
63
+ sleep(1)
64
+ received = @input.gets_bytestr.map { |m| m[:data] }.flatten.join
65
+ p "received: #{received}"
66
+
67
+ assert_equal(@messages_str.slice(@pointer, received.length), received)
68
+ @pointer += received.length
69
+ @received_str += received
70
+ end
71
+ assert_equal(@messages_str, @received_str)
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+
80
+ end
metadata CHANGED
@@ -1,62 +1,54 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: midi-jruby
3
- version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.12
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
6
5
  platform: ruby
7
- authors:
8
- - Ari Russo
9
- autorequire:
6
+ authors:
7
+ - Ari Russo
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
-
13
- date: 2012-03-04 00:00:00 -05:00
14
- default_executable:
11
+ date: 2014-09-28 00:00:00.000000000 Z
15
12
  dependencies: []
16
-
17
- description: Simple MIDI wrapper for realtime IO in JRuby. Uses javax.sound.midi. Note that this library requires JRuby to be run in '1.9 mode'
18
- email:
19
- - ari.russo@gmail.com
13
+ description: Realtime MIDI IO with JRuby using the javax.sound.midi API
14
+ email:
15
+ - ari.russo@gmail.com
20
16
  executables: []
21
-
22
17
  extensions: []
23
-
24
18
  extra_rdoc_files: []
25
-
26
- files:
27
- - lib/midi-jruby.rb
28
- - lib/midi-jruby/device.rb
29
- - lib/midi-jruby/input.rb
30
- - lib/midi-jruby/output.rb
31
- - LICENSE
32
- - README.rdoc
33
- has_rdoc: true
19
+ files:
20
+ - lib/midi-jruby.rb
21
+ - lib/midi-jruby/api.rb
22
+ - lib/midi-jruby/device.rb
23
+ - lib/midi-jruby/input.rb
24
+ - lib/midi-jruby/output.rb
25
+ - test/helper.rb
26
+ - test/input_buffer_test.rb
27
+ - test/io_test.rb
28
+ - LICENSE
29
+ - README.md
34
30
  homepage: http://github.com/arirusso/midi-jruby
35
- licenses: []
36
-
37
- post_install_message:
31
+ licenses:
32
+ - Apache 2.0
33
+ metadata: {}
34
+ post_install_message:
38
35
  rdoc_options: []
39
-
40
- require_paths:
41
- - lib
42
- required_ruby_version: !ruby/object:Gem::Requirement
43
- none: false
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: "0"
48
- required_rubygems_version: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: 1.3.6
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.6
54
48
  requirements: []
55
-
56
49
  rubyforge_project: midi-jruby
57
- rubygems_version: 1.5.1
58
- signing_key:
59
- specification_version: 3
60
- summary: Simple MIDI wrapper for realtime IO in JRuby
50
+ rubygems_version: 2.1.9
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Realtime MIDI IO with JRuby
61
54
  test_files: []
62
-
data/README.rdoc DELETED
@@ -1,52 +0,0 @@
1
- = midi-jruby
2
-
3
- == Summary
4
-
5
- Simple MIDI wrapper for realtime IO in JRuby. Uses javax.sound.midi.
6
-
7
- In the interest of allowing people on other platforms to utilize your code, you should consider using {unimidi}[http://github.com/arirusso/unimidi]. Unimidi is a platform independent wrapper which implements midi-jruby for users who are using jruby.
8
-
9
- == Features
10
-
11
- * Input and output on multiple devices concurrently
12
- * Agnostically handle different MIDI Message types (including SysEx)
13
- * Timestamped input events
14
-
15
- == Install
16
-
17
- * gem install midi-jruby
18
-
19
- == Usage
20
-
21
- This library requires that JRuby be run in '1.9 mode'. This is normally done by passing --1.9 to JRuby at the command line
22
-
23
- == Examples
24
-
25
- * {input}[http://github.com/arirusso/midi-jruby/blob/master/examples/input.rb]
26
- * {output}[http://github.com/arirusso/midi-jruby/blob/master/examples/output.rb]
27
-
28
- == Issues
29
-
30
- There is an issue that causes javax.sound.midi not to be able to send SysEx messages in some OSX Snow Leopard versions.
31
-
32
- == Tests
33
-
34
- Use
35
-
36
- jruby --1.9 -S rake test
37
-
38
- * please see {test/config.rb}[http://github.com/arirusso/midi-jruby/blob/master/test/config.rb] before running tests
39
-
40
- == Documentation
41
-
42
- * {rdoc}[http://rdoc.info/gems/midi-jruby]
43
-
44
- == Author
45
-
46
- {Ari Russo}[http://github.com/arirusso] <ari.russo at gmail.com>
47
-
48
- == License
49
-
50
- Apache 2.0, See the file LICENSE
51
-
52
- Copyright (c) 2011 Ari Russo