Skip to content

chore(juce): bump to JUCE 9.0.5 - #21

Merged
pob31 merged 3 commits into
mainfrom
juce-9.0.5
Nov 2, 2026
Merged

pob31 merged 3 commits into
mainfrom
juce-9.0.5

Conversation

@pob31

@pob31 pob31 commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

JUCE 9.0.4 → 9.0.5, aimed at beta65. Draft until the Mac and Ubuntu NUC checks pass.

What changes

  • Projucer resave: JUCE_PROJUCER_VERSION 0x90005, HarfBuzz 15 file lists, macOS deployment target 10.13 → 12.0 (the new Projucer default; Xcode 27 refuses anything lower). The DAW plugins stay at 10.13.
  • Message boxes: every withButton now has an explicit result code (the codes the buttons had before, so no handler changes meaning). Cancel buttons get MessageBoxButtonRole::cancel. The deprecated showOkCancelBox calls move to showAsync. WfsLookAndFeel overrides createAlertWindowForOptions.
  • spatcore pinned to main after PR #33.
  • Docs: README / beta checklist say macOS 12+; the changelog's Unreleased section is filled in.

Verified on the Windows dev box
Debug + Release + WFSPluginsAll build clean. offline-render cpu/effects/gpu: 0 mismatches. spatcore-tests pass. Control-replay drivers pass (session_roundtrip, osc_replay, mcp_replay, oscquery_echo, fade_ramp, remote_tablet_mock, tracking, network_threads). Channel-list self-test: 973 PASS.

midi_snapshot_check failed every note-recall check, and the 9.0.4 build failed it the same way: WinMM listed no MIDI inputs at all, even with the test's teVirtualMIDI port open.

Corrected 2026-11-04: this line first said Windows MIDI Services hides virtual MIDI ports from WinMM. That was wrong. After a reboot, the same Windows build lists teVirtualMIDI ports in WinMM about 0.3 s after they are created, through the service's KS aggregate transport. The MIDI service had most likely stopped picking up new devices: a USB Behringer X-Touch didn't appear either until the reboot. So the failure came from the box's MIDI service, not from JUCE 9.0.5.

Still to check

  • macOS (POB12, Mac mini M4 Pro, macOS 15.8.2, Xcode 26.3): patch applied, Debug build at minos 12.0, touch on the ILITEK panel works; offline-render cpu 20/20 + Metal gpu 20/20 match mac-m4pro baselines, effects 16/16 identical to a 9.0.4 build
  • CI: all 14 checks green (app on 3 OSes, spatcore tests, plugins on 3 OSes, CodeQL)
  • macOS: Escape on a confirm dialog
  • Ubuntu NUC (POB11, Ubuntu 24.04, GNOME 46 Wayland/XWayland, GCC 13.3): Debug Makefile build clean, multitouch on the ILITEK panel works; offline-render cpu 20/20 + effects 16/16 identical to a 9.0.4 build (cpu also matches linux-gtx1650). GNOME's own touch gestures interfered; fixed separately in feat(linux): GNOME touch-gesture extension, offered by the installer #22
  • WinMM MIDI with a real device
  • Escape / close on a three-button dialog (channel identity gate)

🤖 Generated with Claude Code

pob31 and others added 2 commits November 2, 2026 15:16
…12 minimum

JUCE 9.0.5 hardens ZIP/XML/JSON/ValueTree/OSC parsing and the PNG, GIF,
WAV and AIFF readers, and fixes Direct2D presentation and WinMM MIDI issues.

- Projucer 9.0.5 resave: version stamp, HarfBuzz 15 sources (stale
  duplicates dropped), default macOS deployment target 10.13 -> 12.0
  (Xcode 27 refuses lower; the plugins keep their explicit 10.13).
- MessageBoxOptions::withButton() now takes a result code: every button keeps
  the code it had under the old (index + 1) % N rule, so no handler changes
  meaning, and Cancel buttons get MessageBoxButtonRole::cancel.
  ChannelIdentityGate uses the button index as the code and drops toIndex().
- WfsLookAndFeel overrides createAlertWindowForOptions (createAlertWindow is
  gone), keeping Screen Rendering on alerts.
- The four deprecated AlertWindow::showOkCancelBox calls move to showAsync.

Verified on the Windows dev box: Debug + Release, WFSPluginsAll,
offline-render cpu/effects/gpu bit-exact vs win-dev-nvidia baselines (only the
known */stereo MISSING), spatcore-tests, spatcore minimal-app, and 8 drivers +
WFS_TEST_CHANNEL_LIST self-test (973 PASS) on Release. midi_snapshot_check
cannot run here: since the 2026-10-28 Windows updates WinMM sees no MIDI
inputs (Windows MIDI Services), and the 9.0.4 build fails it identically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gicalBounds)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…PR #34)

Changelog: the Unreleased sections combined; one spatcore pin line naming
PR #33 and PR #34 (main after PR #34 contains PR #33).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pob31
pob31 marked this pull request as ready for review November 2, 2026 21:16
@pob31
pob31 merged commit a11c8c6 into main Nov 2, 2026
11 checks passed
@Psychlist1972

Copy link
Copy Markdown

@pob31

midi_snapshot_check was not run: Windows MIDI Services hides virtual MIDI ports from WinMM on this box. The 9.0.4 build fails the same way.

Can you elaborate? Is this a bug you've run across? Or is it that the Virtual Device transport is disabled on the box due to the known issue where it hangs the service when the endpoint is closed (will be fixed in retail later this month).

Pete
Microsoft

@pob31

pob31 commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner Author

Thanks Pete, and sorry, that line was my misreading. I've corrected it in the PR. Windows MIDI Services doesn't hide virtual ports from WinMM.

What happened on 2 November: midiInGetNumDevs() returned 0 with a teVirtualMIDI port open. The next day a USB Behringer X-Touch didn't appear either, until a reboot. After the reboot, teVirtualMIDI ports show up in WinMM 0.2 to 0.6 s after they're created, through Midi2KSAggregateTransport. Our MIDI snapshot test passes 4 times in a row. So MidiSrv had most likely stopped picking up new endpoints. I haven't been able to reproduce that full state since.

We don't use the Virtual Device transport: our test uses teVirtualMIDI 1.3.0.43 (loopMIDI's driver). All four transport plugins are Enabled = 1.

One thing that does reproduce: about 1 in 6 closes of a teVirtualMIDI port (3 of 18 in one run) leave a stale endpoint, whatever the delay before the next port opens.

WinMM keeps listing the closed port's name.
MidiSrv keeps SWD\MIDISRV\MIDIU_KSA_2236756859055812926 present.
A client that opens the stale entry gets MMSYSERR_NOERROR.
On the driver's side, all of teVirtualMIDI's KS interfaces are disabled at that point (ROOT#MEDIA#0001#{…}\tevmidi0 and \tevmidi1).
The stale entry stays until the next teVirtualMIDI port comes and goes; once that was more than 4 minutes.
teVirtualMIDI reuses the same interface paths (tevmidi0, tevmidi1) for every port, so MidiSrv may be missing a disable that comes soon after an enable of the same path.
Is this related to the endpoint-close hang you mentioned, or worth a separate issue? If someone repeatedly opens and closes loopMIDI ports, could stale entries like this build up into the state we saw on 2 November?

Environment: Windows 11 26H2, build 26300.9818 (KB5127936 and KB5132402 installed 28 October). MidiSrv is manual start. There's nothing about it in the System or Application event logs. I can share the repro scripts, or capture a trace if you tell me which provider.

@Psychlist1972

Psychlist1972 commented Nov 4, 2026 •

Copy link
Copy Markdown

Thanks @pob31

One thing that does reproduce: about 1 in 6 closes of a teVirtualMIDI port (3 of 18 in one run) leave a stale endpoint, whatever the delay before the next port opens.

Can you share your repro code for that?

Or, if you prefer to capture a trace, grab the latest tools from https://aka.ms/midireleases (Preview 13) and use the MIDI Troubleshooting and Repair app to capture a full repro trace with all options selected.

On the driver's side, all of teVirtualMIDI's KS interfaces are disabled at that point (ROOT#MEDIA#0001#{…}\tevmidi0 and \tevmidi1).

So if you run midiksinfo from the tools download, those interfaces do not show up for teVirtualMIDI as enabled interfaces, but they do show up as active MIDI Endpoints, if I understand you correctly.

Thanks.

Pete
Microsoft

@pob31

pob31 commented Nov 4, 2026

Copy link
Copy Markdown
Owner Author

Thanks Pete. Here's the repro (below). It needs only Python 3 64-bit and teVirtualMIDI64.dll (installed by loopMIDI). On the latest run here it left 6 stale endpoints out of 24 closes; earlier runs gave 1 in 18 and 3 in 18.

Your understanding is right, with one caveat: I haven't installed the Preview 13 tools yet, so this comes from pnputil /enum-interfaces rather than midiksinfo. While an endpoint is stale:

all six of teVirtualMIDI's KS interfaces are Disabled (ROOT#MEDIA#0001#{65e8773e…}, {65e8773d…} and {6994ad04…}, each \tevmidi0 and \tevmidi1);
MidiSrv still has SWD\MIDISRV\MIDIU_KSA_2236756859055812926 present;
WinMM still lists the closed port's name, and midiInOpen on it succeeds.
The stale entry goes away when the next teVirtualMIDI port is created and closed (once it lasted more than 4 minutes). The delay before the next port opens makes no difference. Other devices aren't affected: an RME Babyface and the Dante MIDI port of an RME Digiface stayed fine throughout.

I'll install Preview 13 and capture a trace with the MIDI Troubleshooting and Repair app (all options), plus the midiksinfo output, and post them here.

tevm_stale_repro.py """Stale MidiSrv endpoint after closing a teVirtualMIDI port.

Needs Python 3 (64-bit) and teVirtualMIDI64.dll, which loopMIDI installs.
Each cycle creates a teVirtualMIDI port, waits until WinMM lists it, closes it,
and waits up to 8 s for WinMM to drop it. When it does not, the script prints
the teVirtualMIDI KS interface states (pnputil) and the MidiSrv KSA endpoints.

usage: python tevm_stale_repro.py [cycles] (default 18)
"""
import ctypes
import ctypes.wintypes as wt
import subprocess
import sys
import time

tevm = ctypes.WinDLL("teVirtualMIDI64.dll")
winmm = ctypes.windll.winmm

RX_CB = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_ubyte), wt.DWORD, ctypes.c_void_p)
tevm.virtualMIDICreatePortEx2.restype = ctypes.c_void_p
tevm.virtualMIDICreatePortEx2.argtypes = [wt.LPCWSTR, RX_CB, ctypes.c_void_p, wt.DWORD, wt.DWORD]
tevm.virtualMIDIClosePort.argtypes = [ctypes.c_void_p]
TE_VM_FLAGS_PARSE_RX = 1
TE_VM_FLAGS_INSTANTIATE_BOTH = 12 # RX + TX
ignore_rx = RX_CB(lambda *a: None)

class MIDIINCAPSW(ctypes.Structure):
fields = [("wMid", wt.WORD), ("wPid", wt.WORD), ("vDriverVersion", wt.UINT),
("szPname", wt.WCHAR * 32), ("dwSupport", wt.DWORD)]

def winmm_inputs():
names = []
for i in range(winmm.midiInGetNumDevs()):
caps = MIDIINCAPSW()
winmm.midiInGetDevCapsW(i, ctypes.byref(caps), ctypes.sizeof(caps))
names.append(caps.szPname)
return names

def wait_for(pred, timeout):
t0 = time.perf_counter()
while time.perf_counter() - t0 < timeout:
if pred():
return time.perf_counter() - t0
time.sleep(0.02)
return None

def dump_state():
ps = r"""
$tevm = (Get-PnpDevice -PresentOnly | Where-Object { $.FriendlyName -like 'teVirtualMIDI*' -and $.InstanceId -notlike 'SWD*' }).InstanceId
'teVirtualMIDI device: ' + $tevm
(pnputil /enum-interfaces | Out-String) -split '(\r?\n){2,}' |
Where-Object { $_ -match [regex]::Escape($tevm) } |
ForEach-Object { ($_ -split "`n" | Where-Object { $_ -match 'Interface Path|Interface Status' }) -join ' ' }
'MidiSrv KSA endpoints:'
Get-PnpDevice -PresentOnly | Where-Object InstanceId -like 'SWD\MIDISRV\MIDIU_KSA*' |
ForEach-Object { ' ' + $.FriendlyName + ' ' + $.InstanceId }
"""
out = subprocess.run(["powershell", "-NoProfile", "-Command", ps], capture_output=True, text=True).stdout
print(" " + "\n ".join(l.strip() for l in out.splitlines() if l.strip()), flush=True)

cycles = int(sys.argv[1]) if len(sys.argv) > 1 else 18
print(f"WinMM inputs at start: {winmm_inputs()}", flush=True)
stale = 0
for n in range(1, cycles + 1):
name = f"StaleRepro-{n}"
port = tevm.virtualMIDICreatePortEx2(name, ignore_rx, None, 65535,
TE_VM_FLAGS_PARSE_RX | TE_VM_FLAGS_INSTANTIATE_BOTH)
if not port:
sys.exit(f"virtualMIDICreatePortEx2 failed, GetLastError={ctypes.GetLastError()}")
up = wait_for(lambda: name in winmm_inputs(), 5.0)
tevm.virtualMIDIClosePort(port)
gone = wait_for(lambda: name not in winmm_inputs(), 8.0)
if gone is None:
stale += 1
print(f"#{n}: listed after {up1000:.0f} ms; STILL LISTED 8 s after close. "
f"WinMM inputs: {winmm_inputs()}", flush=True)
dump_state()
else:
print(f"#{n}: listed after {up
1000:.0f} ms, gone {gone*1000:.0f} ms after close", flush=True)
time.sleep(0.5)
print(f"stale: {stale}/{cycles}; WinMM inputs at end: {winmm_inputs()}")

@pob31

pob31 commented Nov 4, 2026

Copy link
Copy Markdown
Owner Author

Captured with Preview 13 (Tools 0.99.96-preview.13), MIDI Troubleshooting and Repair with all options: MIDI Capture_20261104-215352.zip (78 MB, too big to attach here). Tell me where you'd like it.

What's in it. The repro script ran from 21:52:09 to 21:53:35 local (UTC+1). The trace ran 21:51:50–21:54:41; it's a 2 GB ring trace, so the kept part should be the end.

StaleRepro-1 was held open about a minute while I ran midiksinfo and midi enumerate, then closed cleanly.
StaleRepro-2 was listed by WinMM after 992 ms (about 350 ms when not tracing), closed right away at about 21:53:17, and stayed stale. It was still stale when the capture stopped. The capture's mididiag (21:54:48) still shows the KSA endpoint with StaleRepro-2 as group terminal blocks, MIDI 1.0 ports and WinMM ports, while its midiksinfo (21:54:50) lists no teVirtualMIDI filter.
midiksinfo: yes, your understanding is right. While a teVirtualMIDI port is open, midiksinfo lists the ROOT#MEDIA#0001#{6994ad04…}\tevmidi0 filter under the port's name. Once the endpoint is stale, it no longer lists teVirtualMIDI at all, and pnputil shows tevmidi0 and tevmidi1 both Disabled. Meanwhile midi enumerate ep still shows "teVirtualMIDI - Virtual MIDI Driver x64" on KSA (SWD\MIDISRV\MIDIU_KSA_2236756859055812926), and midi enumerate legacy and WinMM still list the closed port in both directions.

It looks timing-dependent. In alternating cycles:

closing the port as soon as WinMM lists it: stale in 4 of 12;
holding it open 2.5 s before closing: stale in 0 of 12, and 0 of 12 again in a separate run.
So it seems to be a close that arrives while the KSA endpoint is still being set up.

Two smaller observations:

Once, with a port open, midi enumerate legacy listed it twice in each direction (WinMM ports 1+2 in, 2+3 out) against a single KS filter. It didn't happen again in 12 tries.
This PC has two teVirtualMIDI installs, both 1.3.0.43: loopMIDI's teVirtualMIDI64 and "teVirtualMIDI for Allen-Heath MIDI gateway application". There are also loopMIDIBlockLegacy and rtpMIDIBlockLegacy entries at 9.9.9.9.

@Psychlist1972

Psychlist1972 commented Nov 4, 2026 •

Copy link
Copy Markdown

Thanks. Copilot was able to reproduce that here based on your python scripts.

It was fixed in August under Feature_Servicing_MIDI2KSAWatcherHardening. I believe that starts rolling out under CFR at the end of this month for anyone who is up to date with Windows Updates and on Windows 11 25h2 or higher. It takes 30-45 days to enable for everyone who is up to date. Blocked, uninstalled, or paused updates will prevent the fix from being enabled properly.

Until then, keep each port open for about a second after it appears before closing it.

Pete
Microsoft

@pob31

pob31 commented Nov 4, 2026

Copy link
Copy Markdown
Owner Author

Great, thanks for chasing it down so fast, Pete. Good to know it's already fixed and rolling out. Until then we'll keep virtual test ports open a moment before closing them. Thanks again for the help with this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants