jsound 0.1.0-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.
- data/.yardopts +3 -0
- data/LICENSE.txt +27 -0
- data/README.md +96 -0
- data/Rakefile +19 -0
- data/examples/harmonizer.rb +25 -0
- data/examples/launchpad/launchpad.rb +65 -0
- data/examples/launchpad/launchpad_generator.rb +111 -0
- data/examples/list_devices.rb +6 -0
- data/examples/monitor.rb +15 -0
- data/examples/notes.rb +90 -0
- data/examples/transposer.rb +20 -0
- data/lib/jsound.rb +4 -0
- data/lib/jsound/convert.rb +30 -0
- data/lib/jsound/midi.rb +77 -0
- data/lib/jsound/midi/device.rb +72 -0
- data/lib/jsound/midi/device_list.rb +157 -0
- data/lib/jsound/midi/devices/generator.rb +22 -0
- data/lib/jsound/midi/devices/input_device.rb +51 -0
- data/lib/jsound/midi/devices/jdevice.rb +100 -0
- data/lib/jsound/midi/devices/monitor.rb +18 -0
- data/lib/jsound/midi/devices/output_device.rb +36 -0
- data/lib/jsound/midi/devices/recorder.rb +56 -0
- data/lib/jsound/midi/devices/repeater.rb +28 -0
- data/lib/jsound/midi/devices/transformer.rb +30 -0
- data/lib/jsound/midi/message.rb +165 -0
- data/lib/jsound/midi/message_builder.rb +52 -0
- data/lib/jsound/midi/messages/channel_pressure.rb +26 -0
- data/lib/jsound/midi/messages/control_change.rb +29 -0
- data/lib/jsound/midi/messages/note_off.rb +10 -0
- data/lib/jsound/midi/messages/note_on.rb +29 -0
- data/lib/jsound/midi/messages/pitch_bend.rb +43 -0
- data/lib/jsound/midi/messages/poly_pressure.rb +29 -0
- data/lib/jsound/midi/messages/program_change.rb +27 -0
- data/lib/jsound/type_from_class_name.rb +23 -0
- data/spec/jsound/convert_spec.rb +68 -0
- data/spec/jsound/midi/device_spec.rb +75 -0
- data/spec/jsound/midi/devices/generator_spec.rb +21 -0
- data/spec/jsound/midi/devices/output_device_spec.rb +22 -0
- data/spec/jsound/midi/devices/recorder_spec.rb +88 -0
- data/spec/jsound/midi/devices/repeater_device_spec.rb +19 -0
- data/spec/jsound/midi/devices/transformer_spec.rb +20 -0
- data/spec/jsound/midi/devlice_list_spec.rb +60 -0
- data/spec/jsound/midi/message_builder_spec.rb +22 -0
- data/spec/jsound/midi/message_spec.rb +30 -0
- data/spec/jsound/midi/messages/note_off_spec.rb +62 -0
- data/spec/jsound/midi/messages/note_on_spec.rb +109 -0
- data/spec/jsound/midi/messages/pitch_bend_spec.rb +88 -0
- data/spec/jsound/midi_spec.rb +33 -0
- data/spec/jsound/type_from_class_name_spec.rb +26 -0
- data/spec/spec_helper.rb +23 -0
- metadata +103 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'jsound'
|
4
|
+
include JSound::Midi
|
5
|
+
include Devices
|
6
|
+
|
7
|
+
transposer = Transformer.new do |message|
|
8
|
+
message.pitch += 24 if message.respond_to? :pitch # transpose up two octaves
|
9
|
+
message
|
10
|
+
end
|
11
|
+
|
12
|
+
# Adjust the INPUTS and OUTPUTS as needed to use the devices you want:
|
13
|
+
INPUTS.open_first >> transposer >> OUTPUTS.open_first
|
14
|
+
# For example, to send my Akai keyboard through the harmonizer to the OS X IAC bus, I can do:
|
15
|
+
# INPUTS.Akai >> transposer >> OUTPUTS.IAC
|
16
|
+
|
17
|
+
# force the script to keep running (MIDI devices run in a background thread)
|
18
|
+
while(true)
|
19
|
+
sleep 5
|
20
|
+
end
|
data/lib/jsound.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module JSound
|
2
|
+
|
3
|
+
# Helper methods for converting MIDI data values
|
4
|
+
module Convert
|
5
|
+
|
6
|
+
# The maximum value for unsigned 14-bit integer
|
7
|
+
MAX_14BIT_VALUE = 16383 # == 127 + (127 << 7)
|
8
|
+
|
9
|
+
# Convert a single integer to a [least_significant, most_significant] pair of 7-bit ints
|
10
|
+
def to_7bit(value)
|
11
|
+
[value & 127, (value >> 7) & 127]
|
12
|
+
end
|
13
|
+
|
14
|
+
# Convert a [least_significant, most_significant] pair of 7-bit ints to a single integer
|
15
|
+
def from_7bit(lsb, msb)
|
16
|
+
lsb + (msb << 7)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Scales a float from the range [-1.0, 1.0] to the integer range [0, 16383]
|
20
|
+
def normalized_float_to_14bit(float)
|
21
|
+
(MAX_14BIT_VALUE*(float+1)/2).round
|
22
|
+
end
|
23
|
+
|
24
|
+
# Make all methods be module functions (accessible by sending the method name to module directly)
|
25
|
+
instance_methods.each do |method|
|
26
|
+
module_function method
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/jsound/midi.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'jsound/midi/message'
|
2
|
+
require 'jsound/midi/messages/channel_pressure'
|
3
|
+
require 'jsound/midi/messages/control_change'
|
4
|
+
require 'jsound/midi/messages/note_on'
|
5
|
+
require 'jsound/midi/messages/note_off'
|
6
|
+
require 'jsound/midi/messages/pitch_bend'
|
7
|
+
require 'jsound/midi/messages/poly_pressure'
|
8
|
+
require 'jsound/midi/messages/program_change'
|
9
|
+
|
10
|
+
require 'jsound/midi/message_builder'
|
11
|
+
|
12
|
+
require 'jsound/midi/device'
|
13
|
+
require 'jsound/midi/devices/generator'
|
14
|
+
require 'jsound/midi/devices/jdevice'
|
15
|
+
require 'jsound/midi/devices/input_device'
|
16
|
+
require 'jsound/midi/devices/output_device'
|
17
|
+
require 'jsound/midi/devices/monitor'
|
18
|
+
require 'jsound/midi/devices/recorder'
|
19
|
+
require 'jsound/midi/devices/repeater'
|
20
|
+
require 'jsound/midi/devices/transformer'
|
21
|
+
|
22
|
+
require 'jsound/midi/device_list'
|
23
|
+
|
24
|
+
|
25
|
+
module JSound
|
26
|
+
|
27
|
+
# Module containing all MIDI functionality.
|
28
|
+
#
|
29
|
+
# Also provies the core interface for accessing MIDI devices, see the device list constants defined here.
|
30
|
+
#
|
31
|
+
module Midi
|
32
|
+
include_package 'javax.sound.midi'
|
33
|
+
|
34
|
+
# All MIDI devices
|
35
|
+
# @return [DeviceList]
|
36
|
+
DEVICES = DeviceList.new
|
37
|
+
|
38
|
+
# MIDI input devices
|
39
|
+
# @return [DeviceList] a list of {Devices::InputDevice}s
|
40
|
+
INPUTS = DeviceList.new
|
41
|
+
|
42
|
+
# MIDI output devices
|
43
|
+
# @return [DeviceList] a list of {Devices::OutputDevice}s
|
44
|
+
OUTPUTS = DeviceList.new
|
45
|
+
|
46
|
+
# MIDI synthesizer devices
|
47
|
+
# @return [DeviceList]
|
48
|
+
SYNTHESIZERS = SYNTHS = DeviceList.new
|
49
|
+
|
50
|
+
# MIDI sequencer devices
|
51
|
+
# @return [DeviceList]
|
52
|
+
SEQUENCERS = DeviceList.new
|
53
|
+
|
54
|
+
# Refresh the list of connected devices.
|
55
|
+
# @note this happens automatically when JSound is required.
|
56
|
+
def refresh_devices
|
57
|
+
[DEVICES,INPUTS,OUTPUTS,SYNTHESIZERS,SEQUENCERS].each{|collection| collection.clear}
|
58
|
+
MidiSystem.getMidiDeviceInfo.each do |device_info|
|
59
|
+
java_device = MidiSystem.getMidiDevice(device_info)
|
60
|
+
device = Devices::JDevice.from_java(java_device)
|
61
|
+
case device.type
|
62
|
+
when :sequencer then SEQUENCERS << device
|
63
|
+
when :synthesizer then SYNTHESIZERS << device
|
64
|
+
when :input then INPUTS << device
|
65
|
+
when :output then OUTPUTS << device
|
66
|
+
end
|
67
|
+
DEVICES << device
|
68
|
+
end
|
69
|
+
DEVICES
|
70
|
+
end
|
71
|
+
module_function :refresh_devices
|
72
|
+
|
73
|
+
# Refresh devices automatically when loaded:
|
74
|
+
refresh_devices()
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
|
4
|
+
# A device that can transmit and/or receive messages (typically MIDI messages).
|
5
|
+
# This default implementation simply passes through all messages.
|
6
|
+
class Device
|
7
|
+
include JSound::Mixins::TypeFromClassName
|
8
|
+
|
9
|
+
# Open the device and allocate the needed resources so that it can send and receive messages
|
10
|
+
# @note this operation is typically only relevant for Java-based devices such as {Devices::InputDevice} and {Devices::OutputDevice}
|
11
|
+
# @see DeviceList#open
|
12
|
+
def open
|
13
|
+
end
|
14
|
+
|
15
|
+
# return true if this device is currently open
|
16
|
+
def open?
|
17
|
+
# typically, ruby devices are always open, subclasses might not be
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# Close the device and free up any resources used by this device.
|
22
|
+
# @note this operation is typically only relevant for Java-based devices such as {Devices::InputDevice} and {Devices::OutputDevice}
|
23
|
+
def close
|
24
|
+
end
|
25
|
+
|
26
|
+
def type
|
27
|
+
# The base Device behaves like a 'pass through'
|
28
|
+
@type ||= (self.class == Device ? :pass_through : self.class.type)
|
29
|
+
end
|
30
|
+
|
31
|
+
# the device connected to this device's output
|
32
|
+
# @return [Device] the connected device, or nil if nothing is connected
|
33
|
+
def output
|
34
|
+
@output
|
35
|
+
end
|
36
|
+
|
37
|
+
# connect a device as the output for this device
|
38
|
+
# @param [Device] the device to connect, or nil to disconnect the currently connected device
|
39
|
+
# @see {#>>}
|
40
|
+
def output= device
|
41
|
+
@output = device
|
42
|
+
end
|
43
|
+
|
44
|
+
# Connect a device as the output for this device. shortcut for {#output=}
|
45
|
+
# @param [Device] the device to connect, or nil to disconnect the currently connected device
|
46
|
+
# @see {#output=}
|
47
|
+
def >> device
|
48
|
+
self.output= device
|
49
|
+
end
|
50
|
+
|
51
|
+
# Send a message to this device
|
52
|
+
# @param [Message]
|
53
|
+
# @see #<=
|
54
|
+
def message(message)
|
55
|
+
# default behavior is to pass the message to any connected output
|
56
|
+
@output.message(message) if @output
|
57
|
+
end
|
58
|
+
|
59
|
+
# send a message to this device. shortcut for {#message}
|
60
|
+
# @see #message
|
61
|
+
def <=(message)
|
62
|
+
message(message)
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
"MIDI #{type} device"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
|
4
|
+
# A collection of MIDI {Device}s that provides various methods to lookup (and optionally auto-open) specific devices
|
5
|
+
#
|
6
|
+
# @see Midi the Midi module for constants to access your system devices
|
7
|
+
#
|
8
|
+
class DeviceList
|
9
|
+
|
10
|
+
# The devices within this collection.
|
11
|
+
attr_reader :devices
|
12
|
+
|
13
|
+
def initialize(list=[])
|
14
|
+
@devices = list
|
15
|
+
end
|
16
|
+
|
17
|
+
# Find the first {Device} matching the criteria
|
18
|
+
#
|
19
|
+
# @param criteria matches description against for a Regexp argument, matches the device field against the value for a Hash argument, otherwise checks for an equal description
|
20
|
+
# @return [Device] the first matching {Device} or nil if nothing matched
|
21
|
+
# @note I/O devices need to be opened before they can be used (see {Device#open})
|
22
|
+
# @see #find_all
|
23
|
+
# @see #open
|
24
|
+
# @example {OUTPUTS}.find "IAC Driver Bus 1" #=> find the first output with the exact description "IAC Driver Bus 1"
|
25
|
+
# @example {OUTPUTS}.find /IAC/ #=> find the first output with a description containing "IAC"
|
26
|
+
# @example {OUTPUTS}.find :vendor => "Apple Inc." #=> find the first output with the exact vendor field "Apple Inc."
|
27
|
+
# @example {OUTPUTS}.find :vendor => /Apple / #=> find the first output with a vendor field containing "Apple"
|
28
|
+
#
|
29
|
+
def find(criteria)
|
30
|
+
search :find, criteria
|
31
|
+
end
|
32
|
+
|
33
|
+
# Find all {Device}s matching the criteria
|
34
|
+
#
|
35
|
+
# @param criteria matches description against for a Regexp argument, matches the device field against the value for a Hash argument, otherwise checks for an equal description
|
36
|
+
# @return [Array] an Array of all matching {Device}s or [] if nothing matched
|
37
|
+
# @note I/O devices need to be opened before they can be used (see {Device#open})
|
38
|
+
# @see #find for examples
|
39
|
+
# @see #open
|
40
|
+
#
|
41
|
+
def find_all(criteria)
|
42
|
+
search :find_all, criteria
|
43
|
+
end
|
44
|
+
|
45
|
+
# Acts like Array#[] for Numeric and Range arguments. Otherwise acts like {#find_all}.
|
46
|
+
#
|
47
|
+
# @see #find_all
|
48
|
+
#
|
49
|
+
def [](criteria)
|
50
|
+
if criteria.kind_of? Fixnum or criteria.kind_of? Range
|
51
|
+
@devices[criteria]
|
52
|
+
else
|
53
|
+
find_all(criteria)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Find the first device matching the argument and open it.
|
58
|
+
#
|
59
|
+
# @param device matches description against for a Regexp argument, matches the device field against the value for a Hash argument, otherwise checks for an equal description
|
60
|
+
# @return [Device] the device
|
61
|
+
# @raise an error if no device can be found
|
62
|
+
# @see #find
|
63
|
+
# @see #find_all
|
64
|
+
#
|
65
|
+
def open(device)
|
66
|
+
device = find device unless device.is_a? Device
|
67
|
+
if device
|
68
|
+
device.open
|
69
|
+
else
|
70
|
+
raise "Device note found: #{device}"
|
71
|
+
end
|
72
|
+
device
|
73
|
+
end
|
74
|
+
|
75
|
+
# Find and open the first device with a description matching the argument.
|
76
|
+
#
|
77
|
+
# @return [Device] the device
|
78
|
+
# @raise an error if no device can be found
|
79
|
+
# @see #open
|
80
|
+
#
|
81
|
+
def /(regexp)
|
82
|
+
regexp = Regexp.new(regexp.to_s, Regexp::IGNORECASE) if not regexp.kind_of? Regexp
|
83
|
+
open regexp
|
84
|
+
end
|
85
|
+
|
86
|
+
# open the first device in this list
|
87
|
+
#
|
88
|
+
# @return [Device] the device
|
89
|
+
# @raise an error if no device can be found
|
90
|
+
# @see #open
|
91
|
+
#
|
92
|
+
def open_first
|
93
|
+
open @devices.first
|
94
|
+
end
|
95
|
+
|
96
|
+
# open and return the last device in this list
|
97
|
+
#
|
98
|
+
# @return [Device] the device
|
99
|
+
# @raise an error if no device can be found
|
100
|
+
# @see #open
|
101
|
+
#
|
102
|
+
def open_last
|
103
|
+
open @devices.first
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_s
|
107
|
+
@devices.join("\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
########################
|
111
|
+
private
|
112
|
+
|
113
|
+
def search(iterator, criteria)
|
114
|
+
field, target_value = field_and_target_value_for(criteria)
|
115
|
+
matcher = matcher_for(target_value)
|
116
|
+
@devices.send(iterator) do |device|
|
117
|
+
device.send(field).send(matcher, target_value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def field_and_target_value_for(criteria)
|
122
|
+
if criteria.is_a? Hash
|
123
|
+
first_key = criteria.keys.first
|
124
|
+
return first_key, criteria[first_key]
|
125
|
+
else
|
126
|
+
return :description, criteria
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def matcher_for(target_value)
|
131
|
+
case target_value
|
132
|
+
when Regexp then '=~'
|
133
|
+
else '=='
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Pass method calls through to the underlying Array.
|
138
|
+
# If Array doesn't understand the method, call {#open} with a case-insensitive regexp match
|
139
|
+
# against description, treating underscore as a wildcard.
|
140
|
+
#
|
141
|
+
# @example # {INPUTS}.Akai => open the first "Akai" input device connected to this computer
|
142
|
+
# @example # {INPUTS}.Akai_2 => open an input device matching /Akai.*2/i
|
143
|
+
#
|
144
|
+
def method_missing(sym, *args, &block)
|
145
|
+
if @devices.respond_to? sym
|
146
|
+
@devices.send(sym, *args, &block)
|
147
|
+
elsif args.length == 0 and block.nil?
|
148
|
+
self.open /#{sym.to_s.sub '_','.*'}/i
|
149
|
+
else
|
150
|
+
super
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
module Devices
|
4
|
+
|
5
|
+
# A device which provides methods for generating MIDI command messages.
|
6
|
+
# See JSound::Midi::Messages::Builder for the available methods.
|
7
|
+
class Generator < Device
|
8
|
+
|
9
|
+
# For all the methods defined in the MessageBuilder module (note_on, pitch_bend, control_change, etc),
|
10
|
+
# define a corresponding device method that constructs the message and sends the connected device
|
11
|
+
MessageBuilder.private_instance_methods.each do |method|
|
12
|
+
define_method(method) do |*args|
|
13
|
+
message = MessageBuilder.send(method, *args)
|
14
|
+
self.message message
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
module Devices
|
4
|
+
|
5
|
+
# A device that receives messages from a system MIDI input port,
|
6
|
+
# and passes the messages to any connected device.
|
7
|
+
#
|
8
|
+
# Available inputs are contained in the {INPUTS} list in the {Midi} module.
|
9
|
+
#
|
10
|
+
class InputDevice < JDevice
|
11
|
+
|
12
|
+
# Wrap a javax.sound.midi.MidiDevice receiver to provide MIDI output.
|
13
|
+
#
|
14
|
+
# @note Typically you won't instantiate these directly. Instead, find an input via the {INPUTS} list in the {Midi} module.
|
15
|
+
#
|
16
|
+
def initialize(java_device)
|
17
|
+
super(java_device, :input)
|
18
|
+
@bridge = Bridge.new(self)
|
19
|
+
java_device.transmitter.receiver = @bridge
|
20
|
+
end
|
21
|
+
|
22
|
+
def output=(device)
|
23
|
+
super
|
24
|
+
@bridge.output= device
|
25
|
+
end
|
26
|
+
|
27
|
+
# A subcomponent of {InputDevice} that implements javax.sound.midi.Receiver
|
28
|
+
# by translating incoming Java MIDI Messages to Ruby Messages.
|
29
|
+
class Bridge < Device
|
30
|
+
include javax.sound.midi.Receiver
|
31
|
+
|
32
|
+
def initialize(source_device)
|
33
|
+
@source_device = source_device
|
34
|
+
end
|
35
|
+
|
36
|
+
# Receives incoming Java MIDI Messages, converts them to Ruby Messages,
|
37
|
+
# and sends them to any connected devices.
|
38
|
+
def send(java_message, timestamp=-1)
|
39
|
+
self.message Message.from_java(java_message, :source => @source_device)
|
40
|
+
rescue
|
41
|
+
STDERR.puts $! if $DEBUG # otherwise this can get swallowed
|
42
|
+
raise
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
module Devices
|
4
|
+
|
5
|
+
# A Java-provided MIDI device (wraps javax.sound.midi.MidiDevice objects)
|
6
|
+
class JDevice < Device
|
7
|
+
|
8
|
+
# the javax.sound.midi.MidiDevice.Info object for this java device
|
9
|
+
attr_reader :info
|
10
|
+
|
11
|
+
# the description of this device
|
12
|
+
# @return [String]
|
13
|
+
attr_reader :description
|
14
|
+
|
15
|
+
attr_reader :type
|
16
|
+
|
17
|
+
# All open JDevices
|
18
|
+
# @note All open devices will be automatically closed at_exit
|
19
|
+
def self.open_devices
|
20
|
+
@@open_devices ||= []
|
21
|
+
end
|
22
|
+
at_exit do
|
23
|
+
# Close all open devices so we don't hang the program at shutdown time
|
24
|
+
for device in JDevice.open_devices
|
25
|
+
device.close
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(java_device, type)
|
30
|
+
@java_device = java_device
|
31
|
+
@info = @java_device.deviceInfo
|
32
|
+
@description = @info.description
|
33
|
+
@type = type
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.from_java(java_device)
|
37
|
+
case java_device
|
38
|
+
when javax.sound.midi.Sequencer then type = :sequencer
|
39
|
+
when javax.sound.midi.Synthesizer then type = :synthesizer
|
40
|
+
else
|
41
|
+
# This assumes a single device cannot be both an input and an output:
|
42
|
+
if java_device.maxTransmitters != 0
|
43
|
+
return InputDevice.new(java_device)
|
44
|
+
elsif java_device.maxReceivers != 0
|
45
|
+
return OutputDevice.new(java_device)
|
46
|
+
else
|
47
|
+
type = :unknown
|
48
|
+
end
|
49
|
+
end
|
50
|
+
new java_device, type
|
51
|
+
end
|
52
|
+
|
53
|
+
# @see Device#open
|
54
|
+
# @note All open devices will be automatically closed at_exit
|
55
|
+
def open
|
56
|
+
unless @java_device.open?
|
57
|
+
puts "Opening #{to_s}"
|
58
|
+
@java_device.open
|
59
|
+
self.class.open_devices << self
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @see Device#close
|
64
|
+
def close
|
65
|
+
if @java_device.open?
|
66
|
+
puts "Closing #{to_s}"
|
67
|
+
@java_device.close
|
68
|
+
self.class.open_devices.delete(self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def method_missing(sym, *args, &block)
|
73
|
+
if @java_device.respond_to? sym
|
74
|
+
@java_device.send(sym, *args, &block)
|
75
|
+
else
|
76
|
+
@info.send(sym, *args, &block)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def respond_to?(sym)
|
81
|
+
super or @java_device.respond_to? sym or info.respond_to? sym
|
82
|
+
end
|
83
|
+
|
84
|
+
def [](field)
|
85
|
+
send field
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
"#{super}: #{info.description}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def inspect
|
93
|
+
to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|