midi-communications-windows 0.0.3

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.version +6 -0
  4. data/.yardoc/checksums +8 -0
  5. data/.yardoc/complete +0 -0
  6. data/.yardoc/object_types +0 -0
  7. data/.yardoc/objects/root.dat +0 -0
  8. data/.yardoc/proxy_types +0 -0
  9. data/.yardopts +8 -0
  10. data/Gemfile +3 -0
  11. data/LICENSE +165 -0
  12. data/README.md +176 -0
  13. data/Rakefile +13 -0
  14. data/doc/MIDICommunicationsWindows/API/MIDIHdr.html +144 -0
  15. data/doc/MIDICommunicationsWindows/API/MIDIInCaps.html +142 -0
  16. data/doc/MIDICommunicationsWindows/API/MIDIOutCaps.html +140 -0
  17. data/doc/MIDICommunicationsWindows/API/MSG.html +143 -0
  18. data/doc/MIDICommunicationsWindows/API.html +981 -0
  19. data/doc/MIDICommunicationsWindows/Device/ClassMethods.html +365 -0
  20. data/doc/MIDICommunicationsWindows/Device/InstanceMethods.html +1096 -0
  21. data/doc/MIDICommunicationsWindows/Device.html +988 -0
  22. data/doc/MIDICommunicationsWindows/Error.html +438 -0
  23. data/doc/MIDICommunicationsWindows/Input.html +1554 -0
  24. data/doc/MIDICommunicationsWindows/Message.html +700 -0
  25. data/doc/MIDICommunicationsWindows/Output.html +1697 -0
  26. data/doc/MIDICommunicationsWindows/TypeConversion.html +369 -0
  27. data/doc/MIDICommunicationsWindows.html +210 -0
  28. data/doc/_index.html +262 -0
  29. data/doc/class_list.html +54 -0
  30. data/doc/css/common.css +1 -0
  31. data/doc/css/full_list.css +206 -0
  32. data/doc/css/style.css +1089 -0
  33. data/doc/file.README.html +240 -0
  34. data/doc/file.testing-on-windows.html +253 -0
  35. data/doc/file_list.html +64 -0
  36. data/doc/frames.html +22 -0
  37. data/doc/index.html +240 -0
  38. data/doc/js/app.js +801 -0
  39. data/doc/js/full_list.js +334 -0
  40. data/doc/js/jquery.js +4 -0
  41. data/doc/method_list.html +382 -0
  42. data/doc/top-level-namespace.html +112 -0
  43. data/docs/testing-on-windows.md +222 -0
  44. data/examples/input.rb +27 -0
  45. data/examples/list_ports.rb +17 -0
  46. data/examples/output.rb +22 -0
  47. data/examples/sysex_output.rb +15 -0
  48. data/lib/midi-communications-windows/api.rb +340 -0
  49. data/lib/midi-communications-windows/device.rb +310 -0
  50. data/lib/midi-communications-windows/input.rb +486 -0
  51. data/lib/midi-communications-windows/message.rb +133 -0
  52. data/lib/midi-communications-windows/output.rb +206 -0
  53. data/lib/midi-communications-windows/type_conversion.rb +20 -0
  54. data/lib/midi-communications-windows/version.rb +4 -0
  55. data/lib/midi-communications-windows.rb +76 -0
  56. data/midi-communications-windows.gemspec +32 -0
  57. metadata +220 -0
@@ -0,0 +1,206 @@
1
+ module MIDICommunicationsWindows
2
+ # A MIDI output port: somewhere to send messages.
3
+ #
4
+ # @example Send a note through the first output
5
+ # output = MIDICommunicationsWindows::Output.first
6
+ # output.open
7
+ # output.puts(0x90, 60, 100) # Note On, middle C
8
+ # sleep 0.5
9
+ # output.puts(0x80, 60, 0) # Note Off
10
+ #
11
+ # @example Send the same thing as a hex string
12
+ # output.puts_s('903C64')
13
+ #
14
+ # @see Input for receiving
15
+ #
16
+ # @api public
17
+ class Output
18
+ extend Device::ClassMethods
19
+ include Device::InstanceMethods
20
+
21
+ # @return [Symbol] `:output`
22
+ def self.direction
23
+ :output
24
+ end
25
+
26
+ # Sends a MIDI message, in whichever form it is given.
27
+ #
28
+ # @param args [Array<Integer>, Array<String>, Integer, String] one message,
29
+ # as loose bytes, an array of bytes, or a hex string
30
+ # @return [Boolean] true
31
+ # @raise [Error] if WinMM refuses the message
32
+ #
33
+ # @example Every one of these sends the same Note On
34
+ # output.puts(0x90, 60, 100)
35
+ # output.puts([0x90, 60, 100])
36
+ # output.puts('903C64')
37
+ def puts(*args)
38
+ case args.first
39
+ when Array then args.each { |argument| puts(*argument) }
40
+ when Integer then puts_bytes(*args)
41
+ when String then puts_s(*args)
42
+ end
43
+
44
+ true
45
+ end
46
+ alias write puts
47
+
48
+ # Sends a MIDI message given as a hex string.
49
+ #
50
+ # @param data [String] hex, two characters per byte, e.g. `'903C64'`
51
+ # @return [Boolean] true
52
+ # @raise [Error] if WinMM refuses the message
53
+ def puts_s(data)
54
+ puts_bytes(*data.scan(/../).map(&:hex))
55
+ end
56
+ alias puts_bytestr puts_s
57
+ alias puts_hex puts_s
58
+
59
+ # Sends a MIDI message given as numeric bytes.
60
+ #
61
+ # System Exclusive takes a different route through WinMM than everything
62
+ # else, and this is where the two part company.
63
+ #
64
+ # @param data [Array<Integer>] the message, status byte first
65
+ # @return [Boolean] true
66
+ # @raise [Error] if WinMM refuses the message
67
+ def puts_bytes(*data)
68
+ Message.sysex?(data) ? send_sysex(data) : send_short(data)
69
+
70
+ true
71
+ end
72
+
73
+ private
74
+
75
+ # Opens the port.
76
+ #
77
+ # `CALLBACK_NULL` asks WinMM for no notifications at all. The only event an
78
+ # output can raise is `MM_MOM_DONE`, saying a System Exclusive buffer has
79
+ # finished, and {#send_sysex} learns that from the buffer's own flags
80
+ # instead. Asking for no callback keeps the driver from ever calling into
81
+ # Ruby, which is one whole class of threading problem that then cannot
82
+ # occur on the sending side.
83
+ #
84
+ # @return [void]
85
+ # @raise [Error]
86
+ # @api private
87
+ def connect
88
+ handle_pointer = FFI::MemoryPointer.new(:uintptr_t)
89
+
90
+ API.check!(API.midiOutOpen(handle_pointer, @id, 0, 0, API::CALLBACK_NULL),
91
+ :midiOutOpen, :output)
92
+
93
+ @handle = handle_pointer.read(:uintptr_t)
94
+ end
95
+
96
+ # Closes the port.
97
+ #
98
+ # `midiOutReset` first, and not only for tidiness: it turns off every note
99
+ # the port has left sounding and releases any buffer still queued. Closing
100
+ # without it can leave a synthesiser holding notes with nothing left to
101
+ # send it a Note Off.
102
+ #
103
+ # @return [void]
104
+ # @raise [Error]
105
+ # @api private
106
+ def disconnect
107
+ API.check!(API.midiOutReset(@handle), :midiOutReset, :output)
108
+ API.check!(API.midiOutClose(@handle), :midiOutClose, :output)
109
+
110
+ @handle = nil
111
+ end
112
+
113
+ # Sends a message of three bytes or fewer.
114
+ #
115
+ # @param data [Array<Integer>]
116
+ # @return [void]
117
+ # @raise [Error]
118
+ # @api private
119
+ def send_short(data)
120
+ API.check!(API.midiOutShortMsg(@handle, Message.pack(data)), :midiOutShortMsg, :output)
121
+ end
122
+
123
+ # Sends a System Exclusive message.
124
+ #
125
+ # WinMM wants such a message in a buffer it has been told about, and the
126
+ # sequence is fixed: prepare the header, send it, wait for the driver to
127
+ # finish with it, then unprepare. Unpreparing early fails with
128
+ # `MIDIERR_STILLPLAYING`, and freeing the buffer early is worse than that.
129
+ #
130
+ # The wait can be a real one. MIDI runs at 31250 baud, so on a physical
131
+ # interface a byte takes about 320 microseconds and a 200-byte dump should
132
+ # occupy something like 64 milliseconds during which this method does not
133
+ # return — a caller sending System Exclusive from a sequencer thread should
134
+ # know that is possible. Said as arithmetic rather than as measurement: the
135
+ # only device this has been run against is a software synthesiser, which
136
+ # marks the buffer done at once because there is no wire, and returned in
137
+ # well under a millisecond.
138
+ #
139
+ # @param data [Array<Integer>] the whole message, 0xF0 through 0xF7
140
+ # @return [void]
141
+ # @raise [Error]
142
+ # @api private
143
+ def send_sysex(data)
144
+ buffer = FFI::MemoryPointer.new(:uint8, data.size)
145
+ buffer.write_array_of_uint8(data)
146
+
147
+ header = API::MIDIHdr.new
148
+ header[:lpData] = buffer
149
+ header[:dwBufferLength] = data.size
150
+ header[:dwBytesRecorded] = data.size
151
+
152
+ API.check!(API.midiOutPrepareHeader(@handle, header, API::MIDIHdr.size),
153
+ :midiOutPrepareHeader, :output)
154
+
155
+ begin
156
+ API.check!(API.midiOutLongMsg(@handle, header, API::MIDIHdr.size), :midiOutLongMsg, :output)
157
+ wait_until_sent(header)
158
+ rescue Error
159
+ # The driver still owns the buffer. Unpreparing it now would fail with
160
+ # MIDIERR_STILLPLAYING and the memory would be freed underneath a device
161
+ # that is still reading it; midiOutReset takes it back first.
162
+ API.midiOutReset(@handle)
163
+ raise
164
+ ensure
165
+ API.midiOutUnprepareHeader(@handle, header, API::MIDIHdr.size)
166
+ end
167
+ end
168
+
169
+ # Blocks until the driver marks the buffer done, or gives up.
170
+ #
171
+ # There is no blocking WinMM call to wait on, so this polls the flag the
172
+ # driver sets. The interval is short enough not to add meaningfully to a
173
+ # transfer measured in tens of milliseconds, and long enough not to spin.
174
+ #
175
+ # The wait is bounded because the flag is set by a device that can go away:
176
+ # unplug an interface in the middle of a dump and it is never set at all.
177
+ # Without a limit this would spin for the life of the process, on whichever
178
+ # thread happened to be sending — in MusaDSL, the sequencer's.
179
+ #
180
+ # @param header [API::MIDIHdr]
181
+ # @return [void]
182
+ # @raise [Error] if the device has not finished within {SYSEX_TIMEOUT}
183
+ # @api private
184
+ def wait_until_sent(header)
185
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + SYSEX_TIMEOUT
186
+
187
+ until (header[:dwFlags] & API::MHDR_DONE) != 0
188
+ if Process.clock_gettime(Process::CLOCK_MONOTONIC) > deadline
189
+ raise Error.new(:midiOutLongMsg, nil,
190
+ "the device did not finish sending a #{header[:dwBufferLength]}-byte " \
191
+ "System Exclusive message within #{SYSEX_TIMEOUT} s")
192
+ end
193
+
194
+ sleep SYSEX_POLL_INTERVAL
195
+ end
196
+ end
197
+
198
+ # How often to look at a System Exclusive buffer's done flag, in seconds.
199
+ SYSEX_POLL_INTERVAL = 0.001
200
+
201
+ # How long to wait for a System Exclusive message to go out, in seconds.
202
+ # MIDI runs at 31250 baud, so this is room for roughly 3 kB — far more than
203
+ # any message, and short enough that a vanished device is noticed.
204
+ SYSEX_TIMEOUT = 1.0
205
+ end
206
+ end
@@ -0,0 +1,20 @@
1
+ module MIDICommunicationsWindows
2
+ # Utility methods for converting between MIDI data formats.
3
+ #
4
+ # @api public
5
+ module TypeConversion
6
+ extend self
7
+
8
+ # Converts an array of numeric bytes to a hex string.
9
+ #
10
+ # @param bytes [Array<Integer>] numeric bytes, e.g. `[0x90, 0x40, 0x40]`
11
+ # @return [String] uppercase hex, two characters per byte, e.g. `'904040'`
12
+ #
13
+ # @example
14
+ # TypeConversion.numeric_bytes_to_hex_string([0x90, 0x40, 0x40])
15
+ # # => "904040"
16
+ def numeric_bytes_to_hex_string(bytes)
17
+ bytes.map { |byte| format('%02X', byte) }.join
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ module MIDICommunicationsWindows
2
+ # Current version of the midi-communications-windows gem.
3
+ VERSION = '0.0.3'.freeze
4
+ end
@@ -0,0 +1,76 @@
1
+ #
2
+ # midi-communications-windows
3
+ # Realtime MIDI IO with Ruby for Windows
4
+ #
5
+ # (c)2026 Javier Sánchez Yeste, licensed under LGPL 3.0 License
6
+ #
7
+
8
+ # Libs
9
+ require 'ffi'
10
+
11
+ # Modules with no platform binding of their own, so that they load anywhere
12
+ require 'midi-communications-windows/message'
13
+ require 'midi-communications-windows/type_conversion'
14
+
15
+ # Modules and classes that bind winmm.dll
16
+ require 'midi-communications-windows/api'
17
+ require 'midi-communications-windows/device'
18
+ require 'midi-communications-windows/input'
19
+ require 'midi-communications-windows/output'
20
+
21
+ require_relative 'midi-communications-windows/version'
22
+
23
+ # Windows-specific MIDI I/O through the Windows Multimedia (WinMM) API.
24
+ #
25
+ # This library gives low-level access to MIDI ports on Windows by binding
26
+ # `winmm.dll` with FFI. It is normally used through the higher-level
27
+ # {https://github.com/javier-sy/midi-communications midi-communications} gem,
28
+ # which selects it automatically on Windows.
29
+ #
30
+ # The classes are {Input}, for receiving, and {Output}, for sending. {Device}
31
+ # enumerates both.
32
+ #
33
+ # ## Why WinMM, and not one of the newer APIs
34
+ #
35
+ # Windows has three MIDI APIs a program could use. WinMM is the oldest, and as
36
+ # of February 2026 it is also the most sensible choice for Ruby.
37
+ #
38
+ # Windows MIDI Services, generally available in Windows 11 since then, replaced
39
+ # the MIDI stack underneath. The existing MIDI 1.0 APIs were reconnected to the
40
+ # new service rather than retired, so a WinMM client reaches what the new stack
41
+ # offers with nothing installed — including the loopback endpoints the system
42
+ # now creates itself, which used to need a third-party driver.
43
+ #
44
+ # Whether a port can be opened by more than one program at a time follows the
45
+ # endpoint, not the API. Measured: two clients shared a loopback input and both
46
+ # received every message, while a second open of a classic `wdmaud` device was
47
+ # refused as already in use. On Windows 10 there is no Windows MIDI Services.
48
+ #
49
+ # The new App SDK offers MIDI 2.0 and UMP, which this library does not need —
50
+ # `midi-communications` and MusaDSL are MIDI 1.0 throughout — and it is
51
+ # published only as WinRT. Microsoft's own guidance is that other languages
52
+ # need a WinRT projection from their toolchain, and Ruby has none.
53
+ #
54
+ # ## Why no compiled artifact
55
+ #
56
+ # `winmm.dll` is present on every Windows installation, so this gem binds a
57
+ # library that is already there and ships no binary of its own. The only
58
+ # compiled dependency is `ffi` itself, which is published for every Windows
59
+ # platform including ARM64. Distributing platform-specific binaries through
60
+ # RubyGems is where installations break; there is nothing here to break.
61
+ #
62
+ # @example List the output ports
63
+ # MIDICommunicationsWindows::Output.all.each do |output|
64
+ # puts "#{output.id}: #{output.name}"
65
+ # end
66
+ #
67
+ # @example Send a note
68
+ # output = MIDICommunicationsWindows::Output.first
69
+ # output.open
70
+ # output.puts(0x90, 60, 100)
71
+ #
72
+ # @see https://learn.microsoft.com/en-us/windows/win32/multimedia/midi-reference Microsoft's MIDI reference
73
+ #
74
+ # @api public
75
+ module MIDICommunicationsWindows
76
+ end
@@ -0,0 +1,32 @@
1
+ require_relative 'lib/midi-communications-windows/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'midi-communications-windows'
5
+ s.version = MIDICommunicationsWindows::VERSION
6
+ s.date = '2026-09-06'
7
+ s.summary = 'Realtime MIDI IO with Ruby for Windows'
8
+ s.description = 'Access the Windows Multimedia (WinMM) MIDI API with Ruby.'
9
+ s.authors = ['Javier Sánchez Yeste']
10
+ s.email = ['javier.sy@gmail.com']
11
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
12
+ s.homepage = 'https://github.com/javier-sy/midi-communications-windows'
13
+ s.license = 'LGPL-3.0-or-later'
14
+
15
+ s.required_ruby_version = '>= 2.7'
16
+
17
+ s.metadata = {
18
+ 'homepage_uri' => s.homepage,
19
+ 'source_code_uri' => s.homepage,
20
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/midi-communications-windows'
21
+ }
22
+
23
+ s.add_runtime_dependency 'ffi', '~> 1.15', '>= 1.15.4'
24
+
25
+ s.add_development_dependency 'minitest', '~>5', '>= 5.14.4'
26
+ s.add_development_dependency 'rake', '~>13', '>= 13.0.6'
27
+ s.add_development_dependency 'shoulda-context', '~>2', '>= 2.0.0'
28
+
29
+ s.add_development_dependency 'yard', '~> 0.9'
30
+ s.add_development_dependency 'redcarpet', '~> 3.6'
31
+ s.add_development_dependency 'webrick', '~> 1.8'
32
+ end
metadata ADDED
@@ -0,0 +1,220 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: midi-communications-windows
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Javier Sánchez Yeste
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-09-06 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ffi
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.15'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 1.15.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.15'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 1.15.4
32
+ - !ruby/object:Gem::Dependency
33
+ name: minitest
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '5'
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 5.14.4
42
+ type: :development
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '5'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 5.14.4
52
+ - !ruby/object:Gem::Dependency
53
+ name: rake
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '13'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 13.0.6
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13'
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 13.0.6
72
+ - !ruby/object:Gem::Dependency
73
+ name: shoulda-context
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '2'
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 2.0.0
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2'
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 2.0.0
92
+ - !ruby/object:Gem::Dependency
93
+ name: yard
94
+ requirement: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '0.9'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '0.9'
106
+ - !ruby/object:Gem::Dependency
107
+ name: redcarpet
108
+ requirement: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '3.6'
113
+ type: :development
114
+ prerelease: false
115
+ version_requirements: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '3.6'
120
+ - !ruby/object:Gem::Dependency
121
+ name: webrick
122
+ requirement: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - "~>"
125
+ - !ruby/object:Gem::Version
126
+ version: '1.8'
127
+ type: :development
128
+ prerelease: false
129
+ version_requirements: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: '1.8'
134
+ description: Access the Windows Multimedia (WinMM) MIDI API with Ruby.
135
+ email:
136
+ - javier.sy@gmail.com
137
+ executables: []
138
+ extensions: []
139
+ extra_rdoc_files: []
140
+ files:
141
+ - ".gitignore"
142
+ - ".version"
143
+ - ".yardoc/checksums"
144
+ - ".yardoc/complete"
145
+ - ".yardoc/object_types"
146
+ - ".yardoc/objects/root.dat"
147
+ - ".yardoc/proxy_types"
148
+ - ".yardopts"
149
+ - Gemfile
150
+ - LICENSE
151
+ - README.md
152
+ - Rakefile
153
+ - doc/MIDICommunicationsWindows.html
154
+ - doc/MIDICommunicationsWindows/API.html
155
+ - doc/MIDICommunicationsWindows/API/MIDIHdr.html
156
+ - doc/MIDICommunicationsWindows/API/MIDIInCaps.html
157
+ - doc/MIDICommunicationsWindows/API/MIDIOutCaps.html
158
+ - doc/MIDICommunicationsWindows/API/MSG.html
159
+ - doc/MIDICommunicationsWindows/Device.html
160
+ - doc/MIDICommunicationsWindows/Device/ClassMethods.html
161
+ - doc/MIDICommunicationsWindows/Device/InstanceMethods.html
162
+ - doc/MIDICommunicationsWindows/Error.html
163
+ - doc/MIDICommunicationsWindows/Input.html
164
+ - doc/MIDICommunicationsWindows/Message.html
165
+ - doc/MIDICommunicationsWindows/Output.html
166
+ - doc/MIDICommunicationsWindows/TypeConversion.html
167
+ - doc/_index.html
168
+ - doc/class_list.html
169
+ - doc/css/common.css
170
+ - doc/css/full_list.css
171
+ - doc/css/style.css
172
+ - doc/file.README.html
173
+ - doc/file.testing-on-windows.html
174
+ - doc/file_list.html
175
+ - doc/frames.html
176
+ - doc/index.html
177
+ - doc/js/app.js
178
+ - doc/js/full_list.js
179
+ - doc/js/jquery.js
180
+ - doc/method_list.html
181
+ - doc/top-level-namespace.html
182
+ - docs/testing-on-windows.md
183
+ - examples/input.rb
184
+ - examples/list_ports.rb
185
+ - examples/output.rb
186
+ - examples/sysex_output.rb
187
+ - lib/midi-communications-windows.rb
188
+ - lib/midi-communications-windows/api.rb
189
+ - lib/midi-communications-windows/device.rb
190
+ - lib/midi-communications-windows/input.rb
191
+ - lib/midi-communications-windows/message.rb
192
+ - lib/midi-communications-windows/output.rb
193
+ - lib/midi-communications-windows/type_conversion.rb
194
+ - lib/midi-communications-windows/version.rb
195
+ - midi-communications-windows.gemspec
196
+ homepage: https://github.com/javier-sy/midi-communications-windows
197
+ licenses:
198
+ - LGPL-3.0-or-later
199
+ metadata:
200
+ homepage_uri: https://github.com/javier-sy/midi-communications-windows
201
+ source_code_uri: https://github.com/javier-sy/midi-communications-windows
202
+ documentation_uri: https://www.rubydoc.info/gems/midi-communications-windows
203
+ rdoc_options: []
204
+ require_paths:
205
+ - lib
206
+ required_ruby_version: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - ">="
209
+ - !ruby/object:Gem::Version
210
+ version: '2.7'
211
+ required_rubygems_version: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ requirements: []
217
+ rubygems_version: 3.6.9
218
+ specification_version: 4
219
+ summary: Realtime MIDI IO with Ruby for Windows
220
+ test_files: []