midi-jruby 0.0.12

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.
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2010-2011 Ari Russo
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.rdoc ADDED
@@ -0,0 +1,52 @@
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
@@ -0,0 +1,73 @@
1
+ module MIDIJRuby
2
+
3
+ #
4
+ # Module containing methods used by both input and output devices
5
+ #
6
+ module Device
7
+
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
25
+
26
+ alias_method :enabled?, :enabled
27
+
28
+ def initialize(id, device, options = {}, &block)
29
+ @name = options[:name]
30
+ @description = options[:description]
31
+ @vendor = options[:vendor]
32
+ @id = id
33
+ @device = device
34
+
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
+
38
+ @enabled = false
39
+ end
40
+
41
+ # select the first device of type <em>type</em>
42
+ def self.first(type)
43
+ all_by_type[type].first
44
+ end
45
+
46
+ # select the last device of type <em>type</em>
47
+ def self.last(type)
48
+ all_by_type[type].last
49
+ end
50
+
51
+ # a Hash of :input and :output devices
52
+ 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
64
+ end
65
+
66
+ # all devices of both types
67
+ def self.all
68
+ all_by_type.values.flatten
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,190 @@
1
+ module MIDIJRuby
2
+
3
+ #
4
+ # Input device class
5
+ #
6
+ class Input
7
+
8
+ import javax.sound.midi.Transmitter
9
+
10
+ include Device
11
+
12
+ 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
+
58
+ #
59
+ # returns an array of MIDI event hashes as such:
60
+ # [
61
+ # { :data => [144, 60, 100], :timestamp => 1024 },
62
+ # { :data => [128, 60, 100], :timestamp => 1100 },
63
+ # { :data => [144, 40, 120], :timestamp => 1200 }
64
+ # ]
65
+ #
66
+ # the data is an array of Numeric bytes
67
+ # the timestamp is the number of millis since this input was enabled
68
+ #
69
+ def gets
70
+ until queued_messages?
71
+ end
72
+ msgs = queued_messages
73
+ @pointer = @buffer.length
74
+ msgs
75
+ end
76
+ alias_method :read, :gets
77
+
78
+ # same as gets but returns message data as string of hex digits as such:
79
+ # [
80
+ # { :data => "904060", :timestamp => 904 },
81
+ # { :data => "804060", :timestamp => 1150 },
82
+ # { :data => "90447F", :timestamp => 1300 }
83
+ # ]
84
+ #
85
+ #
86
+ def gets_s
87
+ msgs = gets
88
+ msgs.each { |msg| msg[:data] = numeric_bytes_to_hex_string(msg[:data]) }
89
+ msgs
90
+ end
91
+ alias_method :gets_bytestr, :gets_s
92
+ alias_method :gets_hex, :gets_s
93
+
94
+ # enable this the input for use; can be passed a block
95
+ 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!
102
+ @enabled = true
103
+ if block_given?
104
+ begin
105
+ block.call(self)
106
+ ensure
107
+ close
108
+ end
109
+ else
110
+ self
111
+ end
112
+ end
113
+ alias_method :open, :enable
114
+ alias_method :start, :enable
115
+
116
+ # close this input
117
+ def close
118
+ @listener.kill
119
+ @transmitter.close
120
+ @device.close
121
+ @enabled = false
122
+ end
123
+
124
+ def self.first
125
+ Device.first(:input)
126
+ end
127
+
128
+ def self.last
129
+ Device.last(:input)
130
+ end
131
+
132
+ def self.all
133
+ Device.all_by_type[:input]
134
+ end
135
+
136
+ private
137
+
138
+ def initialize_buffer
139
+ @buffer = []
140
+ @pointer = 0
141
+ def @buffer.clear
142
+ @pointer = 0
143
+ super
144
+ end
145
+ end
146
+
147
+ def now
148
+ ((Time.now.to_f - @start_time) * 1000)
149
+ end
150
+
151
+ # give a message its timestamp and package it in a Hash
152
+ def get_message_formatted(raw, time)
153
+ { :data => raw, :timestamp => time }
154
+ end
155
+
156
+ def queued_messages
157
+ @buffer.slice(@pointer, @buffer.length - @pointer)
158
+ end
159
+
160
+ def queued_messages?
161
+ @pointer < @buffer.length
162
+ end
163
+
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)
170
+ end
171
+ populate_local_buffer(msgs) unless msgs.empty?
172
+ end
173
+ end
174
+ 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
+
184
+ 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
187
+
188
+ end
189
+
190
+ end
@@ -0,0 +1,82 @@
1
+ module MIDIJRuby
2
+
3
+ #
4
+ # Output device class
5
+ #
6
+ class Output
7
+ import javax.sound.midi.ShortMessage
8
+ import javax.sound.midi.SysexMessage
9
+
10
+ include Device
11
+
12
+ # close this output
13
+ def close
14
+ @device.close
15
+ @enabled = false
16
+ end
17
+
18
+ # sends a MIDI message comprised of a String of hex digits
19
+ 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)
26
+ end
27
+ alias_method :puts_bytestr, :puts_s
28
+ alias_method :puts_hex, :puts_s
29
+
30
+ # sends a MIDI messages comprised of Numeric bytes
31
+ 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)
40
+ end
41
+
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)
48
+ end
49
+ end
50
+ alias_method :write, :puts
51
+
52
+ # enable this device; also takes a block
53
+ def enable(options = {}, &block)
54
+ @device.open
55
+ @enabled = true
56
+ unless block.nil?
57
+ begin
58
+ block.call(self)
59
+ ensure
60
+ close
61
+ end
62
+ else
63
+ self
64
+ end
65
+ end
66
+ alias_method :open, :enable
67
+ alias_method :start, :enable
68
+
69
+ def self.first
70
+ Device.first(:output)
71
+ end
72
+
73
+ def self.last
74
+ Device.last(:output)
75
+ end
76
+
77
+ def self.all
78
+ Device.all_by_type[:output]
79
+ end
80
+ end
81
+
82
+ end
data/lib/midi-jruby.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'java'
2
+ require 'forwardable'
3
+
4
+ #
5
+ # Set of modules and classes for interacting with javax.sound.midi
6
+ #
7
+ module MIDIJRuby
8
+
9
+ VERSION = "0.0.12"
10
+
11
+ end
12
+
13
+ require 'midi-jruby/device'
14
+ require 'midi-jruby/input'
15
+ require 'midi-jruby/output'
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: midi-jruby
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.12
6
+ platform: ruby
7
+ authors:
8
+ - Ari Russo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-03-04 00:00:00 -05:00
14
+ default_executable:
15
+ 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
20
+ executables: []
21
+
22
+ extensions: []
23
+
24
+ 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
34
+ homepage: http://github.com/arirusso/midi-jruby
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ 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
54
+ requirements: []
55
+
56
+ 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
61
+ test_files: []
62
+