midi-communications 0.7.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c54220cd8b8985c56f5a076a8f16a1e6bacf76e6e71ed84cbf86c5e090014fad
4
- data.tar.gz: d29c68b6c6a1d04aca389d3a7b093a16da815d7532194ef0020aa0215967db40
3
+ metadata.gz: 21b452b0402e76c284ff8e74011e54e193528f27eccc6e67e03d90be46eeaf98
4
+ data.tar.gz: e5bc3c0d00284f259f2808fe2e3f6b231eadea633c6cf7bf6a9217f49cdeb0d5
5
5
  SHA512:
6
- metadata.gz: 8705e81b899107f807099b4ff073bedaea01bfeffeddd956a6833d7143c9cb65118b9b67f2f9a3fedf6539aea5578732478ef0f88f1bfe09d4c3257961e259ae
7
- data.tar.gz: 0a41a5f60e95477efdf99be00a5af42a5cdaf3e957ddf234aadef9df8b0d518f5bd01f339476ba23b5e1a6899fae803e79684c25b13b03ed7d2a450186d975b5
6
+ metadata.gz: 86c68969777a5d3322ffab60927763a39b742149746165251ae51cecb6f3c718daa0f3043bd37f7386fcb60db5a787ad5a9d8df66c4ee45287ea5ab74914b22d
7
+ data.tar.gz: fb2441905249105c97d21f648a2fb347a702de332cde9d02013dc4b2b239341f1eee76d0b4faaa2dfdb5ee52552cf7e179e7b269feadc6c7fd7a542a60f16a47
@@ -0,0 +1,17 @@
1
+ name: Notify Plugin Rebuild
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+
7
+ jobs:
8
+ notify:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Trigger nota-plugin rebuild
12
+ uses: peter-evans/repository-dispatch@v3
13
+ with:
14
+ token: ${{ secrets.PLUGIN_REBUILD_PAT }}
15
+ repository: javier-sy/nota-plugin
16
+ event-type: source-updated
17
+ client-payload: '{"repo": "${{ github.repository }}", "sha": "${{ github.sha }}"}'
data/README.md CHANGED
@@ -15,7 +15,7 @@ This library is part of a suite of Ruby libraries for MIDI:
15
15
  | Low level MIDI interface to MacOS | [MIDI Communications MacOS Layer](https://github.com/javier-sy/midi-communications-macos) |
16
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)) |
17
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))|
18
- | 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) |
19
19
 
20
20
  This library is based on [Ari Russo's](http://github.com/arirusso) library [UniMIDI](https://github.com/arirusso/unimidi).
21
21
 
@@ -37,7 +37,7 @@ Platform
37
37
  * OSX: [midi-communications-macos](http://github.com/javier-sy/midi-communications-macos)
38
38
  * JRuby: [midi-jruby](http://github.com/arirusso/midi-jruby) (**TODO: update to midi-communications-jruby**)
39
39
  * Linux: [alsa-rawmidi](http://github.com/arirusso/alsa-rawmidi) (**TODO: update to midi-communications-linux**)
40
- * 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)
41
41
 
42
42
  ### Install
43
43
 
@@ -134,6 +134,6 @@ Thanks to [Ari Russo](http://github.com/arirusso) for his ruby library [unimidi]
134
134
 
135
135
  ### License
136
136
 
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
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
138
138
 
139
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)
@@ -26,6 +26,17 @@ module MIDICommunications
26
26
  def outputs
27
27
  ::MIDICommunicationsMacOS::Endpoint.all_by_type[:destination]
28
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
29
40
  end
30
41
  end
31
42
  end
@@ -1,10 +1,15 @@
1
- require 'midi-winmm'
1
+ require 'midi-communications-windows'
2
2
 
3
3
  module MIDICommunications
4
4
  module Adapter
5
- # Windows adapter using the midi-winmm gem.
5
+ # Windows adapter using the midi-communications-windows gem.
6
6
  #
7
- # Uses Windows Multimedia API to communicate with MIDI devices.
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.
8
13
  #
9
14
  # @api private
10
15
  module Windows
@@ -14,15 +19,15 @@ module MIDICommunications
14
19
  extend self
15
20
 
16
21
  # Returns all available MIDI input devices.
17
- # @return [Array<MIDIWinMM::Input>]
22
+ # @return [Array<MIDICommunicationsWindows::Input>]
18
23
  def inputs
19
- ::MIDIWinMM::Device.all_by_type[:input]
24
+ ::MIDICommunicationsWindows::Device.all_by_type[:input]
20
25
  end
21
26
 
22
27
  # Returns all available MIDI output devices.
23
- # @return [Array<MIDIWinMM::Output>]
28
+ # @return [Array<MIDICommunicationsWindows::Output>]
24
29
  def outputs
25
- ::MIDIWinMM::Device.all_by_type[:output]
30
+ ::MIDICommunicationsWindows::Device.all_by_type[:output]
26
31
  end
27
32
  end
28
33
  end
@@ -45,8 +45,12 @@ module MIDICommunications
45
45
 
46
46
  # Finds a device by its name.
47
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
+ #
48
52
  # @param name [String, Symbol] the device name to search for
49
- # @return [Input, Output, nil] the matching device or nil if not found
53
+ # @return [Input, Output, nil] the first matching device, or nil
50
54
  #
51
55
  # @example
52
56
  # output = MIDICommunications::Output.find_by_name("IAC Driver Bus 1")
@@ -7,25 +7,30 @@ module MIDICommunications
7
7
  #
8
8
  # @api public
9
9
  module StreamReader
10
- # Reads MIDI messages from the input buffer.
10
+ # Reads MIDI messages from the input.
11
11
  #
12
- # Returns all messages received since the last read. Each message
13
- # includes the MIDI data and a timestamp.
12
+ # **Blocks until at least one message has arrived**, and then returns
13
+ # every message that accumulated. It never returns an empty array.
14
+ #
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.
14
19
  #
15
20
  # @param args [Object] arguments passed to the underlying device
16
- # @return [Array<Hash>] array of message hashes with :data and :timestamp keys
21
+ # @return [Array<Hash>] message hashes with :data and :timestamp keys
17
22
  #
18
23
  # @example
19
24
  # messages = input.gets
20
25
  # # => [{ data: [144, 60, 100], timestamp: 1024 },
21
26
  # # { data: [128, 60, 100], timestamp: 1100 }]
22
27
  #
23
- # @example Process messages in a loop
28
+ # @example Read messages as they arrive
24
29
  # loop do
25
- # messages = input.gets
26
- # messages.each { |m| puts m[:data].inspect }
27
- # sleep(0.01)
30
+ # input.gets.each { |message| puts message[:data].inspect }
28
31
  # end
32
+ #
33
+ # @see PhysicalLayer the contract this method's behaviour comes from
29
34
  def gets(*args)
30
35
  @device.gets(*args)
31
36
  rescue SystemExit, Interrupt
@@ -34,7 +39,8 @@ module MIDICommunications
34
39
 
35
40
  # Reads MIDI messages as hex strings.
36
41
  #
37
- # Similar to {#gets} but returns data as hex strings instead of byte arrays.
42
+ # Blocks exactly as {#gets} does, and returns the same messages with
43
+ # `:data` as a hex string instead of an array of bytes.
38
44
  #
39
45
  # @param args [Object] arguments passed to the underlying device
40
46
  # @return [Array<Hash>] array of message hashes with :data (String) and :timestamp keys
@@ -53,8 +59,8 @@ module MIDICommunications
53
59
 
54
60
  # Reads MIDI data as a flat array of bytes.
55
61
  #
56
- # Returns all message data concatenated into a single array,
57
- # without timestamps.
62
+ # Blocks as {#gets} does, and returns all message data concatenated into
63
+ # a single array, without timestamps.
58
64
  #
59
65
  # @param args [Object] arguments passed to the underlying device
60
66
  # @return [Array<Integer>] flat array of all MIDI bytes
@@ -1,37 +1,104 @@
1
1
  module MIDICommunications
2
- # Populates MIDI devices using platform-specific adapters.
2
+ # Populates MIDI devices from the platform adapter.
3
3
  #
4
- # This class lazily loads and caches MIDI devices from the underlying
5
- # platform adapter (macOS, Linux, Windows, or JRuby).
4
+ # The device list is built once and kept, because enumerating is not free —
5
+ # on macOS it walks every device, entity and endpoint Core MIDI knows about —
6
+ # and most programs ask for it far more often than the machine's hardware
7
+ # changes. {refresh} is how a program that outlives a hardware change asks
8
+ # for the list again.
9
+ #
10
+ # ## What refreshing does not do
11
+ #
12
+ # A device that is still present comes back as the object it came back as
13
+ # before, rather than as a new wrapper around the same port. That matters
14
+ # because callers hold on to these: `Musa::Clock::InputMidiClock` keeps an
15
+ # {Input} for the length of a piece, `Musa::MIDIVoices` keeps an {Output}, and
16
+ # {Device::InstanceMethods#open} registers an `at_exit` on the instance.
17
+ # Replacing the list wholesale would leave those objects open, still due to be
18
+ # closed at exit, and no longer in {Input.all} — a device the program is
19
+ # actively using that the program can no longer find.
20
+ #
21
+ # "Still present" means the same id reporting the same name.
6
22
  #
7
23
  # @api private
8
24
  class Loader
9
25
  class << self
10
26
  # Sets the platform-specific loader to use.
11
27
  #
12
- # @param loader [Module] a loader module with `inputs` and `outputs` methods
28
+ # Any devices already enumerated are discarded: they came from a
29
+ # different platform adapter and mean nothing to this one.
30
+ #
31
+ # @param loader [Module] a loader answering `inputs` and `outputs`, and
32
+ # optionally `refresh`; see {PhysicalLayer}
13
33
  # @return [Module] the loader
14
34
  def use(loader)
15
35
  @loader = loader
36
+ @devices = nil
37
+
38
+ loader
16
39
  end
17
40
 
18
41
  # Returns all MIDI devices, optionally filtered by direction.
19
42
  #
20
- # Lazily loads and caches devices from the platform adapter on first call.
21
- #
22
- # @param options [Hash] filter options
23
- # @option options [Symbol] :direction Return only :input or :output devices
24
- # @return [Array<Input>, Array<Output>] array of devices
25
- def devices(options = {})
26
- if @devices.nil?
27
- inputs = @loader.inputs.map { |device| ::MIDICommunications::Input.new(device) }
28
- outputs = @loader.outputs.map { |device| ::MIDICommunications::Output.new(device) }
29
- @devices = {
30
- input: inputs,
31
- output: outputs
32
- }
33
- end
34
- options[:direction].nil? ? @devices.values.flatten : @devices[options[:direction]]
43
+ # Enumerates on the first call, and then answers from what it found.
44
+ #
45
+ # @param direction [Symbol, nil] `:input` or `:output`, or nil for both
46
+ # @param refresh [Boolean] ask the platform adapter again first
47
+ # @return [Array<Input>, Array<Output>] the devices
48
+ def devices(direction: nil, refresh: false)
49
+ populate if refresh || @devices.nil?
50
+
51
+ direction.nil? ? @devices.values.flatten : @devices[direction]
52
+ end
53
+
54
+ # Asks the platform adapter what devices exist now.
55
+ #
56
+ # Call this when devices may have been plugged in or unplugged since the
57
+ # program started. Devices that are still there keep their identity; see
58
+ # the note on the class.
59
+ #
60
+ # @return [Array<Input, Output>] every device, after re-enumerating
61
+ #
62
+ # @example
63
+ # MIDICommunications::Loader.refresh
64
+ # MIDICommunications::Output.all # now includes what was just plugged in
65
+ def refresh
66
+ devices(refresh: true)
67
+ end
68
+
69
+ private
70
+
71
+ # Asks the adapter for its devices and wraps them, keeping the wrapper of
72
+ # every device that was already there.
73
+ #
74
+ # The adapter is told to re-enumerate first if it can. Without that, an
75
+ # adapter with a cache of its own — the macOS one has — would hand back
76
+ # the same list it handed back before, and refreshing here would rebuild
77
+ # wrappers around a stale answer.
78
+ #
79
+ # Not on the first enumeration, though: there is nothing to invalidate
80
+ # yet, and asking a platform to walk its devices twice to answer the
81
+ # first question would make every program pay for a capability most of
82
+ # them never use.
83
+ #
84
+ # @return [void]
85
+ def populate
86
+ @loader.refresh if @devices && @loader.respond_to?(:refresh)
87
+
88
+ @devices = {
89
+ input: wrap(:input, @loader.inputs) { |device| Input.new(device) },
90
+ output: wrap(:output, @loader.outputs) { |device| Output.new(device) }
91
+ }
92
+ end
93
+
94
+ # @param direction [Symbol] `:input` or `:output`
95
+ # @param devices [Array] the adapter's device objects
96
+ # @yield [device] builds a wrapper for a device not seen before
97
+ # @return [Array<Input>, Array<Output>]
98
+ def wrap(direction, devices)
99
+ known = (@devices && @devices[direction] || []).to_h { |device| [[device.id, device.name], device] }
100
+
101
+ devices.map { |device| known[[device.id, device.name]] || yield(device) }
35
102
  end
36
103
  end
37
104
  end
@@ -0,0 +1,105 @@
1
+ module MIDICommunications
2
+ # The contract a platform adapter's device objects must satisfy.
3
+ #
4
+ # This module defines no behaviour. It exists because the contract between
5
+ # `midi-communications` and the low-level gem underneath it was, until now,
6
+ # implicit: spread across {Loader}, {Device}, {Input::StreamReader} and
7
+ # {Output}, and discoverable only by reading all four and inferring what they
8
+ # assume. That is workable while one adapter exists. It stops being workable
9
+ # the moment a second one is written, because the second author reads the
10
+ # documentation rather than the macOS gem's source, and the documentation did
11
+ # not say any of this.
12
+ #
13
+ # An adapter supplies a loader module answering `inputs` and `outputs` with
14
+ # arrays of device objects, and optionally `refresh` (see {Loader.refresh}).
15
+ # Each device object must behave as described below. {Input} and {Output}
16
+ # wrap them; nothing else in this library touches them.
17
+ #
18
+ # ## Attributes, readable before the device is opened
19
+ #
20
+ # {Input} and {Output} read these in their constructor, which runs while the
21
+ # device list is being built and long before anyone opens anything. An
22
+ # adapter that only knows a device's name once it is open does not satisfy
23
+ # this contract.
24
+ #
25
+ # | Method | Type | Meaning |
26
+ # | --- | --- | --- |
27
+ # | `id` | Integer | identifies the device; see the note on uniqueness below |
28
+ # | `name` | String | the device's name |
29
+ # | `display_name` | String | the name to show a person choosing a device |
30
+ # | `manufacturer` | String, nil | **nil when the platform does not report it** |
31
+ # | `model` | String, nil | **nil when the platform does not report it** |
32
+ # | `type` | Symbol | `:input`/`:source`, or `:output`/`:destination` |
33
+ #
34
+ # `manufacturer` and `model` are nullable on purpose. Core MIDI reports both
35
+ # as strings; the Windows Multimedia API reports numeric codes from a
36
+ # registry that stopped being maintained in the 1990s, from which no honest
37
+ # string can be derived. An adapter in that position returns nil rather than
38
+ # an empty string or an invention, so that a consumer filtering on either can
39
+ # tell the difference between "does not match" and "not known here".
40
+ #
41
+ # ### On the uniqueness of `name`
42
+ #
43
+ # `name` is a label, not an identifier. Two devices may report the same one,
44
+ # and a consumer matching on it may therefore be matching the wrong device.
45
+ #
46
+ # This is measured, not defensive. The Windows Multimedia API stores 31
47
+ # characters of a name and drops the rest without saying so, and two endpoints
48
+ # whose names differed only past that point came back identical in every field
49
+ # it reports — same name, same manufacturer code, same product code, same
50
+ # driver version — distinguishable only by their index. Two ports of one
51
+ # interface whose long names differ at the end collapse the same way.
52
+ #
53
+ # {Device::ClassMethods#find_by_name} returns the first match, which is all it
54
+ # can do.
55
+ #
56
+ # ### On the uniqueness of `id`
57
+ #
58
+ # `id` is unique **within a direction**. It is not necessarily unique across
59
+ # both: on Windows a device is identified by its index among inputs or among
60
+ # outputs, so input 0 and output 0 are different devices and both are valid.
61
+ # Core MIDI happens to number endpoints of both directions from a single
62
+ # counter, but that is a property of Core MIDI and not something a consumer
63
+ # may rely on. Nothing in this library compares an id across directions —
64
+ # `Input.all` and `Output.all` each search their own list.
65
+ #
66
+ # ## Lifecycle
67
+ #
68
+ # - `open(*args)` — makes the device usable. Opening an already-open device
69
+ # must succeed and do nothing, because {Device::InstanceMethods#open} may be
70
+ # called on a device a caller already holds open.
71
+ # - `close(*args)` — releases it. Closing an already-closed device must
72
+ # succeed.
73
+ #
74
+ # ## Input
75
+ #
76
+ # - `gets` — **blocks** until at least one message has arrived, then returns
77
+ # every message accumulated, as an Array of Hashes with:
78
+ # - `:data`, an Array of Integer bytes making up **one complete message**,
79
+ # System Exclusive included, already split per message;
80
+ # - `:timestamp`, a Float of seconds — when the library received the
81
+ # message, not a stamp applied by the driver. Both existing adapters take
82
+ # it with `Time.now.to_f` at the moment the message reaches Ruby.
83
+ # - `gets_s` — the same, with `:data` as a hex String.
84
+ #
85
+ # The blocking is the part most easily got wrong, and it is load-bearing.
86
+ # `Musa::Clock::InputMidiClock` reads MIDI Clock in a loop with no delay of
87
+ # its own, relying on `gets` to be where the thread waits. An adapter whose
88
+ # `gets` returned an empty array immediately would turn that loop into a spin
89
+ # on a full core — and would do it silently, because the music would still
90
+ # play.
91
+ #
92
+ # ## Output
93
+ #
94
+ # - `puts_bytes(*bytes)` — sends one message given as Integer bytes.
95
+ # - `puts_s(hex_string)` — sends one message given as hex.
96
+ #
97
+ # Both must accept System Exclusive.
98
+ #
99
+ # @see Loader how adapters are registered and their devices enumerated
100
+ # @see Input::StreamReader the reading methods built on `gets`
101
+ #
102
+ # @api public
103
+ module PhysicalLayer
104
+ end
105
+ end
@@ -7,14 +7,23 @@ module MIDICommunications
7
7
 
8
8
  # Converts an array of numeric bytes to a hex string.
9
9
  #
10
+ # Every byte becomes exactly two characters. Without the padding a byte
11
+ # below 0x10 produces one, and the string can no longer be read back as
12
+ # bytes: [0x90, 0x0A, 0x64] came out as "90a64", which is five characters
13
+ # and describes no MIDI message at all.
14
+ #
10
15
  # @param bytes [Array<Integer>] array of numeric bytes (e.g., [0x90, 0x40, 0x40])
11
16
  # @return [String] hex string representation (e.g., "904040")
12
17
  #
13
18
  # @example
14
19
  # TypeConversion.numeric_byte_array_to_hex_string([0x90, 0x40, 0x40])
15
20
  # # => "904040"
21
+ #
22
+ # @example A byte below 0x10 still takes two characters
23
+ # TypeConversion.numeric_byte_array_to_hex_string([0x90, 0x0A, 0x64])
24
+ # # => "900A64"
16
25
  def numeric_byte_array_to_hex_string(bytes)
17
- bytes.map { |b| b.to_s(16) }.join
26
+ bytes.map { |byte| format('%02X', byte) }.join
18
27
  end
19
28
  end
20
29
  end
@@ -1,4 +1,4 @@
1
1
  module MIDICommunications
2
2
  # Current version of the midi-communications gem.
3
- VERSION = '0.7.0'.freeze
3
+ VERSION = '0.7.1'.freeze
4
4
  end
@@ -8,6 +8,7 @@
8
8
 
9
9
  # modules
10
10
  require 'midi-communications/device'
11
+ require 'midi-communications/physical_layer'
11
12
  require 'midi-communications/platform'
12
13
  require 'midi-communications/type_conversion'
13
14
 
@@ -3,7 +3,7 @@ require_relative 'lib/midi-communications/version'
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'midi-communications'
5
5
  s.version = MIDICommunications::VERSION
6
- s.date = '2025-08-23'
6
+ s.date = '2026-09-06'
7
7
  s.summary = 'Platform independent realtime MIDI input and output for Ruby'
8
8
  s.description = 'Access MIDI devices for MacOS, Linux (wip), Windows (wip) and JRuby (wip).'
9
9
  s.authors = ['Javier Sánchez Yeste']
@@ -20,10 +20,16 @@ Gem::Specification.new do |s|
20
20
  'documentation_uri' => 'https://www.rubydoc.info/gems/midi-communications'
21
21
  }
22
22
 
23
+ # RubyGems has no platform-conditional dependencies, so both platform layers
24
+ # are declared unconditionally. That is harmless only because both are pure
25
+ # Ruby over a library the operating system already provides -- CoreMIDI and
26
+ # winmm.dll -- so each installs anywhere and is simply not required where it
27
+ # does not apply. A layer that shipped compiled binaries per platform could
28
+ # not be declared this way.
23
29
  s.add_runtime_dependency 'midi-communications-macos', '~> 0.7'
30
+ s.add_runtime_dependency 'midi-communications-windows', '~> 0.0.1'
24
31
  # s.add_runtime_dependency 'alsa-rawmidi', '~> 0.3', '>= 0.3.1'
25
32
  # s.add_runtime_dependency 'midi-jruby', '~> 0.1', '>= 0.1.4'
26
- # s.add_runtime_dependency 'midi-winmm', '~> 0.1', '>= 0.1.10'
27
33
 
28
34
  s.add_development_dependency 'minitest', '~>5', '>= 5.14.4'
29
35
  s.add_development_dependency 'rake', '~>13', '>= 13.0.6'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: midi-communications
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javier Sánchez Yeste
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-08-23 00:00:00.000000000 Z
10
+ date: 2026-09-06 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: midi-communications-macos
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0.7'
26
+ - !ruby/object:Gem::Dependency
27
+ name: midi-communications-windows
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 0.0.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.0.1
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: minitest
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -132,6 +146,7 @@ executables: []
132
146
  extensions: []
133
147
  extra_rdoc_files: []
134
148
  files:
149
+ - ".github/workflows/notify-plugin.yml"
135
150
  - ".gitignore"
136
151
  - ".version"
137
152
  - ".yardopts"
@@ -154,6 +169,7 @@ files:
154
169
  - lib/midi-communications/input/stream_reader.rb
155
170
  - lib/midi-communications/loader.rb
156
171
  - lib/midi-communications/output.rb
172
+ - lib/midi-communications/physical_layer.rb
157
173
  - lib/midi-communications/platform.rb
158
174
  - lib/midi-communications/type_conversion.rb
159
175
  - lib/midi-communications/version.rb