midi-communications-windows 0.0.3 → 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.
@@ -1,222 +0,0 @@
1
- # Testing this gem on Windows without MIDI hardware
2
-
3
- *Written 2026-09-06. Section 1 is the part with a shelf life: it describes
4
- software that was in preview at the time, which Microsoft's release notes said
5
- was headed for Windows 11 25H2 and later during the last week of November 2026.
6
- If that has happened, most of section 1 collapses into "nothing to install" and
7
- the rest still applies.*
8
-
9
- This is **the procedure the library was measured with**, not a promise that it
10
- works on another machine. All of it ran once, on one particular installation.
11
- Where something was checked, it says so; where it was not, it says that too.
12
-
13
- The machine was:
14
-
15
- | | |
16
- |---|---|
17
- | Windows | 11 25H2, build **26200.9278** |
18
- | CPU | ARM64 — a VMware VM on Apple silicon |
19
- | Ruby | 3.4.10 `x64-mingw-ucrt`, **emulated** under Prism (no ARM64 Ruby present) |
20
- | ffi | 1.17.4 `x64-mingw-ucrt`, precompiled binary |
21
- | MIDI hardware | **none** — no USB device passed through to the guest |
22
-
23
- That last row is why this document exists: with no hardware, a loopback pair is
24
- the only way to exercise input and output at the same time.
25
-
26
- Nothing measured on this setup says anything about **timing**. Emulation inside
27
- a virtual machine; latency and jitter need real hardware and a native Ruby.
28
-
29
- ---
30
-
31
- ## 1. What has to be installed
32
-
33
- Windows 11 25H2 ships **Windows MIDI Services** in the box: the `midisrv`
34
- service (`C:\Windows\System32\midisrv.exe`) and the `wdmaud2.drv` shim,
35
- registered as `midi1` under
36
- `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32`. That shim is what
37
- makes endpoints from the new stack **visible to WinMM** without this gem doing
38
- anything special, and therefore what makes this setup able to test a gem that
39
- speaks only WinMM.
40
-
41
- What is *not* in the box are the MIDI 1.0 loopback transport and the tools. Those
42
- come from the [`microsoft/MIDI`] repository, release `inbox-dev-preview-6`:
43
-
44
- - `Windows.MIDI.Services.Basic.MIDI.1.0.Loopback.Preview.1.0.18-preview.3-arm64.exe`
45
- - `Windows.MIDI.Services.Tools.0.99.64-devpreview.6-arm64.exe`
46
-
47
- There are `-x64` variants of both. The measuring machine used the **native ARM64**
48
- ones (PE header `0xAA64`) even though the Ruby was emulated x64: the gem talks to
49
- `winmm.dll`, and Windows handles the crossing.
50
-
51
- The Network and Bluetooth transports from the same release are not needed.
52
-
53
- [`microsoft/MIDI`]: https://github.com/microsoft/MIDI/releases
54
-
55
- ### Warnings that are not optional
56
-
57
- - **These are unsigned preview binaries.** The release notes require Windows
58
- **Developer Mode**, and are explicit: *"There are almost certainly bugs and
59
- missing/incomplete features."* This is not production software.
60
- - **Do not redistribute them.** The release's permitted-use table forbids it. A
61
- repository should link to the release and never host the installers.
62
- - Minimum system version declared: **Windows 11 25H2**.
63
- - Downloaded through a browser they carry Mark of the Web and SmartScreen will
64
- object; `Unblock-File` clears it. Fetched with `Invoke-WebRequest` they do not.
65
-
66
- ## 2. Installing
67
-
68
- Both installers are **WiX bundles**, so they accept `/quiet`, `/norestart` and
69
- `/log`. Both need **elevation**, and Developer Mode writes to `HKLM`, so it is
70
- worth doing the whole thing in one elevated script and getting one UAC prompt.
71
-
72
- The release notes fix the order: **loopback transport first, tools second.**
73
-
74
- ```powershell
75
- # --- 1. Developer Mode (the key may not exist)
76
- $k = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock'
77
- if (-not (Test-Path $k)) { New-Item $k -Force | Out-Null }
78
- Set-ItemProperty $k -Name AllowDevelopmentWithoutDevLicense -Value 1 -Type DWord
79
- Set-ItemProperty $k -Name AllowAllTrustedApps -Value 1 -Type DWord
80
-
81
- # --- 2. installers, in this order
82
- Start-Process -Wait -FilePath '<...>Basic.MIDI.1.0.Loopback.Preview...-arm64.exe' `
83
- -ArgumentList '/quiet','/norestart','/log','loopback.log'
84
- Start-Process -Wait -FilePath '<...>Tools.0.99.64-devpreview.6-arm64.exe' `
85
- -ArgumentList '/quiet','/norestart','/log','tools.log'
86
- ```
87
-
88
- **Both return exit `3010`**, which is `ERROR_SUCCESS_REBOOT_REQUIRED`: installed
89
- correctly, reboot pending. The measuring machine **was not rebooted**, and every
90
- measurement was taken that way. If something behaves oddly, that reboot is the
91
- first suspect.
92
-
93
- To check the transport registered:
94
-
95
- ```powershell
96
- Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Windows MIDI Services\Transport Plugins'
97
- # Midi2BasicLoopbackMidiTransport should appear, with Enabled = 1
98
- ```
99
-
100
- ### `midi.exe` and the PATH
101
-
102
- The console lands at:
103
-
104
- ```
105
- C:\Program Files\Windows MIDI Services\Tools\Console\midi.exe
106
- ```
107
-
108
- The installer **does** add it to the machine PATH, but **a shell opened before
109
- installing still has the old environment** and will say `midi` does not exist. In
110
- a fresh shell, `midi` is enough; in the installing shell, use the full path. This
111
- detail cost time and produced one wrong report before it was noticed.
112
-
113
- ## 3. Creating the loopback pair
114
-
115
- ```powershell
116
- $midi = 'C:\Program Files\Windows MIDI Services\Tools\Console\midi.exe'
117
-
118
- & $midi basic-loopback create --name "Reloj Bitwig ñ prueba de longitud"
119
- & $midi basic-loopback list
120
- ```
121
-
122
- Options for `create`:
123
-
124
- | option | what it does |
125
- |---|---|
126
- | `-n`, `--name` | the endpoint's name |
127
- | `-u`, `--unique-identifier` | an identifier of your own |
128
- | `-s`, `--save-to-config` | **persists it in the system configuration** |
129
-
130
- **Without `--save-to-config` a loopback is temporary**: it disappears when
131
- `midisrv` or the machine restarts, and nothing is written to disk. Every
132
- measurement was taken that way, deliberately. `--save-to-config` does modify the
133
- user's system configuration — ask before using it.
134
-
135
- A `create` returns an **Association Id**, a GUID. Keep it: it is the only thing
136
- that will remove the loopback again.
137
-
138
- ### Checking through WinMM, which is what the gem sees
139
-
140
- ```
141
- ruby -Ilib examples/list_ports.rb
142
- ```
143
-
144
- One loopback shows up as **one input and one output with the same name, byte for
145
- byte**. With two loopbacks created, enumeration gives 2 inputs and 3 outputs —
146
- the third being the *Microsoft GS Wavetable Synth*, which is always there.
147
-
148
- ## 4. The name-collision recipe
149
-
150
- WinMM stores **31 characters** of a name (`MAXPNAMELEN` is 32, less the NUL) and
151
- drops the rest **silently**. To reproduce the case where two distinct ports are
152
- indistinguishable, create two loopbacks whose **first 31 characters match**:
153
-
154
- ```powershell
155
- & $midi basic-loopback create --name "Reloj Bitwig ñ prueba de longitud"
156
- & $midi basic-loopback create --name "Reloj Bitwig ñ prueba de longitud DOS"
157
- ```
158
-
159
- The first is 33 characters, the second 37; both truncate to
160
- `Reloj Bitwig ñ prueba de longit`. In `MIDIINCAPSW`/`MIDIOUTCAPSW` they come back
161
- **identical field by field**: same name, same `wMid` (1), same `wPid` (25 for
162
- inputs, 26 for outputs), same `vDriverVersion`, same `wTechnology`. Only the
163
- index separates them.
164
-
165
- If you change the names, keep the character counts working — the recipe depends
166
- on one being longer than 31 and both agreeing up to that point.
167
-
168
- The truncation happens **upstream of WinMM**: `midi enumerate
169
- midi-services-endpoints` shows the service already holding the 31-character name.
170
- Whether the console or the service truncates was not determined; for the gem it
171
- makes no difference.
172
-
173
- The `ñ` survives intact (`U+00F1` in the UTF-16LE dump), which is what validates
174
- the **W** entry points against a genuinely non-ASCII character.
175
-
176
- ## 5. Removing
177
-
178
- ```powershell
179
- & $midi basic-loopback remove --association-id "{bfba125e-3aca-446c-9b94-692a70153034}"
180
- ```
181
-
182
- **The braces around the GUID are required.** `list` prints the id without them;
183
- they have to be added.
184
-
185
- Afterwards, check the machine is back where it started — with `list`, and better
186
- still by enumerating through WinMM: with no loopbacks there should be **0 inputs
187
- and 1 output**.
188
-
189
- ## 6. What this setup can test, and what it cannot
190
-
191
- Exercised with it, and working: enumeration with colliding names, short messages
192
- in all three lengths (3, 2 and 1 bytes), accumulation in `gets`, System Exclusive
193
- both ways at 200, 3000 and 5000 bytes, buffer recycling, closing and reopening,
194
- and the propagation of an error from opening a port.
195
-
196
- **Not testable with this setup**, and therefore still untested:
197
-
198
- - **`SYSEX_TIMEOUT` / `Output#wait_until_sent`.** The only real output device is
199
- the *GS Wavetable Synth*, a software synthesiser that marks `MHDR_DONE` in
200
- under a millisecond because there is no wire. This needs a physical interface.
201
- - **`RESET_TIMEOUT` / `Input#await_returned_buffers`.** It never came close:
202
- closes took between 4 and 21 ms against a one-second bound. The
203
- device-went-away path needs something to unplug.
204
- - **Renumbering in `Device.enumerate`.** Needs a port to actually appear or go.
205
- - **`MM_MIM_LONGERROR`.** None could be provoked, not even with a System
206
- Exclusive message larger than the entire buffer queue. The code handling it is
207
- unexercised — its presence has been shown harmless, which is not the same
208
- thing.
209
- - **That an input and an output of the same device share a name.** Established
210
- for a loopback, where they are literally the same device. Not for a real USB
211
- interface.
212
- - **Any measurement of time.**
213
-
214
- Those are the items a session with real hardware should close.
215
-
216
- ## 7. One finding from here worth not forgetting
217
-
218
- Multi-client access **follows the endpoint, not the API**. Two WinMM clients on
219
- the same BLOOP input both work and **both receive every message**. The *GS
220
- Wavetable Synth*, which belongs to the classic `wdmaud` stack, refuses the second
221
- open with *"The specified device is already in use."* So on one machine, through
222
- one API, shareable ports and exclusive ports coexist.