midi-communications-windows 0.0.4 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8fc6b5ad53dfa328b84bf63dcdb0660a2c5d6e00b18faec4eb5549a8f4baa116
4
- data.tar.gz: 664db0add4987b5f180481b26e6d85e3ea66b7b645b59bda5a930bb6fe59eb6d
3
+ metadata.gz: fab76aff1317f582ff061ecac55ad7bdf25f27ed0ad1c55508f7481f88ae159a
4
+ data.tar.gz: b8d67bd3c739c80b5373ef1f6834ed73c20ccfec7301d539706d13703381d98b
5
5
  SHA512:
6
- metadata.gz: 2bfafc2e6f554e568b37a2e53d4261734d858da411d42a8a04a0d3cc3ccbb7bb5fe7d62b7e243c9ca62a0a3804cbf451f02372c86b4b9fb88899a1c872c0e52b
7
- data.tar.gz: 1718a6d1111ab6ff8e4cdbb79d6bc2c3de18aed3478cd5d997283e8c437657d4b3c91670edebaddd661ff8d4b08b7d12ac4541cd3e38284c65811a219eab90ff
6
+ metadata.gz: bd3861ebb4b96e659894b35810a5db436be4901a631ce13f06ae7401d72846d25b32b408f5607cb35020045756ba0e20bc375f4c57947f73f3a45bd303dad8ae
7
+ data.tar.gz: 559595e2a885245f981d4d8bd90376fc584a4d15ebd475c5e5391b81dee1aeafb5b3ea82551af4cae31f717d0f5d940d3b23d5d76984eb158fbd0f55697740b0
data/.yardoc/checksums CHANGED
@@ -4,5 +4,5 @@ lib/midi-communications-windows/input.rb 36e58891d1c604dd4b2f333382a4010660d4786
4
4
  lib/midi-communications-windows/device.rb 9f151193d144c298c064592ee21d44111f968d01
5
5
  lib/midi-communications-windows/output.rb 3069d46892268c529d999363bd05fac891e5f4fc
6
6
  lib/midi-communications-windows/message.rb 2390946620b244ec12a6b373e003304ef9c29e4d
7
- lib/midi-communications-windows/version.rb 4804d6731ca80a4f6f679dd5585ed1b2c7517513
7
+ lib/midi-communications-windows/version.rb 902ae38c68bbfd2670740b7d7bb6a342f77cbb68
8
8
  lib/midi-communications-windows/type_conversion.rb dae7dc5109d955f4693920fa173f5c143dcb7bf1
Binary file
data/README.md CHANGED
@@ -19,66 +19,32 @@ This library is part of a suite of Ruby libraries for MIDI:
19
19
  | 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)) |
20
20
  | 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)) |
21
21
 
22
- ## Status
22
+ You will normally reach it through
23
+ [MIDI Communications](https://github.com/javier-sy/midi-communications), which
24
+ depends on it and selects it on Windows. It implements that gem's physical layer
25
+ contract; anything about how ports behave is documented there, in
26
+ `MIDICommunications::PhysicalLayer`.
23
27
 
24
- **Early.** Everything is written and has been exercised end to end on Windows 11
25
- 25H2 against system loopback endpoints: enumeration, short messages in both
26
- directions, System Exclusive in both directions including a message larger than
27
- the whole buffer queue, twenty open/close cycles, and the error path. What has
28
- not been exercised is anything needing real hardware — a physical interface slow
29
- enough to make `Output`'s System Exclusive wait mean something, and a port
30
- appearing or disappearing while the program runs.
28
+ ## Features
31
29
 
32
- The version stays in the 0.0.x series because that was a virtual machine with no
33
- MIDI hardware in it, not because anything is known to be missing.
30
+ * Simplified API
31
+ * Input and output on multiple devices concurrently
32
+ * Generalized handling of different MIDI Message types (including SysEx)
33
+ * Timestamped input events
34
+ * Patch MIDI via software to other programs using a loopback endpoint
35
+ * No compiled artifact: `winmm.dll` is part of Windows
34
36
 
35
- How those measurements were taken, and what a session with real hardware still
36
- has to close, is written down in
37
- [`dev/testing-on-windows.md`](dev/testing-on-windows.md).
37
+ Runnable examples of the API are in [`examples/`](examples).
38
38
 
39
- ### One thing worth knowing before you use it
39
+ ## Status
40
40
 
41
- `Input#gets` **waits**. It does not return an empty array when nothing has
42
- arrived; it blocks until something does, and then returns everything that
43
- accumulated. Calling it on a quiet port looks exactly like a hung program. That
44
- is deliberate — `Musa::Clock::InputMidiClock` reads in a loop with no delay of
45
- its own and relies on it — but it will surprise anyone arriving from an API that
46
- polls.
41
+ **Early.** Tested on Windows 11 without external MIDI devices. Everything works
42
+ against loopback ports; running it against real hardware is still pending.
47
43
 
48
44
  ## Requirements
49
45
 
50
46
  * [ffi](http://github.com/ffi/ffi)
51
47
 
52
- Nothing else. `winmm.dll` is part of Windows, so this gem binds a library that
53
- is already on the machine and ships no compiled artifact of its own.
54
-
55
- ### Which versions of Windows
56
-
57
- **By construction, old ones.** Everything this gem calls is the oldest MIDI API
58
- Windows has — `midiInOpen`, `midiOutShortMsg`, `midiOutLongMsg` and their
59
- neighbours — plus three functions for a thread's message queue, `GetMessageW`,
60
- `PeekMessageW` and `PostThreadMessageW`. All of it, the Unicode entry points
61
- included, dates from the NT era. Nothing here calls Windows MIDI Services, which
62
- means nothing here needs Windows 11.
63
-
64
- What changes on an older system is not whether the gem works but what the
65
- platform gives it. Before Windows MIDI Services a MIDI port is **exclusive** —
66
- one program at a time — and there are no system loopback endpoints, so routing
67
- MIDI between applications on one machine needs a third-party driver such as
68
- loopMIDI. The same code, a poorer platform.
69
-
70
- A 32-bit Ruby should also be fine: the two size checks that only hold on 64-bit
71
- Windows are guarded, and the two that are checked everywhere describe structures
72
- whose layout does not depend on the word size.
73
-
74
- **None of which has been run.** The only system this has ever executed on is
75
- Windows 11 25H2, in a virtual machine, on an x64 Ruby under ARM emulation. The
76
- paragraphs above say what the code asks of Windows, not what has been observed;
77
- treat them as a reason to expect it to work rather than as a report that it does.
78
-
79
- Where the floor actually sits is a question about Ruby, not about this gem:
80
- RubyInstaller recommends Windows 10 or 11 and publishes no hard cutoff.
81
-
82
48
  ## Installation
83
49
 
84
50
  If you're using Bundler, add this line to your application's Gemfile:
@@ -91,78 +57,7 @@ Otherwise
91
57
 
92
58
  ## Documentation
93
59
 
94
- [rdoc](http://rubydoc.info/github/javier-sy/midi-communications-windows)
95
-
96
- ## Why WinMM and not one of the newer Windows MIDI APIs
97
-
98
- Windows has three MIDI APIs a program could reach for, and as of February 2026
99
- the oldest is the right one for Ruby.
100
-
101
- **Windows MIDI Services** became generally available in Windows 11 in February
102
- 2026, replacing the MIDI stack underneath. Rather than retiring the older APIs,
103
- Microsoft reconnected them to the new service, so a WinMM client needs nothing
104
- installed to reach what the new stack provides — including the loopback
105
- endpoints the system now creates itself, which is what previously required a
106
- third-party driver.
107
-
108
- Multi-client access arrives the same way, but **per endpoint, not per API**.
109
- Measured on Windows 11 25H2 through this library: two clients opened the same
110
- loopback input at once and both received every message, while a second open of
111
- the classic `wdmaud` software synthesiser was refused with "The specified device
112
- is already in use". Endpoints carried by the new transports are shared;
113
- endpoints still on the old drivers are exclusive, as they always were. On
114
- Windows 10 there is no Windows MIDI Services at all.
115
-
116
- **The Windows MIDI Services App SDK** offers MIDI 2.0 and UMP, neither of which
117
- this library needs: `midi-communications` and MusaDSL are MIDI 1.0 throughout.
118
- It is published only as WinRT, and Microsoft's guidance is that other languages
119
- obtain a WinRT projection from their toolchain. Ruby has none.
120
-
121
- **WinRT `Windows.Devices.Midi`** is, under the new stack, another compatibility
122
- layer onto the same service. It offers what WinMM offers, in exchange for
123
- hand-written COM.
124
-
125
- ## Differences from the macOS layer
126
-
127
- The two libraries implement the same contract over platforms that do not have
128
- the same shape. Where they differ, they differ because the platforms do:
129
-
130
- * **`manufacturer` and `model` are `nil`.** WinMM reports `wMid` and `wPid`,
131
- numeric codes from a manufacturer registry that stopped being maintained in
132
- the 1990s and that class-compliant USB devices almost all answer with
133
- Microsoft's. Core MIDI reports real strings. Inventing text from the codes
134
- would describe the code table, not the hardware.
135
- * **`display_name` is the port name.** On macOS it is composed as
136
- "manufacturer model (name)", which here would be a name wrapped in the
137
- punctuation of two absent fields.
138
- * **Port ids are unique within a direction, not across both.** A WinMM port is
139
- identified by its index among inputs or among outputs, and that index is what
140
- is passed to `midiInOpen`. Core MIDI numbers endpoints of both directions from
141
- one counter. Input 0 and output 0 are both valid here, and unrelated.
142
- * **Port names are truncated to 31 characters, and do not identify a port.**
143
- That is all WinMM stores, and it drops the rest silently. Two loopback
144
- endpoints whose names differed only past character 31 came back identical in
145
- every field of `MIDIINCAPSW` — same name, same `wMid`, same `wPid`, same
146
- `wTechnology` — distinguishable only by index. Core MIDI names are neither
147
- truncated nor, in practice, ambiguous.
148
- * **Enumeration is not cached, but wrappers are reused.** The macOS layer reads
149
- the device list once and keeps it, so a device plugged in later is never seen.
150
- Here WinMM is asked on every call, and a port still present comes back as the
151
- same object it was before, so opening a port and then reading from it works
152
- whether or not the caller kept the reference.
153
- * **There is no packet-list parsing.** Core MIDI delivers a list of packets that
154
- has to be walked; WinMM delivers one complete short message at a time, with
155
- running status already resolved by the driver.
156
-
157
- ## Relationship to midi-winmm
158
-
159
- This library is **not** derived from [midi-winmm](http://github.com/arirusso/midi-winmm),
160
- which was written in 2011 for 32-bit Ruby. Windows is LLP64: on a 64-bit Ruby,
161
- `unsigned long` is 32 bits while a `HANDLE` is 64, so that library's
162
- `typedef :ulong, :HANDLE` truncates every handle it obtains. It also reads port
163
- names through the ANSI entry points, which mangle any name that is not ASCII.
164
- Both are addressed here by construction; see the notes in
165
- `lib/midi-communications-windows/api.rb`.
60
+ [rdoc](https://www.rubydoc.info/gems/midi-communications-windows)
166
61
 
167
62
  ## Author
168
63
 
@@ -134,7 +134,7 @@ WinMM there expects.</p>
134
134
  </div>
135
135
 
136
136
  <div id="footer">
137
- Generated on Mon Sep 7 00:14:18 2026 by
137
+ Generated on Mon Sep 7 01:25:12 2026 by
138
138
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
139
139
  0.9.45 (ruby-3.4.7).
140
140
  </div>
@@ -132,7 +132,7 @@ declared as <code>:uint16</code> so that reading it cannot split a character in
132
132
  </div>
133
133
 
134
134
  <div id="footer">
135
- Generated on Mon Sep 7 00:14:18 2026 by
135
+ Generated on Mon Sep 7 01:25:12 2026 by
136
136
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
137
137
  0.9.45 (ruby-3.4.7).
138
138
  </div>
@@ -130,7 +130,7 @@
130
130
  </div>
131
131
 
132
132
  <div id="footer">
133
- Generated on Mon Sep 7 00:14:18 2026 by
133
+ Generated on Mon Sep 7 01:25:12 2026 by
134
134
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
135
135
  0.9.45 (ruby-3.4.7).
136
136
  </div>
@@ -133,7 +133,7 @@ is 48 bytes on 64-bit Windows, which is what the queue functions expect.</p>
133
133
  </div>
134
134
 
135
135
  <div id="footer">
136
- Generated on Mon Sep 7 00:14:18 2026 by
136
+ Generated on Mon Sep 7 01:25:12 2026 by
137
137
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
138
138
  0.9.45 (ruby-3.4.7).
139
139
  </div>
@@ -971,7 +971,7 @@ than showing a replacement character.</p>
971
971
  </div>
972
972
 
973
973
  <div id="footer">
974
- Generated on Mon Sep 7 00:14:18 2026 by
974
+ Generated on Mon Sep 7 01:25:12 2026 by
975
975
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
976
976
  0.9.45 (ruby-3.4.7).
977
977
  </div>
@@ -355,7 +355,7 @@ back as the same object as before; see <span class='object_link'><a href="../Dev
355
355
  </div>
356
356
 
357
357
  <div id="footer">
358
- Generated on Mon Sep 7 00:14:18 2026 by
358
+ Generated on Mon Sep 7 01:25:12 2026 by
359
359
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
360
360
  0.9.45 (ruby-3.4.7).
361
361
  </div>
@@ -1086,7 +1086,7 @@ may already hold.</p>
1086
1086
  </div>
1087
1087
 
1088
1088
  <div id="footer">
1089
- Generated on Mon Sep 7 00:14:18 2026 by
1089
+ Generated on Mon Sep 7 01:25:12 2026 by
1090
1090
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
1091
1091
  0.9.45 (ruby-3.4.7).
1092
1092
  </div>
@@ -978,7 +978,7 @@ the reserved id <code>-1</code> and does not count among its devices.</p>
978
978
  </div>
979
979
 
980
980
  <div id="footer">
981
- Generated on Mon Sep 7 00:14:18 2026 by
981
+ Generated on Mon Sep 7 01:25:12 2026 by
982
982
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
983
983
  0.9.45 (ruby-3.4.7).
984
984
  </div>
@@ -428,7 +428,7 @@ for instance</p></div>
428
428
  </div>
429
429
 
430
430
  <div id="footer">
431
- Generated on Mon Sep 7 00:14:18 2026 by
431
+ Generated on Mon Sep 7 01:25:12 2026 by
432
432
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
433
433
  0.9.45 (ruby-3.4.7).
434
434
  </div>
@@ -1544,7 +1544,7 @@ may already hold.</p>
1544
1544
  </div>
1545
1545
 
1546
1546
  <div id="footer">
1547
- Generated on Mon Sep 7 00:14:18 2026 by
1547
+ Generated on Mon Sep 7 01:25:12 2026 by
1548
1548
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
1549
1549
  0.9.45 (ruby-3.4.7).
1550
1550
  </div>
@@ -690,7 +690,7 @@ byte calls for</p></div>
690
690
  </div>
691
691
 
692
692
  <div id="footer">
693
- Generated on Mon Sep 7 00:14:18 2026 by
693
+ Generated on Mon Sep 7 01:25:12 2026 by
694
694
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
695
695
  0.9.45 (ruby-3.4.7).
696
696
  </div>
@@ -1687,7 +1687,7 @@ else, and this is where the two part company.</p>
1687
1687
  </div>
1688
1688
 
1689
1689
  <div id="footer">
1690
- Generated on Mon Sep 7 00:14:18 2026 by
1690
+ Generated on Mon Sep 7 01:25:12 2026 by
1691
1691
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
1692
1692
  0.9.45 (ruby-3.4.7).
1693
1693
  </div>
@@ -359,7 +359,7 @@
359
359
  </div>
360
360
 
361
361
  <div id="footer">
362
- Generated on Mon Sep 7 00:14:18 2026 by
362
+ Generated on Mon Sep 7 01:25:12 2026 by
363
363
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
364
364
  0.9.45 (ruby-3.4.7).
365
365
  </div>
@@ -184,7 +184,7 @@ RubyGems is where installations break; there is nothing here to break.</p>
184
184
 
185
185
  </div>
186
186
  </dt>
187
- <dd><pre class="code"><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>0.0.4</span><span class='tstring_end'>&#39;</span></span><span class='period'>.</span><span class='id identifier rubyid_freeze'>freeze</span></pre></dd>
187
+ <dd><pre class="code"><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>0.1.0</span><span class='tstring_end'>&#39;</span></span><span class='period'>.</span><span class='id identifier rubyid_freeze'>freeze</span></pre></dd>
188
188
 
189
189
  </dl>
190
190
 
@@ -200,7 +200,7 @@ RubyGems is where installations break; there is nothing here to break.</p>
200
200
  </div>
201
201
 
202
202
  <div id="footer">
203
- Generated on Mon Sep 7 00:14:18 2026 by
203
+ Generated on Mon Sep 7 01:25:12 2026 by
204
204
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
205
205
  0.9.45 (ruby-3.4.7).
206
206
  </div>
data/doc/_index.html CHANGED
@@ -249,7 +249,7 @@
249
249
  </div>
250
250
 
251
251
  <div id="footer">
252
- Generated on Mon Sep 7 00:14:17 2026 by
252
+ Generated on Mon Sep 7 01:25:12 2026 by
253
253
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
254
254
  0.9.45 (ruby-3.4.7).
255
255
  </div>
data/doc/file.README.html CHANGED
@@ -103,122 +103,35 @@
103
103
  </tr>
104
104
  </tbody>
105
105
  </table>
106
+ <p>You will normally reach it through
107
+ <a href="https://github.com/javier-sy/midi-communications">MIDI Communications</a>, which
108
+ depends on it and selects it on Windows. It implements that gem's physical layer
109
+ contract; anything about how ports behave is documented there, in
110
+ <code>MIDICommunications::PhysicalLayer</code>.</p>
111
+ <h2 id="Features">Features</h2>
112
+ <ul>
113
+ <li>Simplified API</li>
114
+ <li>Input and output on multiple devices concurrently</li>
115
+ <li>Generalized handling of different MIDI Message types (including SysEx)</li>
116
+ <li>Timestamped input events</li>
117
+ <li>Patch MIDI via software to other programs using a loopback endpoint</li>
118
+ <li>No compiled artifact: <code>winmm.dll</code> is part of Windows</li>
119
+ </ul>
120
+ <p>Runnable examples of the API are in <a href="examples"><code>examples/</code></a>.</p>
106
121
  <h2 id="Status">Status</h2>
107
- <p><strong>Early.</strong> Everything is written and has been exercised end to end on Windows 11
108
- 25H2 against system loopback endpoints: enumeration, short messages in both
109
- directions, System Exclusive in both directions including a message larger than
110
- the whole buffer queue, twenty open/close cycles, and the error path. What has
111
- not been exercised is anything needing real hardware — a physical interface slow
112
- enough to make <code>Output</code>'s System Exclusive wait mean something, and a port
113
- appearing or disappearing while the program runs.</p>
114
- <p>The version stays in the 0.0.x series because that was a virtual machine with no
115
- MIDI hardware in it, not because anything is known to be missing.</p>
116
- <p>How those measurements were taken, and what a session with real hardware still
117
- has to close, is written down in
118
- <a href="dev/testing-on-windows.md"><code>dev/testing-on-windows.md</code></a>.</p>
119
- <h3 id="One_thing_worth_knowing_before_you_use_it">One thing worth knowing before you use it</h3>
120
- <p><code>Input#gets</code> <strong>waits</strong>. It does not return an empty array when nothing has
121
- arrived; it blocks until something does, and then returns everything that
122
- accumulated. Calling it on a quiet port looks exactly like a hung program. That
123
- is deliberate — <code>Musa::Clock::InputMidiClock</code> reads in a loop with no delay of
124
- its own and relies on it — but it will surprise anyone arriving from an API that
125
- polls.</p>
122
+ <p><strong>Early.</strong> Tested on Windows 11 without external MIDI devices. Everything works
123
+ against loopback ports; running it against real hardware is still pending.</p>
126
124
  <h2 id="Requirements">Requirements</h2>
127
125
  <ul>
128
126
  <li><a href="http://github.com/ffi/ffi">ffi</a></li>
129
127
  </ul>
130
- <p>Nothing else. <code>winmm.dll</code> is part of Windows, so this gem binds a library that
131
- is already on the machine and ships no compiled artifact of its own.</p>
132
- <h3 id="Which_versions_of_Windows">Which versions of Windows</h3>
133
- <p><strong>By construction, old ones.</strong> Everything this gem calls is the oldest MIDI API
134
- Windows has — <code>midiInOpen</code>, <code>midiOutShortMsg</code>, <code>midiOutLongMsg</code> and their
135
- neighbours — plus three functions for a thread's message queue, <code>GetMessageW</code>,
136
- <code>PeekMessageW</code> and <code>PostThreadMessageW</code>. All of it, the Unicode entry points
137
- included, dates from the NT era. Nothing here calls Windows MIDI Services, which
138
- means nothing here needs Windows 11.</p>
139
- <p>What changes on an older system is not whether the gem works but what the
140
- platform gives it. Before Windows MIDI Services a MIDI port is <strong>exclusive</strong> —
141
- one program at a time — and there are no system loopback endpoints, so routing
142
- MIDI between applications on one machine needs a third-party driver such as
143
- loopMIDI. The same code, a poorer platform.</p>
144
- <p>A 32-bit Ruby should also be fine: the two size checks that only hold on 64-bit
145
- Windows are guarded, and the two that are checked everywhere describe structures
146
- whose layout does not depend on the word size.</p>
147
- <p><strong>None of which has been run.</strong> The only system this has ever executed on is
148
- Windows 11 25H2, in a virtual machine, on an x64 Ruby under ARM emulation. The
149
- paragraphs above say what the code asks of Windows, not what has been observed;
150
- treat them as a reason to expect it to work rather than as a report that it does.</p>
151
- <p>Where the floor actually sits is a question about Ruby, not about this gem:
152
- RubyInstaller recommends Windows 10 or 11 and publishes no hard cutoff.</p>
153
128
  <h2 id="Installation">Installation</h2>
154
129
  <p>If you're using Bundler, add this line to your application's Gemfile:</p>
155
130
  <p><code>gem &quot;midi-communications-windows&quot;</code></p>
156
131
  <p>Otherwise</p>
157
132
  <p><code>gem install midi-communications-windows</code></p>
158
133
  <h2 id="Documentation">Documentation</h2>
159
- <p><a href="http://rubydoc.info/github/javier-sy/midi-communications-windows">rdoc</a></p>
160
- <h2 id="Why_WinMM_and_not_one_of_the_newer_Windows_MIDI_APIs">Why WinMM and not one of the newer Windows MIDI APIs</h2>
161
- <p>Windows has three MIDI APIs a program could reach for, and as of February 2026
162
- the oldest is the right one for Ruby.</p>
163
- <p><strong>Windows MIDI Services</strong> became generally available in Windows 11 in February
164
- 2026, replacing the MIDI stack underneath. Rather than retiring the older APIs,
165
- Microsoft reconnected them to the new service, so a WinMM client needs nothing
166
- installed to reach what the new stack provides — including the loopback
167
- endpoints the system now creates itself, which is what previously required a
168
- third-party driver.</p>
169
- <p>Multi-client access arrives the same way, but <strong>per endpoint, not per API</strong>.
170
- Measured on Windows 11 25H2 through this library: two clients opened the same
171
- loopback input at once and both received every message, while a second open of
172
- the classic <code>wdmaud</code> software synthesiser was refused with &quot;The specified device
173
- is already in use&quot;. Endpoints carried by the new transports are shared;
174
- endpoints still on the old drivers are exclusive, as they always were. On
175
- Windows 10 there is no Windows MIDI Services at all.</p>
176
- <p><strong>The Windows MIDI Services App SDK</strong> offers MIDI 2.0 and UMP, neither of which
177
- this library needs: <code>midi-communications</code> and MusaDSL are MIDI 1.0 throughout.
178
- It is published only as WinRT, and Microsoft's guidance is that other languages
179
- obtain a WinRT projection from their toolchain. Ruby has none.</p>
180
- <p><strong>WinRT <code>Windows.Devices.Midi</code></strong> is, under the new stack, another compatibility
181
- layer onto the same service. It offers what WinMM offers, in exchange for
182
- hand-written COM.</p>
183
- <h2 id="Differences_from_the_macOS_layer">Differences from the macOS layer</h2>
184
- <p>The two libraries implement the same contract over platforms that do not have
185
- the same shape. Where they differ, they differ because the platforms do:</p>
186
- <ul>
187
- <li><strong><code>manufacturer</code> and <code>model</code> are <code>nil</code>.</strong> WinMM reports <code>wMid</code> and <code>wPid</code>,
188
- numeric codes from a manufacturer registry that stopped being maintained in
189
- the 1990s and that class-compliant USB devices almost all answer with
190
- Microsoft's. Core MIDI reports real strings. Inventing text from the codes
191
- would describe the code table, not the hardware.</li>
192
- <li><strong><code>display_name</code> is the port name.</strong> On macOS it is composed as
193
- &quot;manufacturer model (name)&quot;, which here would be a name wrapped in the
194
- punctuation of two absent fields.</li>
195
- <li><strong>Port ids are unique within a direction, not across both.</strong> A WinMM port is
196
- identified by its index among inputs or among outputs, and that index is what
197
- is passed to <code>midiInOpen</code>. Core MIDI numbers endpoints of both directions from
198
- one counter. Input 0 and output 0 are both valid here, and unrelated.</li>
199
- <li><strong>Port names are truncated to 31 characters, and do not identify a port.</strong>
200
- That is all WinMM stores, and it drops the rest silently. Two loopback
201
- endpoints whose names differed only past character 31 came back identical in
202
- every field of <code>MIDIINCAPSW</code> — same name, same <code>wMid</code>, same <code>wPid</code>, same
203
- <code>wTechnology</code> — distinguishable only by index. Core MIDI names are neither
204
- truncated nor, in practice, ambiguous.</li>
205
- <li><strong>Enumeration is not cached, but wrappers are reused.</strong> The macOS layer reads
206
- the device list once and keeps it, so a device plugged in later is never seen.
207
- Here WinMM is asked on every call, and a port still present comes back as the
208
- same object it was before, so opening a port and then reading from it works
209
- whether or not the caller kept the reference.</li>
210
- <li><strong>There is no packet-list parsing.</strong> Core MIDI delivers a list of packets that
211
- has to be walked; WinMM delivers one complete short message at a time, with
212
- running status already resolved by the driver.</li>
213
- </ul>
214
- <h2 id="Relationship_to_midi_winmm">Relationship to midi-winmm</h2>
215
- <p>This library is <strong>not</strong> derived from <a href="http://github.com/arirusso/midi-winmm">midi-winmm</a>,
216
- which was written in 2011 for 32-bit Ruby. Windows is LLP64: on a 64-bit Ruby,
217
- <code>unsigned long</code> is 32 bits while a <code>HANDLE</code> is 64, so that library's
218
- <code>typedef :ulong, :HANDLE</code> truncates every handle it obtains. It also reads port
219
- names through the ANSI entry points, which mangle any name that is not ASCII.
220
- Both are addressed here by construction; see the notes in
221
- <code>lib/midi-communications-windows/api.rb</code>.</p>
134
+ <p><a href="https://www.rubydoc.info/gems/midi-communications-windows">rdoc</a></p>
222
135
  <h2 id="Author">Author</h2>
223
136
  <ul>
224
137
  <li><a href="https://github.com/javier-sy">Javier Sánchez Yeste</a></li>
@@ -227,7 +140,7 @@ Both are addressed here by construction; see the notes in
227
140
  <p><a href="https://github.com/javier-sy/midi-communications-windows">MIDI Communications Windows Layer</a> Copyright (c) 2026 <a href="https://yeste.studio">Javier Sánchez Yeste</a>, licensed under LGPL 3.0 License</p></div></div>
228
141
 
229
142
  <div id="footer">
230
- Generated on Mon Sep 7 00:14:18 2026 by
143
+ Generated on Mon Sep 7 01:25:12 2026 by
231
144
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
232
145
  0.9.45 (ruby-3.4.7).
233
146
  </div>
data/doc/index.html CHANGED
@@ -103,122 +103,35 @@
103
103
  </tr>
104
104
  </tbody>
105
105
  </table>
106
+ <p>You will normally reach it through
107
+ <a href="https://github.com/javier-sy/midi-communications">MIDI Communications</a>, which
108
+ depends on it and selects it on Windows. It implements that gem's physical layer
109
+ contract; anything about how ports behave is documented there, in
110
+ <code>MIDICommunications::PhysicalLayer</code>.</p>
111
+ <h2 id="Features">Features</h2>
112
+ <ul>
113
+ <li>Simplified API</li>
114
+ <li>Input and output on multiple devices concurrently</li>
115
+ <li>Generalized handling of different MIDI Message types (including SysEx)</li>
116
+ <li>Timestamped input events</li>
117
+ <li>Patch MIDI via software to other programs using a loopback endpoint</li>
118
+ <li>No compiled artifact: <code>winmm.dll</code> is part of Windows</li>
119
+ </ul>
120
+ <p>Runnable examples of the API are in <a href="examples"><code>examples/</code></a>.</p>
106
121
  <h2 id="Status">Status</h2>
107
- <p><strong>Early.</strong> Everything is written and has been exercised end to end on Windows 11
108
- 25H2 against system loopback endpoints: enumeration, short messages in both
109
- directions, System Exclusive in both directions including a message larger than
110
- the whole buffer queue, twenty open/close cycles, and the error path. What has
111
- not been exercised is anything needing real hardware — a physical interface slow
112
- enough to make <code>Output</code>'s System Exclusive wait mean something, and a port
113
- appearing or disappearing while the program runs.</p>
114
- <p>The version stays in the 0.0.x series because that was a virtual machine with no
115
- MIDI hardware in it, not because anything is known to be missing.</p>
116
- <p>How those measurements were taken, and what a session with real hardware still
117
- has to close, is written down in
118
- <a href="dev/testing-on-windows.md"><code>dev/testing-on-windows.md</code></a>.</p>
119
- <h3 id="One_thing_worth_knowing_before_you_use_it">One thing worth knowing before you use it</h3>
120
- <p><code>Input#gets</code> <strong>waits</strong>. It does not return an empty array when nothing has
121
- arrived; it blocks until something does, and then returns everything that
122
- accumulated. Calling it on a quiet port looks exactly like a hung program. That
123
- is deliberate — <code>Musa::Clock::InputMidiClock</code> reads in a loop with no delay of
124
- its own and relies on it — but it will surprise anyone arriving from an API that
125
- polls.</p>
122
+ <p><strong>Early.</strong> Tested on Windows 11 without external MIDI devices. Everything works
123
+ against loopback ports; running it against real hardware is still pending.</p>
126
124
  <h2 id="Requirements">Requirements</h2>
127
125
  <ul>
128
126
  <li><a href="http://github.com/ffi/ffi">ffi</a></li>
129
127
  </ul>
130
- <p>Nothing else. <code>winmm.dll</code> is part of Windows, so this gem binds a library that
131
- is already on the machine and ships no compiled artifact of its own.</p>
132
- <h3 id="Which_versions_of_Windows">Which versions of Windows</h3>
133
- <p><strong>By construction, old ones.</strong> Everything this gem calls is the oldest MIDI API
134
- Windows has — <code>midiInOpen</code>, <code>midiOutShortMsg</code>, <code>midiOutLongMsg</code> and their
135
- neighbours — plus three functions for a thread's message queue, <code>GetMessageW</code>,
136
- <code>PeekMessageW</code> and <code>PostThreadMessageW</code>. All of it, the Unicode entry points
137
- included, dates from the NT era. Nothing here calls Windows MIDI Services, which
138
- means nothing here needs Windows 11.</p>
139
- <p>What changes on an older system is not whether the gem works but what the
140
- platform gives it. Before Windows MIDI Services a MIDI port is <strong>exclusive</strong> —
141
- one program at a time — and there are no system loopback endpoints, so routing
142
- MIDI between applications on one machine needs a third-party driver such as
143
- loopMIDI. The same code, a poorer platform.</p>
144
- <p>A 32-bit Ruby should also be fine: the two size checks that only hold on 64-bit
145
- Windows are guarded, and the two that are checked everywhere describe structures
146
- whose layout does not depend on the word size.</p>
147
- <p><strong>None of which has been run.</strong> The only system this has ever executed on is
148
- Windows 11 25H2, in a virtual machine, on an x64 Ruby under ARM emulation. The
149
- paragraphs above say what the code asks of Windows, not what has been observed;
150
- treat them as a reason to expect it to work rather than as a report that it does.</p>
151
- <p>Where the floor actually sits is a question about Ruby, not about this gem:
152
- RubyInstaller recommends Windows 10 or 11 and publishes no hard cutoff.</p>
153
128
  <h2 id="Installation">Installation</h2>
154
129
  <p>If you're using Bundler, add this line to your application's Gemfile:</p>
155
130
  <p><code>gem &quot;midi-communications-windows&quot;</code></p>
156
131
  <p>Otherwise</p>
157
132
  <p><code>gem install midi-communications-windows</code></p>
158
133
  <h2 id="Documentation">Documentation</h2>
159
- <p><a href="http://rubydoc.info/github/javier-sy/midi-communications-windows">rdoc</a></p>
160
- <h2 id="Why_WinMM_and_not_one_of_the_newer_Windows_MIDI_APIs">Why WinMM and not one of the newer Windows MIDI APIs</h2>
161
- <p>Windows has three MIDI APIs a program could reach for, and as of February 2026
162
- the oldest is the right one for Ruby.</p>
163
- <p><strong>Windows MIDI Services</strong> became generally available in Windows 11 in February
164
- 2026, replacing the MIDI stack underneath. Rather than retiring the older APIs,
165
- Microsoft reconnected them to the new service, so a WinMM client needs nothing
166
- installed to reach what the new stack provides — including the loopback
167
- endpoints the system now creates itself, which is what previously required a
168
- third-party driver.</p>
169
- <p>Multi-client access arrives the same way, but <strong>per endpoint, not per API</strong>.
170
- Measured on Windows 11 25H2 through this library: two clients opened the same
171
- loopback input at once and both received every message, while a second open of
172
- the classic <code>wdmaud</code> software synthesiser was refused with &quot;The specified device
173
- is already in use&quot;. Endpoints carried by the new transports are shared;
174
- endpoints still on the old drivers are exclusive, as they always were. On
175
- Windows 10 there is no Windows MIDI Services at all.</p>
176
- <p><strong>The Windows MIDI Services App SDK</strong> offers MIDI 2.0 and UMP, neither of which
177
- this library needs: <code>midi-communications</code> and MusaDSL are MIDI 1.0 throughout.
178
- It is published only as WinRT, and Microsoft's guidance is that other languages
179
- obtain a WinRT projection from their toolchain. Ruby has none.</p>
180
- <p><strong>WinRT <code>Windows.Devices.Midi</code></strong> is, under the new stack, another compatibility
181
- layer onto the same service. It offers what WinMM offers, in exchange for
182
- hand-written COM.</p>
183
- <h2 id="Differences_from_the_macOS_layer">Differences from the macOS layer</h2>
184
- <p>The two libraries implement the same contract over platforms that do not have
185
- the same shape. Where they differ, they differ because the platforms do:</p>
186
- <ul>
187
- <li><strong><code>manufacturer</code> and <code>model</code> are <code>nil</code>.</strong> WinMM reports <code>wMid</code> and <code>wPid</code>,
188
- numeric codes from a manufacturer registry that stopped being maintained in
189
- the 1990s and that class-compliant USB devices almost all answer with
190
- Microsoft's. Core MIDI reports real strings. Inventing text from the codes
191
- would describe the code table, not the hardware.</li>
192
- <li><strong><code>display_name</code> is the port name.</strong> On macOS it is composed as
193
- &quot;manufacturer model (name)&quot;, which here would be a name wrapped in the
194
- punctuation of two absent fields.</li>
195
- <li><strong>Port ids are unique within a direction, not across both.</strong> A WinMM port is
196
- identified by its index among inputs or among outputs, and that index is what
197
- is passed to <code>midiInOpen</code>. Core MIDI numbers endpoints of both directions from
198
- one counter. Input 0 and output 0 are both valid here, and unrelated.</li>
199
- <li><strong>Port names are truncated to 31 characters, and do not identify a port.</strong>
200
- That is all WinMM stores, and it drops the rest silently. Two loopback
201
- endpoints whose names differed only past character 31 came back identical in
202
- every field of <code>MIDIINCAPSW</code> — same name, same <code>wMid</code>, same <code>wPid</code>, same
203
- <code>wTechnology</code> — distinguishable only by index. Core MIDI names are neither
204
- truncated nor, in practice, ambiguous.</li>
205
- <li><strong>Enumeration is not cached, but wrappers are reused.</strong> The macOS layer reads
206
- the device list once and keeps it, so a device plugged in later is never seen.
207
- Here WinMM is asked on every call, and a port still present comes back as the
208
- same object it was before, so opening a port and then reading from it works
209
- whether or not the caller kept the reference.</li>
210
- <li><strong>There is no packet-list parsing.</strong> Core MIDI delivers a list of packets that
211
- has to be walked; WinMM delivers one complete short message at a time, with
212
- running status already resolved by the driver.</li>
213
- </ul>
214
- <h2 id="Relationship_to_midi_winmm">Relationship to midi-winmm</h2>
215
- <p>This library is <strong>not</strong> derived from <a href="http://github.com/arirusso/midi-winmm">midi-winmm</a>,
216
- which was written in 2011 for 32-bit Ruby. Windows is LLP64: on a 64-bit Ruby,
217
- <code>unsigned long</code> is 32 bits while a <code>HANDLE</code> is 64, so that library's
218
- <code>typedef :ulong, :HANDLE</code> truncates every handle it obtains. It also reads port
219
- names through the ANSI entry points, which mangle any name that is not ASCII.
220
- Both are addressed here by construction; see the notes in
221
- <code>lib/midi-communications-windows/api.rb</code>.</p>
134
+ <p><a href="https://www.rubydoc.info/gems/midi-communications-windows">rdoc</a></p>
222
135
  <h2 id="Author">Author</h2>
223
136
  <ul>
224
137
  <li><a href="https://github.com/javier-sy">Javier Sánchez Yeste</a></li>
@@ -227,7 +140,7 @@ Both are addressed here by construction; see the notes in
227
140
  <p><a href="https://github.com/javier-sy/midi-communications-windows">MIDI Communications Windows Layer</a> Copyright (c) 2026 <a href="https://yeste.studio">Javier Sánchez Yeste</a>, licensed under LGPL 3.0 License</p></div></div>
228
141
 
229
142
  <div id="footer">
230
- Generated on Mon Sep 7 00:14:18 2026 by
143
+ Generated on Mon Sep 7 01:25:12 2026 by
231
144
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
232
145
  0.9.45 (ruby-3.4.7).
233
146
  </div>
@@ -102,7 +102,7 @@
102
102
  </div>
103
103
 
104
104
  <div id="footer">
105
- Generated on Mon Sep 7 00:14:18 2026 by
105
+ Generated on Mon Sep 7 01:25:12 2026 by
106
106
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
107
107
  0.9.45 (ruby-3.4.7).
108
108
  </div>
@@ -1,4 +1,4 @@
1
1
  module MIDICommunicationsWindows
2
2
  # Current version of the midi-communications-windows gem.
3
- VERSION = '0.0.4'.freeze
3
+ VERSION = '0.1.0'.freeze
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: midi-communications-windows
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javier Sánchez Yeste