Skip to content

(DICE-3) Read Channel Names From the ROM#48

Merged
mrmidi merged 7 commits into
mrmidi:mainfrom
alicankaralar:feat/dice-channel-names
Jul 10, 2026
Merged

(DICE-3) Read Channel Names From the ROM#48
mrmidi merged 7 commits into
mrmidi:mainfrom
alicankaralar:feat/dice-channel-names

Conversation

@alicankaralar

@alicankaralar alicankaralar commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Populates the names of the channels for DICE devices, via the values retrieved from the ROM. In DICE devices, the channel names exist in some of the registers of the ROM. We're using those values where available. Tested on Venice F32.

It sits on DICE Venice Dual Stream PR and DICE Aggregate device fix which has to be merged before this branch.

@alicankaralar alicankaralar force-pushed the feat/dice-channel-names branch from beb0c92 to 7bc4870 Compare July 7, 2026 11:46
@alicankaralar alicankaralar changed the title (DICE) Read Channel Names From the ROM (DICE-3) Read Channel Names From the ROM Jul 7, 2026
alicankaralar and others added 7 commits July 9, 2026 20:49
…eardown

IsochReceiveContext was an OSObject created via plain `new` (MakeOSObject)
instead of OSTypeAlloc. In DriverKit, OSMetaClassBase has no constructor
setting refcount — only OSObjectAllocate() (the OSTypeAlloc path) does — so
the object was born with refcount 0. The single release() in ~IsochService
then underflowed and hit the DriverKit over-release assert, SIGABRT-ing the
whole dext during ASFWDriver::free() (crash report 2026-07-03 13:21). A
crashed dext is not relaunched, so the device never re-published until
reboot; this also explains recurring shutdown stalls.

Nothing needs OSObject semantics here (no OSAction, no cast, no IIG), so
make it a plain final class owned by std::unique_ptr, matching
IsochTransmitContext, and delete the MakeOSObject helper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ammed the AR path

OHCI §8.4.2: AR bufferFill appends a status/timestamp trailer quadlet to
every received packet. ARPacketParser treated it as optional: a packet whose
header+payload ended flush with the received-byte frontier parsed as
"complete" with totalLength 4 bytes short. The trailer then arrived as an
orphan quadlet at the head of the unread stream, every subsequent parse was
misaligned (responses completed against unknown tLabels, then an unparseable
byte pattern), and the AR response path jammed permanently — all async on
the bus timed out (ackCode=0x1: devices kept ACKing) until reboot.

Field trace 2026-07-03 13:53:28: tLabel=6's 104-byte read-block response
consumed as 120 bytes, its trailer left as the "offset 0/4" tail in
buffer[5]; three real responses destroyed ("No transaction for key"),
parser stuck at offset 56 forever. Chunked DICE section reads made the
flush-boundary case likely enough to hit on the third bring-up.

Fix: no trailer in the window means the packet is incomplete — return
nullopt so the tail is preserved and the stitched cross-buffer retry picks
the packet up whole once the trailer lands.

Adds a parser-level regression test and a ring-level reproduction of the
exact trailer-only straddle; fixes the tLabel-extraction test fixture to be
a valid AR DMA image (trailer included).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DICE stores global-section strings little-endian: the first character of
each quadlet lives in the least-significant byte. The wire transmits
quadlets big-endian, so reading characters MSB-first byte-reversed every
quadlet ("Veni" -> "ineV"). Read LSB-first instead.

Extracted the decode into DecodeDiceNickname() (header-only) and added
DiceNicknameTests covering the Venice regression, short names, payload
bounds, and the empty case.

cross-validated with FFADO dice_avdevice.cpp:696
("Strings from the device are always little-endian").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CopyLabelBlob had the same byte-order bug as the nickname: it read each
quadlet big-endian and emitted MSB-first, byte-reversing every 4-char
group. DICE stores text fields little-endian (first char in the LSB), so
emit LSB-first.

Add SplitDiceLabels() to split a decoded TX/RX name blob into per-channel
names (separator '\', list terminated by '\\'), mirroring FFADO
splitNameString/splitString. Preserves a leading empty token so the
result index stays aligned with the channel index. Covered by
DiceLabelTests; the nickname/label byte order is locked by DiceNicknameTests.

cross-validated with FFADO dice_avdevice.cpp:1527,1547 (TX/RX name strings
little-endian) + splitNameString + ffadotypes.h splitString.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the plumbing to surface real per-channel names (not just "In N"/"Out N")
without changing behavior when no labels are present:

- Widen kMaxNamedChannels 8 -> 32 (Venice F32 is 32x32); the ivars name
  arrays and the SetElementName loops follow the constant instead of a
  hardcoded 8, so all elements can be named.
- Model::ASFWAudioDevice gains input/outputChannelNames vectors, published
  as OSArray<OSString> under kInput/kOutputChannelNames.
- The audio side parses them into deviceInput/OutputChannelNames; a single
  BuildChannelNamesFromPlugs() now prefers a device label per slot and falls
  back to the synthesized "<plug> N" for empty slots. Both the initial parse
  and BuildAudioGraph's post-profile regeneration route through it, so they
  agree.

Inert until a backend populates the names (next commit wires DICE). No
behavior change for devices that publish no labels.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wires the DICE device-truth source into the channel-name carry path:

- DICETcatProtocol retains the per-channel labels parsed from the TX/RX
  stream-format name sections, flattened across each direction's streams in
  channel order (input == device TX, output == device RX per AudioTypes.hpp),
  published through the existing runtimeCapsValid_ release/acquire fence.
  Exposed via a new IDeviceProtocol::GetChannelLabels().
- DiceAudioBackend::EnsureNubForGuid eagerly loads the runtime caps (which
  reads global + TX + RX, including the name sections) once before the first
  nub publish, then fills Model::ASFWAudioDevice input/outputChannelNames from
  GetChannelLabels. The load early-returns if caps are already cached, is
  guarded against teardown, and publishes regardless of outcome so names fall
  back to synthesized "<plug> N" when unavailable.

CoreAudio (Logic / Audio MIDI Setup) now shows the device's real channel
names. Covered by DICETcatProtocolTests.ChannelLabelsFlattenAcrossStreams...;
the wire decode is locked by DiceLabelTests/DiceNicknameTests.

cross-validated with FFADO dice_avdevice.cpp (capture==TX, playback==RX).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…els parse

ReadTx/RxStreamConfig issued a single block read capped at 512 bytes (the safe
single-read payload for DICE max_rec). A multi-stream section is larger: the
Venice F32 needs 8 + 2*280 = 568 bytes, and stream 1's 256-byte label blob
starts at byte 304 -- so ParseStreamConfig's bounds guard silently skipped it
and only stream 0's channel names (first 16 channels per direction) reached
CoreAudio; channels 17-32 fell back to synthesized names.

Add ReadSectionChunked: chain <=512-byte reads into one buffer covering
min(section size, 4KB), then parse once. A failure after the first chunk
delivers the partial buffer (parser guards each region), preserving the old
best-effort behavior; only a failed first chunk is a hard error.

(The PrepareSequenceMatchesReferenceWindow expectation update that rode along
in the original commit stays with the sample-rate series it belongs to.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alicankaralar alicankaralar force-pushed the feat/dice-channel-names branch from 7bc4870 to 3155616 Compare July 9, 2026 18:57
@mrmidi mrmidi merged commit e0ac6bf into mrmidi:main Jul 10, 2026
2 checks passed
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