midi-communications 0.6.1 → 0.7.1

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/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # MIDI Communications
2
2
 
3
+ [![Ruby Version](https://img.shields.io/badge/ruby-2.7+-red.svg)](https://www.ruby-lang.org/)
4
+ [![License](https://img.shields.io/badge/license-LGPL--3.0--or--later-blue.svg)](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:
@@ -12,7 +15,7 @@ This library is part of a suite of Ruby libraries for MIDI:
12
15
  | Low level MIDI interface to MacOS | [MIDI Communications MacOS Layer](https://github.com/javier-sy/midi-communications-macos) |
13
16
  | Low level MIDI interface to Linux | **TO DO** (by now [MIDI Communications](https://github.com/javier-sy/midi-communications) uses [alsa-rawmidi](http://github.com/arirusso/alsa-rawmidi)) |
14
17
  | Low level MIDI interface to JRuby | **TO DO** (by now [MIDI Communications](https://github.com/javier-sy/midi-communications) uses [midi-jruby](http://github.com/arirusso/midi-jruby))|
15
- | Low level MIDI interface to Windows | **TO DO** (by now [MIDI Communications](https://github.com/javier-sy/midi-communications) uses [midi-winm](http://github.com/arirusso/midi-winmm)) |
18
+ | Low level MIDI interface to Windows | [MIDI Communications Windows Layer](https://github.com/javier-sy/midi-communications-windows) |
16
19
 
17
20
  This library is based on [Ari Russo's](http://github.com/arirusso) library [UniMIDI](https://github.com/arirusso/unimidi).
18
21
 
@@ -34,7 +37,7 @@ Platform
34
37
  * OSX: [midi-communications-macos](http://github.com/javier-sy/midi-communications-macos)
35
38
  * JRuby: [midi-jruby](http://github.com/arirusso/midi-jruby) (**TODO: update to midi-communications-jruby**)
36
39
  * Linux: [alsa-rawmidi](http://github.com/arirusso/alsa-rawmidi) (**TODO: update to midi-communications-linux**)
37
- * Windows/Cygwin: [midi-winmm](http://github.com/arirusso/midi-winmm) (**TODO: update to midi-communications-windows**)
40
+ * Windows: [midi-communications-windows](https://github.com/javier-sy/midi-communications-windows)
38
41
 
39
42
  ### Install
40
43
 
@@ -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/arirusso/javier-sy/midi-communications/blob/master/examples/select_a_device.rb)
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) (**TODO**)
73
+ [rdoc](http://rdoc.info/gems/midi-communications)
71
74
 
72
75
  ### Platform Specific Notes
73
76
 
74
77
  ##### JRuby
75
78
 
76
- * (**TO CONFIRM**) You must be in 1.9 mode. This is normally accomplished by passing --1.9 to JRuby at the command line. For testing in 1.9 mode, use `jruby --1.9 -S rake test`
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
- * (**TO CONFIRM**) *libasound* and *libasound-dev* packages are required
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-2023 [Javier Sánchez Yeste](https://yeste.studio), licensed under LGPL 3.0 License
137
+ [MIDI Communications](https://github.com/javier-sy/midi-communications) Copyright (c) 2021-2026 [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)
@@ -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
- # If you want to wait to open the device, you can select it with any of these "finder" methods
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(:first)
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
- # Load underlying devices using the midi-jruby gem
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
- # @return [Array<JRuby::Input>]
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
- # @return [Array<JRuby::Output>]
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
- # Load underlying devices using the alsa-rawmidi gem
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
- # @return [Array<Linux::Input>]
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
- # @return [Array<Linux::Output>]
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,21 +1,42 @@
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
- # Load underlying devices using the coremidi gem
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
- # @return [Array<MacOS::Source>]
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
- # @return [Array<MacOS::Destination>]
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
29
+
30
+ # Discards the Core MIDI device list so that the next enumeration
31
+ # walks the system again.
32
+ #
33
+ # Guarded on `populated?` because Device.refresh clears a list that
34
+ # does not exist until something has enumerated once.
35
+ #
36
+ # @return [void]
37
+ def refresh
38
+ ::MIDICommunicationsMacOS::Device.refresh if ::MIDICommunicationsMacOS::Device.populated?
39
+ end
19
40
  end
20
41
  end
21
42
  end
@@ -1,21 +1,33 @@
1
- require 'midi-winmm'
1
+ require 'midi-communications-windows'
2
2
 
3
3
  module MIDICommunications
4
4
  module Adapter
5
- # Load underlying devices using the midi-winmm gem
5
+ # Windows adapter using the midi-communications-windows gem.
6
+ #
7
+ # Uses the Windows Multimedia (WinMM) API to communicate with MIDI devices.
8
+ #
9
+ # There is no `refresh` here, unlike the macOS adapter: that gem asks WinMM
10
+ # afresh on every enumeration and keeps no list of its own, so there is
11
+ # nothing to invalidate. {Loader} only calls `refresh` on an adapter that
12
+ # answers to it.
13
+ #
14
+ # @api private
6
15
  module Windows
16
+ # Loader for Windows MIDI devices.
17
+ # @api private
7
18
  module Loader
8
-
9
19
  extend self
10
20
 
11
- # @return [Array<Windows::Input>]
21
+ # Returns all available MIDI input devices.
22
+ # @return [Array<MIDICommunicationsWindows::Input>]
12
23
  def inputs
13
- ::MIDIWinMM::Device.all_by_type[:input]
24
+ ::MIDICommunicationsWindows::Device.all_by_type[:input]
14
25
  end
15
26
 
16
- # @return [Array<Windows::Output>]
27
+ # Returns all available MIDI output devices.
28
+ # @return [Array<MIDICommunicationsWindows::Output>]
17
29
  def outputs
18
- ::MIDIWinMM::Device.all_by_type[:output]
30
+ ::MIDICommunicationsWindows::Device.all_by_type[:output]
19
31
  end
20
32
  end
21
33
  end
@@ -1,17 +1,40 @@
1
1
  module MIDICommunications
2
- # Common logic that is shared by both Input and Output devices
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
- # Methods that are shared by both Input and Output classes
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
- # Iterate over all devices of this direction (eg Input, Output)
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 ids and names of each device to the console
14
- # @return [Array<String>]
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,35 @@ module MIDICommunications
20
43
  end
21
44
  end
22
45
 
23
- # Shortcut to select a device by its name
24
- # @param [String, Symbol] name
25
- # @return [Input, Output]
46
+ # Finds a device by its name.
47
+ #
48
+ # Returns the **first** device with that name. A name is a label rather
49
+ # than an identifier and two devices may share one — see the note on names
50
+ # in {PhysicalLayer} — so where it matters, select by {#id} instead.
51
+ #
52
+ # @param name [String, Symbol] the device name to search for
53
+ # @return [Input, Output, nil] the first matching device, or nil
54
+ #
55
+ # @example
56
+ # output = MIDICommunications::Output.find_by_name("IAC Driver Bus 1")
26
57
  def find_by_name(name)
27
58
  all.find { |device| name.to_s == device.name }
28
59
  end
29
60
 
30
- # Streamlined console prompt that asks the user to select a device
31
- # When their input is received, the device is selected and enabled
61
+ # Interactive console prompt for device selection.
62
+ #
63
+ # Displays available devices and waits for user input. When a valid
64
+ # selection is received, the device is opened and returned.
65
+ #
66
+ # @yield [device] optional block to execute with the opened device
67
+ # @yieldparam device [Input, Output] the selected device
68
+ # @return [Input, Output] the selected and opened device
69
+ #
70
+ # @example
71
+ # output = MIDICommunications::Output.gets
72
+ # # Select a MIDI output...
73
+ # # 0) IAC Driver Bus 1
74
+ # # > 0
32
75
  def gets(&block)
33
76
  device = nil
34
77
  direction = get_direction
@@ -47,21 +90,39 @@ module MIDICommunications
47
90
  device
48
91
  end
49
92
 
50
- # Select the first device and enable it
51
- # @return [Input, Output]
93
+ # Selects and opens the first available device.
94
+ #
95
+ # @yield [device] optional block to execute with the device
96
+ # @yieldparam device [Input, Output] the device
97
+ # @return [Input, Output] the first device, opened
98
+ #
99
+ # @example
100
+ # output = MIDICommunications::Output.first
52
101
  def first(&block)
53
102
  use_device(all.first, &block)
54
103
  end
55
104
 
56
- # Select the last device and enable it
57
- # @return [Input, Output]
105
+ # Selects and opens the last available device.
106
+ #
107
+ # @yield [device] optional block to execute with the device
108
+ # @yieldparam device [Input, Output] the device
109
+ # @return [Input, Output] the last device, opened
110
+ #
111
+ # @example
112
+ # output = MIDICommunications::Output.last
58
113
  def last(&block)
59
114
  use_device(all.last, &block)
60
115
  end
61
116
 
62
- # Select the device at the given index and enable it
63
- # @param [Integer] index
64
- # @return [Input, Output]
117
+ # Selects and opens the device at the given index.
118
+ #
119
+ # @param index [Integer, Symbol] device index or :first/:last
120
+ # @yield [device] optional block to execute with the device
121
+ # @yieldparam device [Input, Output] the device
122
+ # @return [Input, Output] the selected device, opened
123
+ #
124
+ # @example
125
+ # output = MIDICommunications::Output.use(0)
65
126
  def use(index, &block)
66
127
  index = case index
67
128
  when :first then 0
@@ -72,9 +133,14 @@ module MIDICommunications
72
133
  end
73
134
  alias open use
74
135
 
75
- # Select the device at the given index
76
- # @param [Integer] index
77
- # @return [Input, Output]
136
+ # Returns the device at the given index without opening it.
137
+ #
138
+ # @param index [Integer] device index
139
+ # @return [Input, Output] the device at the given index
140
+ #
141
+ # @example
142
+ # device = MIDICommunications::Output.at(0)
143
+ # device.open if device
78
144
  def at(index)
79
145
  all[index]
80
146
  end
@@ -101,9 +167,17 @@ module MIDICommunications
101
167
  end
102
168
  end
103
169
 
104
- # Methods that are shared by both Input and Output instances
170
+ # Instance methods shared by both {Input} and {Output} instances.
171
+ #
172
+ # Provides device lifecycle management (open, close) and access
173
+ # to device attributes (name, id, manufacturer, etc.).
174
+ #
175
+ # @api public
105
176
  module InstanceMethods
106
- # @param [AlsaRawMIDI::Input, AlsaRawMIDI::Output, MIDICommunicationsMacOS::Destination, MIDICommunicationsMacOS::Source, MIDIJRuby::Input, MIDIJRuby::Output, MIDIWinMM::Input, MIDIWinMM::Output] device
177
+ # Creates a new device wrapper.
178
+ #
179
+ # @param device [Object] platform-specific device object
180
+ # @api private
107
181
  def initialize(device)
108
182
  @device = device
109
183
  @enabled = false
@@ -111,11 +185,24 @@ module MIDICommunications
111
185
  populate_from_device
112
186
  end
113
187
 
114
- # Enable the device for use
115
- # Params are passed to the underlying device object
116
- # Can be passed a block to which the device will be passed in as the yieldparam
117
- # @param [*Object] args
188
+ # Opens the device for use.
189
+ #
190
+ # When a block is given, the device is automatically closed when
191
+ # the block exits. Otherwise, the device is closed at program exit.
192
+ #
193
+ # @param args [Object] arguments passed to the underlying device
194
+ # @yield [device] optional block to execute with the open device
195
+ # @yieldparam device [Input, Output] self
118
196
  # @return [Input, Output] self
197
+ #
198
+ # @example Open and close automatically with block
199
+ # output.open do |o|
200
+ # o.puts(0x90, 60, 100)
201
+ # end # device closed here
202
+ #
203
+ # @example Open manually (closed at program exit)
204
+ # output.open
205
+ # output.puts(0x90, 60, 100)
119
206
  def open(*args)
120
207
  unless @enabled
121
208
  @device.open(*args)
@@ -135,10 +222,13 @@ module MIDICommunications
135
222
  self
136
223
  end
137
224
 
138
- # Close the device
139
- # Params are passed to the underlying device object
140
- # @param [*Object] args
141
- # @return [Boolean]
225
+ # Closes the device.
226
+ #
227
+ # @param args [Object] arguments passed to the underlying device
228
+ # @return [Boolean] true if the device was closed, false if already closed
229
+ #
230
+ # @example
231
+ # output.close
142
232
  def close(*args)
143
233
  if @enabled
144
234
  @device.close(*args)
@@ -149,14 +239,41 @@ module MIDICommunications
149
239
  end
150
240
  end
151
241
 
152
- # Returns true if the device is not enabled
153
- # @return [Boolean]
242
+ # Returns true if the device is closed (not enabled).
243
+ #
244
+ # @return [Boolean] true if device is closed
154
245
  def closed?
155
246
  !@enabled
156
247
  end
157
248
 
158
- # Add attributes for the device instance
159
- # :direction, :id, :name
249
+ # @!attribute [r] direction
250
+ # @return [Symbol] the device direction (:input or :output)
251
+
252
+ # @!attribute [r] enabled
253
+ # @return [Boolean] whether the device is currently open
254
+
255
+ # @!attribute [r] id
256
+ # @return [Integer] the device ID
257
+
258
+ # @!attribute [r] manufacturer
259
+ # @return [String] the device manufacturer name
260
+
261
+ # @!attribute [r] model
262
+ # @return [String] the device model name
263
+
264
+ # @!attribute [r] name
265
+ # @return [String] the device name
266
+
267
+ # @!attribute [r] display_name
268
+ # @return [String] the device display name
269
+
270
+ # @!method enabled?
271
+ # @return [Boolean] alias for {#enabled}
272
+
273
+ # @!method type
274
+ # @return [Symbol] alias for {#direction}
275
+
276
+ # @api private
160
277
  def self.included(base)
161
278
  base.send(:attr_reader, :direction)
162
279
  base.send(:attr_reader, :enabled)
@@ -1,42 +1,54 @@
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
- # Returns any data in the input buffer that have been received since the last call to a
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.
7
11
  #
8
- # The data is returned as array of MIDI event hashes as such:
9
- # [
10
- # { data: [144, 60, 100], timestamp: 1024 },
11
- # { data: [128, 60, 100], timestamp: 1100 },
12
- # { data: [144, 40, 120], timestamp: 1200 }
13
- # ]
12
+ # **Blocks until at least one message has arrived**, and then returns
13
+ # every message that accumulated. It never returns an empty array.
14
14
  #
15
- # In this case, the data is an array of Numeric bytes
16
- # The timestamp is the number of millis since this input was enabled
17
- # Arguments are passed to the underlying device object
15
+ # This is part of the contract every platform adapter implements, not an
16
+ # accident of one of them — see {PhysicalLayer}. A reader loop therefore
17
+ # needs no delay of its own, and adding one only delays the messages that
18
+ # are already waiting.
18
19
  #
19
- # @param [*Object] args
20
- # @return [Array<Hash>]
20
+ # @param args [Object] arguments passed to the underlying device
21
+ # @return [Array<Hash>] message hashes with :data and :timestamp keys
22
+ #
23
+ # @example
24
+ # messages = input.gets
25
+ # # => [{ data: [144, 60, 100], timestamp: 1024 },
26
+ # # { data: [128, 60, 100], timestamp: 1100 }]
27
+ #
28
+ # @example Read messages as they arrive
29
+ # loop do
30
+ # input.gets.each { |message| puts message[:data].inspect }
31
+ # end
32
+ #
33
+ # @see PhysicalLayer the contract this method's behaviour comes from
21
34
  def gets(*args)
22
35
  @device.gets(*args)
23
36
  rescue SystemExit, Interrupt
24
37
  exit
25
38
  end
26
39
 
27
- # Returns any data in the input buffer that have been received since the last call to a
28
- # StreamReader method. If a StreamReader method has not yet been called, all data received
29
- # since the program was initialized will be returned
40
+ # Reads MIDI messages as hex strings.
30
41
  #
31
- # Similar to Input#gets except that the returned message data as string of hex digits eg:
32
- # [
33
- # { data: "904060", timestamp: 904 },
34
- # { data: "804060", timestamp: 1150 },
35
- # { data: "90447F", timestamp: 1300 }
36
- # ]
42
+ # Blocks exactly as {#gets} does, and returns the same messages with
43
+ # `:data` as a hex string instead of an array of bytes.
37
44
  #
38
- # @param [*Object] args
39
- # @return [Array<Hash>]
45
+ # @param args [Object] arguments passed to the underlying device
46
+ # @return [Array<Hash>] array of message hashes with :data (String) and :timestamp keys
47
+ #
48
+ # @example
49
+ # messages = input.gets_s
50
+ # # => [{ data: "904060", timestamp: 904 },
51
+ # # { data: "804060", timestamp: 1150 }]
40
52
  def gets_s(*args)
41
53
  @device.gets_s(*args)
42
54
  rescue SystemExit, Interrupt
@@ -45,29 +57,33 @@ module MIDICommunications
45
57
  alias gets_bytestr gets_s
46
58
  alias gets_hex gets_s
47
59
 
48
- # Returns any data in the input buffer that have been received since the last call to a
49
- # StreamReader method. If a StreamReader method has not yet been called, all data received
50
- # since the program was initialized will be returned
60
+ # Reads MIDI data as a flat array of bytes.
61
+ #
62
+ # Blocks as {#gets} does, and returns all message data concatenated into
63
+ # a single array, without timestamps.
51
64
  #
52
- # Similar to Input#gets except that the returned message data as an array of data bytes such as
53
- # [144, 60, 100, 128, 60, 100, 144, 40, 120]
65
+ # @param args [Object] arguments passed to the underlying device
66
+ # @return [Array<Integer>] flat array of all MIDI bytes
54
67
  #
55
- # @param [*Object] args
56
- # @return [Array<Integer>]
68
+ # @example
69
+ # data = input.gets_data
70
+ # # => [144, 60, 100, 128, 60, 100, 144, 40, 120]
57
71
  def gets_data(*args)
58
72
  arr = gets(*args)
59
73
  arr.map { |msg| msg[:data] }.inject(:+)
60
74
  end
61
75
 
62
- # Returns any data in the input buffer that have been received since the last call to a
63
- # StreamReader method. If a StreamReader method has not yet been called, all data received
64
- # since the program was initialized will be returned
76
+ # Reads MIDI data as a concatenated hex string.
77
+ #
78
+ # Returns all message data concatenated into a single hex string,
79
+ # without timestamps.
65
80
  #
66
- # Similar to Input#gets except that the returned message data as a string of data such as
67
- # "90406080406090447F"
81
+ # @param args [Object] arguments passed to the underlying device
82
+ # @return [String] concatenated hex string of all MIDI data
68
83
  #
69
- # @param [*Object] args
70
- # @return [String]
84
+ # @example
85
+ # data = input.gets_data_s
86
+ # # => "90406080406090447F"
71
87
  def gets_data_s(*args)
72
88
  arr = gets_bytestr(*args)
73
89
  arr.map { |msg| msg[:data] }.join