midi-communications 0.6.1 → 0.7.0
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.version +6 -0
- data/.yardopts +6 -0
- data/LICENSE +159 -668
- data/README.md +8 -18
- data/examples/select_a_device.rb +5 -6
- data/lib/midi-communications/adapter/jruby.rb +11 -3
- data/lib/midi-communications/adapter/linux.rb +11 -3
- data/lib/midi-communications/adapter/macos.rb +13 -3
- data/lib/midi-communications/adapter/windows.rb +11 -4
- data/lib/midi-communications/device.rb +147 -34
- data/lib/midi-communications/input/stream_reader.rb +49 -39
- data/lib/midi-communications/input.rb +32 -3
- data/lib/midi-communications/loader.rb +17 -8
- data/lib/midi-communications/output.rb +75 -20
- data/lib/midi-communications/platform.rb +12 -3
- data/lib/midi-communications/type_conversion.rb +11 -5
- data/lib/midi-communications/version.rb +2 -1
- data/lib/midi-communications.rb +38 -0
- data/midi-communications.gemspec +11 -9
- metadata +52 -5
data/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# MIDI Communications
|
|
2
2
|
|
|
3
|
+
[](https://www.ruby-lang.org/)
|
|
4
|
+
[](https://www.gnu.org/licenses/lgpl-3.0.html)
|
|
5
|
+
|
|
3
6
|
**Platform independent realtime MIDI input and output for Ruby.**
|
|
4
7
|
|
|
5
8
|
This library is part of a suite of Ruby libraries for MIDI:
|
|
@@ -50,7 +53,7 @@ Otherwise...
|
|
|
50
53
|
|
|
51
54
|
Some examples are included with the library:
|
|
52
55
|
|
|
53
|
-
* [Selecting a device](http://github.com/
|
|
56
|
+
* [Selecting a device](http://github.com/javier-sy/midi-communications/blob/master/examples/select_a_device.rb)
|
|
54
57
|
* [MIDI input](http://github.com/javier-sy/midi-communications/blob/master/examples/input.rb)
|
|
55
58
|
* [MIDI output](http://github.com/javier-sy/midi-communications/blob/master/examples/output.rb)
|
|
56
59
|
* [MIDI Sysex output](http://github.com/javier-sy/midi-communications/blob/master/examples/sysex_output.rb)
|
|
@@ -67,18 +70,17 @@ See below for additional notes on testing with JRuby.
|
|
|
67
70
|
|
|
68
71
|
### Documentation
|
|
69
72
|
|
|
70
|
-
[rdoc](http://rdoc.info/gems/midi-communications)
|
|
73
|
+
[rdoc](http://rdoc.info/gems/midi-communications)
|
|
71
74
|
|
|
72
75
|
### Platform Specific Notes
|
|
73
76
|
|
|
74
77
|
##### JRuby
|
|
75
78
|
|
|
76
|
-
*
|
|
77
|
-
* (**TO CONFIRM**) javax.sound has some documented issues with SysEx messages in some versions OSX Snow Leopard which do affect this library.
|
|
79
|
+
* Not tested. Could have some problems.
|
|
78
80
|
|
|
79
81
|
##### Linux
|
|
80
82
|
|
|
81
|
-
*
|
|
83
|
+
* Not tested. Could have some problems.
|
|
82
84
|
|
|
83
85
|
## Differences between [MIDI Communications](https://github.com/javier-sy/midi-communications) library and [UniMIDI](https://github.com/arirusso/unimidi) library
|
|
84
86
|
|
|
@@ -89,8 +91,6 @@ See below for additional notes on testing with JRuby.
|
|
|
89
91
|
* Updated dependencies versions
|
|
90
92
|
* Renamed module to MIDICommunications instead of UniMIDI
|
|
91
93
|
* Renamed gem to midi-communications instead of unimidi
|
|
92
|
-
* TODO: update tests to use rspec instead of rake
|
|
93
|
-
* TODO: migrate to (or confirm it's working ok on) Ruby 3.0 and Ruby 3.1
|
|
94
94
|
|
|
95
95
|
## Then, why does exist this library if it is mostly a clone of another library?
|
|
96
96
|
|
|
@@ -124,16 +124,6 @@ I've decided to publish my own renamed versions of the modified dependencies bec
|
|
|
124
124
|
|
|
125
125
|
All in all I have decided to publish a suite of libraries optimized for MusaDSL use case that also can be used by other people in their projects.
|
|
126
126
|
|
|
127
|
-
| Function | Library | Based on Ari Russo's| Difference |
|
|
128
|
-
| --- | --- | --- | --- |
|
|
129
|
-
| MIDI Events representation | [MIDI Events](https://github.com/javier-sy/midi-events) | [MIDI Message](https://github.com/arirusso/midi-message) | removed parsing, small improvements |
|
|
130
|
-
| MIDI Data parsing | [MIDI Parser](https://github.com/javier-sy/midi-parser) | [Nibbler](https://github.com/arirusso/nibbler) | removed process history information, minor optimizations |
|
|
131
|
-
| MIDI communication with Instruments and Control Surfaces | [MIDI Communications](https://github.com/javier-sy/midi-communications) | [unimidi](https://github.com/arirusso/unimidi) | use of [MIDI Communications MacOS Layer](https://github.com/javier-sy/midi-communications-macos, removed process history information, removed buffering, removed command line script)
|
|
132
|
-
| Low level MIDI interface to MacOS | [MIDI Communications MacOS Layer](https://github.com/javier-sy/midi-communications-macos) | [ffi-coremidi](https://github.com/arirusso/ffi-coremidi) | removed buffering and process history information, locking behaviour when waiting midi events, improved midi devices name detection, minor optimizations |
|
|
133
|
-
| Low level MIDI interface to Linux | **TO DO** | | |
|
|
134
|
-
| Low level MIDI interface to JRuby | **TO DO** | | |
|
|
135
|
-
| Low level MIDI interface to Windows | **TO DO** | | |
|
|
136
|
-
|
|
137
127
|
## Author
|
|
138
128
|
|
|
139
129
|
* [Javier Sánchez Yeste](https://github.com/javier-sy)
|
|
@@ -144,6 +134,6 @@ Thanks to [Ari Russo](http://github.com/arirusso) for his ruby library [unimidi]
|
|
|
144
134
|
|
|
145
135
|
### License
|
|
146
136
|
|
|
147
|
-
[MIDI Communications](https://github.com/javier-sy/midi-communications) Copyright (c) 2021-
|
|
137
|
+
[MIDI Communications](https://github.com/javier-sy/midi-communications) Copyright (c) 2021-2025 [Javier Sánchez Yeste](https://yeste.studio), licensed under LGPL 3.0 License
|
|
148
138
|
|
|
149
139
|
[unimidi](https://github.com/arirusso/unimidi) Copyright (c) 2010-2017 [Ari Russo](http://arirusso.com), licensed under Apache License 2.0 (see the file LICENSE.unimidi)
|
data/examples/select_a_device.rb
CHANGED
|
@@ -35,17 +35,16 @@ output = MIDICommunications::Output.use(0)
|
|
|
35
35
|
output = MIDICommunications::Output.open(:first)
|
|
36
36
|
output = MIDICommunications::Output.open(0)
|
|
37
37
|
|
|
38
|
-
#
|
|
39
|
-
|
|
38
|
+
# Note: first and last open the device automatically
|
|
40
39
|
output = MIDICommunications::Output.first
|
|
40
|
+
|
|
41
|
+
# If you want to get a device without opening it, use at/[] or all
|
|
41
42
|
output = MIDICommunications::Output[0]
|
|
43
|
+
output = MIDICommunications::Output.at(0)
|
|
42
44
|
output = MIDICommunications::Output.all[0]
|
|
43
45
|
output = MIDICommunications::Output.all.first
|
|
44
|
-
output = MIDICommunications::Device.all_by_type(:output)[0]
|
|
45
|
-
output = MIDICommunications::Device.all_by_type(:output).first
|
|
46
46
|
|
|
47
47
|
# You'll need to call open on these before you use it or an exception will be raised
|
|
48
|
-
|
|
49
48
|
output.open
|
|
50
49
|
|
|
51
50
|
# It's also possible to select a device by name
|
|
@@ -54,4 +53,4 @@ output = MIDICommunications::Output.find_by_name('Roland UM-2 (1)').open
|
|
|
54
53
|
|
|
55
54
|
# or using regex match
|
|
56
55
|
|
|
57
|
-
output = MIDICommunications::Output.find { |device| device.name.match(/Launchpad/) }.open
|
|
56
|
+
output = MIDICommunications::Output.find { |device| device.name.match(/Launchpad/) }.open
|
|
@@ -2,17 +2,25 @@ require 'midi-jruby'
|
|
|
2
2
|
|
|
3
3
|
module MIDICommunications
|
|
4
4
|
module Adapter
|
|
5
|
-
#
|
|
5
|
+
# JRuby adapter using the midi-jruby gem.
|
|
6
|
+
#
|
|
7
|
+
# Uses Java MIDI API to communicate with MIDI devices on JRuby.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
6
10
|
module JRuby
|
|
11
|
+
# Loader for JRuby MIDI devices.
|
|
12
|
+
# @api private
|
|
7
13
|
module Loader
|
|
8
14
|
extend self
|
|
9
15
|
|
|
10
|
-
#
|
|
16
|
+
# Returns all available MIDI input devices.
|
|
17
|
+
# @return [Array<MIDIJRuby::Input>]
|
|
11
18
|
def inputs
|
|
12
19
|
::MIDIJRuby::Device.all_by_type[:input]
|
|
13
20
|
end
|
|
14
21
|
|
|
15
|
-
#
|
|
22
|
+
# Returns all available MIDI output devices.
|
|
23
|
+
# @return [Array<MIDIJRuby::Output>]
|
|
16
24
|
def outputs
|
|
17
25
|
::MIDIJRuby::Device.all_by_type[:output]
|
|
18
26
|
end
|
|
@@ -2,17 +2,25 @@ require 'alsa-rawmidi'
|
|
|
2
2
|
|
|
3
3
|
module MIDICommunications
|
|
4
4
|
module Adapter
|
|
5
|
-
#
|
|
5
|
+
# Linux adapter using the alsa-rawmidi gem.
|
|
6
|
+
#
|
|
7
|
+
# Uses ALSA to communicate with MIDI devices on Linux.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
6
10
|
module Linux
|
|
11
|
+
# Loader for Linux MIDI devices.
|
|
12
|
+
# @api private
|
|
7
13
|
module Loader
|
|
8
14
|
extend self
|
|
9
15
|
|
|
10
|
-
#
|
|
16
|
+
# Returns all available MIDI input devices.
|
|
17
|
+
# @return [Array<AlsaRawMIDI::Input>]
|
|
11
18
|
def inputs
|
|
12
19
|
::AlsaRawMIDI::Device.all_by_type[:input]
|
|
13
20
|
end
|
|
14
21
|
|
|
15
|
-
#
|
|
22
|
+
# Returns all available MIDI output devices.
|
|
23
|
+
# @return [Array<AlsaRawMIDI::Output>]
|
|
16
24
|
def outputs
|
|
17
25
|
::AlsaRawMIDI::Device.all_by_type[:output]
|
|
18
26
|
end
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
require 'midi-communications-macos'
|
|
2
2
|
|
|
3
3
|
module MIDICommunications
|
|
4
|
+
# Platform-specific adapters for MIDI communication.
|
|
5
|
+
# @api private
|
|
4
6
|
module Adapter
|
|
5
|
-
#
|
|
7
|
+
# macOS adapter using the midi-communications-macos gem.
|
|
8
|
+
#
|
|
9
|
+
# Uses Core MIDI to communicate with MIDI devices on macOS.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
6
12
|
module MacOS
|
|
13
|
+
# Loader for macOS MIDI devices.
|
|
14
|
+
# @api private
|
|
7
15
|
module Loader
|
|
8
16
|
extend self
|
|
9
17
|
|
|
10
|
-
#
|
|
18
|
+
# Returns all available MIDI input sources.
|
|
19
|
+
# @return [Array<MIDICommunicationsMacOS::Source>]
|
|
11
20
|
def inputs
|
|
12
21
|
::MIDICommunicationsMacOS::Endpoint.all_by_type[:source]
|
|
13
22
|
end
|
|
14
23
|
|
|
15
|
-
#
|
|
24
|
+
# Returns all available MIDI output destinations.
|
|
25
|
+
# @return [Array<MIDICommunicationsMacOS::Destination>]
|
|
16
26
|
def outputs
|
|
17
27
|
::MIDICommunicationsMacOS::Endpoint.all_by_type[:destination]
|
|
18
28
|
end
|
|
@@ -2,18 +2,25 @@ require 'midi-winmm'
|
|
|
2
2
|
|
|
3
3
|
module MIDICommunications
|
|
4
4
|
module Adapter
|
|
5
|
-
#
|
|
5
|
+
# Windows adapter using the midi-winmm gem.
|
|
6
|
+
#
|
|
7
|
+
# Uses Windows Multimedia API to communicate with MIDI devices.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
6
10
|
module Windows
|
|
11
|
+
# Loader for Windows MIDI devices.
|
|
12
|
+
# @api private
|
|
7
13
|
module Loader
|
|
8
|
-
|
|
9
14
|
extend self
|
|
10
15
|
|
|
11
|
-
#
|
|
16
|
+
# Returns all available MIDI input devices.
|
|
17
|
+
# @return [Array<MIDIWinMM::Input>]
|
|
12
18
|
def inputs
|
|
13
19
|
::MIDIWinMM::Device.all_by_type[:input]
|
|
14
20
|
end
|
|
15
21
|
|
|
16
|
-
#
|
|
22
|
+
# Returns all available MIDI output devices.
|
|
23
|
+
# @return [Array<MIDIWinMM::Output>]
|
|
17
24
|
def outputs
|
|
18
25
|
::MIDIWinMM::Device.all_by_type[:output]
|
|
19
26
|
end
|
|
@@ -1,17 +1,40 @@
|
|
|
1
1
|
module MIDICommunications
|
|
2
|
-
# Common logic
|
|
2
|
+
# Common logic shared by both {Input} and {Output} devices.
|
|
3
|
+
#
|
|
4
|
+
# This module provides the core device management functionality including
|
|
5
|
+
# device discovery, selection, and lifecycle management.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
3
8
|
module Device
|
|
4
|
-
#
|
|
9
|
+
# Class methods shared by both {Input} and {Output} classes.
|
|
10
|
+
#
|
|
11
|
+
# Provides device discovery and selection methods including enumeration,
|
|
12
|
+
# listing, searching by name, and interactive selection.
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
5
15
|
module ClassMethods
|
|
6
16
|
include Enumerable
|
|
7
17
|
|
|
8
|
-
#
|
|
18
|
+
# Iterates over all devices of this type.
|
|
19
|
+
#
|
|
20
|
+
# @yield [device] each device
|
|
21
|
+
# @yieldparam device [Input, Output] a MIDI device
|
|
22
|
+
# @return [Enumerator] if no block given
|
|
23
|
+
#
|
|
24
|
+
# @example
|
|
25
|
+
# MIDICommunications::Output.each { |o| puts o.name }
|
|
9
26
|
def each(&block)
|
|
10
27
|
all.each(&block)
|
|
11
28
|
end
|
|
12
29
|
|
|
13
|
-
# Prints
|
|
14
|
-
#
|
|
30
|
+
# Prints the ID and name of each device to the console.
|
|
31
|
+
#
|
|
32
|
+
# @return [Array<String>] array of formatted device names
|
|
33
|
+
#
|
|
34
|
+
# @example
|
|
35
|
+
# MIDICommunications::Output.list
|
|
36
|
+
# # 0) IAC Driver Bus 1
|
|
37
|
+
# # 1) USB MIDI Device
|
|
15
38
|
def list
|
|
16
39
|
all.map do |device|
|
|
17
40
|
name = "#{device.id}) #{device.display_name}"
|
|
@@ -20,15 +43,31 @@ module MIDICommunications
|
|
|
20
43
|
end
|
|
21
44
|
end
|
|
22
45
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# @
|
|
46
|
+
# Finds a device by its name.
|
|
47
|
+
#
|
|
48
|
+
# @param name [String, Symbol] the device name to search for
|
|
49
|
+
# @return [Input, Output, nil] the matching device or nil if not found
|
|
50
|
+
#
|
|
51
|
+
# @example
|
|
52
|
+
# output = MIDICommunications::Output.find_by_name("IAC Driver Bus 1")
|
|
26
53
|
def find_by_name(name)
|
|
27
54
|
all.find { |device| name.to_s == device.name }
|
|
28
55
|
end
|
|
29
56
|
|
|
30
|
-
#
|
|
31
|
-
#
|
|
57
|
+
# Interactive console prompt for device selection.
|
|
58
|
+
#
|
|
59
|
+
# Displays available devices and waits for user input. When a valid
|
|
60
|
+
# selection is received, the device is opened and returned.
|
|
61
|
+
#
|
|
62
|
+
# @yield [device] optional block to execute with the opened device
|
|
63
|
+
# @yieldparam device [Input, Output] the selected device
|
|
64
|
+
# @return [Input, Output] the selected and opened device
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# output = MIDICommunications::Output.gets
|
|
68
|
+
# # Select a MIDI output...
|
|
69
|
+
# # 0) IAC Driver Bus 1
|
|
70
|
+
# # > 0
|
|
32
71
|
def gets(&block)
|
|
33
72
|
device = nil
|
|
34
73
|
direction = get_direction
|
|
@@ -47,21 +86,39 @@ module MIDICommunications
|
|
|
47
86
|
device
|
|
48
87
|
end
|
|
49
88
|
|
|
50
|
-
#
|
|
51
|
-
#
|
|
89
|
+
# Selects and opens the first available device.
|
|
90
|
+
#
|
|
91
|
+
# @yield [device] optional block to execute with the device
|
|
92
|
+
# @yieldparam device [Input, Output] the device
|
|
93
|
+
# @return [Input, Output] the first device, opened
|
|
94
|
+
#
|
|
95
|
+
# @example
|
|
96
|
+
# output = MIDICommunications::Output.first
|
|
52
97
|
def first(&block)
|
|
53
98
|
use_device(all.first, &block)
|
|
54
99
|
end
|
|
55
100
|
|
|
56
|
-
#
|
|
57
|
-
#
|
|
101
|
+
# Selects and opens the last available device.
|
|
102
|
+
#
|
|
103
|
+
# @yield [device] optional block to execute with the device
|
|
104
|
+
# @yieldparam device [Input, Output] the device
|
|
105
|
+
# @return [Input, Output] the last device, opened
|
|
106
|
+
#
|
|
107
|
+
# @example
|
|
108
|
+
# output = MIDICommunications::Output.last
|
|
58
109
|
def last(&block)
|
|
59
110
|
use_device(all.last, &block)
|
|
60
111
|
end
|
|
61
112
|
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# @
|
|
113
|
+
# Selects and opens the device at the given index.
|
|
114
|
+
#
|
|
115
|
+
# @param index [Integer, Symbol] device index or :first/:last
|
|
116
|
+
# @yield [device] optional block to execute with the device
|
|
117
|
+
# @yieldparam device [Input, Output] the device
|
|
118
|
+
# @return [Input, Output] the selected device, opened
|
|
119
|
+
#
|
|
120
|
+
# @example
|
|
121
|
+
# output = MIDICommunications::Output.use(0)
|
|
65
122
|
def use(index, &block)
|
|
66
123
|
index = case index
|
|
67
124
|
when :first then 0
|
|
@@ -72,9 +129,14 @@ module MIDICommunications
|
|
|
72
129
|
end
|
|
73
130
|
alias open use
|
|
74
131
|
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
# @
|
|
132
|
+
# Returns the device at the given index without opening it.
|
|
133
|
+
#
|
|
134
|
+
# @param index [Integer] device index
|
|
135
|
+
# @return [Input, Output] the device at the given index
|
|
136
|
+
#
|
|
137
|
+
# @example
|
|
138
|
+
# device = MIDICommunications::Output.at(0)
|
|
139
|
+
# device.open if device
|
|
78
140
|
def at(index)
|
|
79
141
|
all[index]
|
|
80
142
|
end
|
|
@@ -101,9 +163,17 @@ module MIDICommunications
|
|
|
101
163
|
end
|
|
102
164
|
end
|
|
103
165
|
|
|
104
|
-
#
|
|
166
|
+
# Instance methods shared by both {Input} and {Output} instances.
|
|
167
|
+
#
|
|
168
|
+
# Provides device lifecycle management (open, close) and access
|
|
169
|
+
# to device attributes (name, id, manufacturer, etc.).
|
|
170
|
+
#
|
|
171
|
+
# @api public
|
|
105
172
|
module InstanceMethods
|
|
106
|
-
#
|
|
173
|
+
# Creates a new device wrapper.
|
|
174
|
+
#
|
|
175
|
+
# @param device [Object] platform-specific device object
|
|
176
|
+
# @api private
|
|
107
177
|
def initialize(device)
|
|
108
178
|
@device = device
|
|
109
179
|
@enabled = false
|
|
@@ -111,11 +181,24 @@ module MIDICommunications
|
|
|
111
181
|
populate_from_device
|
|
112
182
|
end
|
|
113
183
|
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
184
|
+
# Opens the device for use.
|
|
185
|
+
#
|
|
186
|
+
# When a block is given, the device is automatically closed when
|
|
187
|
+
# the block exits. Otherwise, the device is closed at program exit.
|
|
188
|
+
#
|
|
189
|
+
# @param args [Object] arguments passed to the underlying device
|
|
190
|
+
# @yield [device] optional block to execute with the open device
|
|
191
|
+
# @yieldparam device [Input, Output] self
|
|
118
192
|
# @return [Input, Output] self
|
|
193
|
+
#
|
|
194
|
+
# @example Open and close automatically with block
|
|
195
|
+
# output.open do |o|
|
|
196
|
+
# o.puts(0x90, 60, 100)
|
|
197
|
+
# end # device closed here
|
|
198
|
+
#
|
|
199
|
+
# @example Open manually (closed at program exit)
|
|
200
|
+
# output.open
|
|
201
|
+
# output.puts(0x90, 60, 100)
|
|
119
202
|
def open(*args)
|
|
120
203
|
unless @enabled
|
|
121
204
|
@device.open(*args)
|
|
@@ -135,10 +218,13 @@ module MIDICommunications
|
|
|
135
218
|
self
|
|
136
219
|
end
|
|
137
220
|
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
# @param [
|
|
141
|
-
# @return [Boolean]
|
|
221
|
+
# Closes the device.
|
|
222
|
+
#
|
|
223
|
+
# @param args [Object] arguments passed to the underlying device
|
|
224
|
+
# @return [Boolean] true if the device was closed, false if already closed
|
|
225
|
+
#
|
|
226
|
+
# @example
|
|
227
|
+
# output.close
|
|
142
228
|
def close(*args)
|
|
143
229
|
if @enabled
|
|
144
230
|
@device.close(*args)
|
|
@@ -149,14 +235,41 @@ module MIDICommunications
|
|
|
149
235
|
end
|
|
150
236
|
end
|
|
151
237
|
|
|
152
|
-
# Returns true if the device is not enabled
|
|
153
|
-
#
|
|
238
|
+
# Returns true if the device is closed (not enabled).
|
|
239
|
+
#
|
|
240
|
+
# @return [Boolean] true if device is closed
|
|
154
241
|
def closed?
|
|
155
242
|
!@enabled
|
|
156
243
|
end
|
|
157
244
|
|
|
158
|
-
#
|
|
159
|
-
#
|
|
245
|
+
# @!attribute [r] direction
|
|
246
|
+
# @return [Symbol] the device direction (:input or :output)
|
|
247
|
+
|
|
248
|
+
# @!attribute [r] enabled
|
|
249
|
+
# @return [Boolean] whether the device is currently open
|
|
250
|
+
|
|
251
|
+
# @!attribute [r] id
|
|
252
|
+
# @return [Integer] the device ID
|
|
253
|
+
|
|
254
|
+
# @!attribute [r] manufacturer
|
|
255
|
+
# @return [String] the device manufacturer name
|
|
256
|
+
|
|
257
|
+
# @!attribute [r] model
|
|
258
|
+
# @return [String] the device model name
|
|
259
|
+
|
|
260
|
+
# @!attribute [r] name
|
|
261
|
+
# @return [String] the device name
|
|
262
|
+
|
|
263
|
+
# @!attribute [r] display_name
|
|
264
|
+
# @return [String] the device display name
|
|
265
|
+
|
|
266
|
+
# @!method enabled?
|
|
267
|
+
# @return [Boolean] alias for {#enabled}
|
|
268
|
+
|
|
269
|
+
# @!method type
|
|
270
|
+
# @return [Symbol] alias for {#direction}
|
|
271
|
+
|
|
272
|
+
# @api private
|
|
160
273
|
def self.included(base)
|
|
161
274
|
base.send(:attr_reader, :direction)
|
|
162
275
|
base.send(:attr_reader, :enabled)
|
|
@@ -1,42 +1,48 @@
|
|
|
1
1
|
module MIDICommunications
|
|
2
2
|
class Input
|
|
3
|
+
# Methods for reading MIDI messages from an input device.
|
|
4
|
+
#
|
|
5
|
+
# Provides multiple methods for retrieving MIDI data in different formats:
|
|
6
|
+
# numeric bytes, hex strings, or raw data arrays.
|
|
7
|
+
#
|
|
8
|
+
# @api public
|
|
3
9
|
module StreamReader
|
|
4
|
-
#
|
|
5
|
-
# StreamReader method. If a StreamReader method has not yet been called, all data received
|
|
6
|
-
# since the program was initialized will be returned
|
|
10
|
+
# Reads MIDI messages from the input buffer.
|
|
7
11
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# { data: [144, 60, 100], timestamp: 1024 },
|
|
11
|
-
# { data: [128, 60, 100], timestamp: 1100 },
|
|
12
|
-
# { data: [144, 40, 120], timestamp: 1200 }
|
|
13
|
-
# ]
|
|
12
|
+
# Returns all messages received since the last read. Each message
|
|
13
|
+
# includes the MIDI data and a timestamp.
|
|
14
14
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# Arguments are passed to the underlying device object
|
|
15
|
+
# @param args [Object] arguments passed to the underlying device
|
|
16
|
+
# @return [Array<Hash>] array of message hashes with :data and :timestamp keys
|
|
18
17
|
#
|
|
19
|
-
# @
|
|
20
|
-
#
|
|
18
|
+
# @example
|
|
19
|
+
# messages = input.gets
|
|
20
|
+
# # => [{ data: [144, 60, 100], timestamp: 1024 },
|
|
21
|
+
# # { data: [128, 60, 100], timestamp: 1100 }]
|
|
22
|
+
#
|
|
23
|
+
# @example Process messages in a loop
|
|
24
|
+
# loop do
|
|
25
|
+
# messages = input.gets
|
|
26
|
+
# messages.each { |m| puts m[:data].inspect }
|
|
27
|
+
# sleep(0.01)
|
|
28
|
+
# end
|
|
21
29
|
def gets(*args)
|
|
22
30
|
@device.gets(*args)
|
|
23
31
|
rescue SystemExit, Interrupt
|
|
24
32
|
exit
|
|
25
33
|
end
|
|
26
34
|
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
35
|
+
# Reads MIDI messages as hex strings.
|
|
36
|
+
#
|
|
37
|
+
# Similar to {#gets} but returns data as hex strings instead of byte arrays.
|
|
30
38
|
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# { data: "904060", timestamp: 904 },
|
|
34
|
-
# { data: "804060", timestamp: 1150 },
|
|
35
|
-
# { data: "90447F", timestamp: 1300 }
|
|
36
|
-
# ]
|
|
39
|
+
# @param args [Object] arguments passed to the underlying device
|
|
40
|
+
# @return [Array<Hash>] array of message hashes with :data (String) and :timestamp keys
|
|
37
41
|
#
|
|
38
|
-
# @
|
|
39
|
-
#
|
|
42
|
+
# @example
|
|
43
|
+
# messages = input.gets_s
|
|
44
|
+
# # => [{ data: "904060", timestamp: 904 },
|
|
45
|
+
# # { data: "804060", timestamp: 1150 }]
|
|
40
46
|
def gets_s(*args)
|
|
41
47
|
@device.gets_s(*args)
|
|
42
48
|
rescue SystemExit, Interrupt
|
|
@@ -45,29 +51,33 @@ module MIDICommunications
|
|
|
45
51
|
alias gets_bytestr gets_s
|
|
46
52
|
alias gets_hex gets_s
|
|
47
53
|
|
|
48
|
-
#
|
|
49
|
-
# StreamReader method. If a StreamReader method has not yet been called, all data received
|
|
50
|
-
# since the program was initialized will be returned
|
|
54
|
+
# Reads MIDI data as a flat array of bytes.
|
|
51
55
|
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
56
|
+
# Returns all message data concatenated into a single array,
|
|
57
|
+
# without timestamps.
|
|
54
58
|
#
|
|
55
|
-
# @param [
|
|
56
|
-
# @return [Array<Integer>]
|
|
59
|
+
# @param args [Object] arguments passed to the underlying device
|
|
60
|
+
# @return [Array<Integer>] flat array of all MIDI bytes
|
|
61
|
+
#
|
|
62
|
+
# @example
|
|
63
|
+
# data = input.gets_data
|
|
64
|
+
# # => [144, 60, 100, 128, 60, 100, 144, 40, 120]
|
|
57
65
|
def gets_data(*args)
|
|
58
66
|
arr = gets(*args)
|
|
59
67
|
arr.map { |msg| msg[:data] }.inject(:+)
|
|
60
68
|
end
|
|
61
69
|
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
70
|
+
# Reads MIDI data as a concatenated hex string.
|
|
71
|
+
#
|
|
72
|
+
# Returns all message data concatenated into a single hex string,
|
|
73
|
+
# without timestamps.
|
|
65
74
|
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
75
|
+
# @param args [Object] arguments passed to the underlying device
|
|
76
|
+
# @return [String] concatenated hex string of all MIDI data
|
|
68
77
|
#
|
|
69
|
-
# @
|
|
70
|
-
#
|
|
78
|
+
# @example
|
|
79
|
+
# data = input.gets_data_s
|
|
80
|
+
# # => "90406080406090447F"
|
|
71
81
|
def gets_data_s(*args)
|
|
72
82
|
arr = gets_bytestr(*args)
|
|
73
83
|
arr.map { |msg| msg[:data] }.join
|
|
@@ -1,14 +1,43 @@
|
|
|
1
1
|
require 'midi-communications/input/stream_reader'
|
|
2
2
|
|
|
3
3
|
module MIDICommunications
|
|
4
|
-
# A MIDI input device
|
|
4
|
+
# A MIDI input device for receiving MIDI messages.
|
|
5
|
+
#
|
|
6
|
+
# Input devices receive MIDI data from external controllers, instruments,
|
|
7
|
+
# or other MIDI sources. Use the class methods to discover and select
|
|
8
|
+
# available input devices.
|
|
9
|
+
#
|
|
10
|
+
# @example List available inputs
|
|
11
|
+
# MIDICommunications::Input.list
|
|
12
|
+
#
|
|
13
|
+
# @example Open the first input and read messages
|
|
14
|
+
# input = MIDICommunications::Input.first
|
|
15
|
+
# messages = input.gets
|
|
16
|
+
# # => [{ data: [144, 60, 100], timestamp: 1024 }]
|
|
17
|
+
#
|
|
18
|
+
# @example Interactive selection
|
|
19
|
+
# input = MIDICommunications::Input.gets
|
|
20
|
+
#
|
|
21
|
+
# @example Find by name
|
|
22
|
+
# input = MIDICommunications::Input.find_by_name("USB MIDI Device")
|
|
23
|
+
# input.open
|
|
24
|
+
#
|
|
25
|
+
# @see Output For sending MIDI messages
|
|
26
|
+
# @see StreamReader For reading methods (gets, gets_s, etc.)
|
|
27
|
+
#
|
|
28
|
+
# @api public
|
|
5
29
|
class Input
|
|
6
30
|
extend Device::ClassMethods
|
|
7
31
|
include Device::InstanceMethods
|
|
8
32
|
include StreamReader
|
|
9
33
|
|
|
10
|
-
#
|
|
11
|
-
#
|
|
34
|
+
# Returns all available MIDI input devices.
|
|
35
|
+
#
|
|
36
|
+
# @return [Array<Input>] array of input devices
|
|
37
|
+
#
|
|
38
|
+
# @example
|
|
39
|
+
# inputs = MIDICommunications::Input.all
|
|
40
|
+
# inputs.each { |i| puts i.name }
|
|
12
41
|
def self.all
|
|
13
42
|
Loader.devices(direction: :input)
|
|
14
43
|
end
|