v1.0.7: fix dLive/GLD/Qu connections, correct fader levels, dB cue storage#20
Merged
Conversation
Connecting to a dLive sat in Connecting forever with nothing logged. The handshake sent E0 00 04 02 03 as a "session seed", but that is the console's half of the port exchange, not the client's: the client sends E0 00 04 01 03 <ourUdpPort:2> E7 and waits for the console to answer with its own port before anything else is accepted. Subscribes were being fired at a console still waiting on that exchange, so it never replied, and nothing timed out because the TCP connect had already stopped the connect timer. - Send the correct seed, carrying the local port of a UDP socket bound for the purpose, and start the subscribe chain only when the reply lands. - Keep-alive is E0 00 01 03 E7 over UDP every 1000ms, not the seed over TCP every 5000ms; the console drops a session silent for 1.5s. - Add an RX watchdog and a handshake timeout so a stall reports instead of hanging. - Write our source id at [5..6] of SET frames; 00 00 addressed nothing. - TcpTransport: carry the socket error enum (Qt renders an unmapped errno as a bare "Unknown error"), and abort a busy socket before reconnecting, which connectToHost() rejects outright. Frames recovered from theatremix.exe. test_allenheath_session drives a real DLiveProtocol against a stub MixRack that answers only a correct seed; it fails against the old handshake.
Auto-scan never listed a dLive. Two causes: - The ACE by-name request answers with a handle, not a name. We parsed that 14-byte handle frame as the identity string, got garbage, matched no model prefix, and dropped the console as Unknown. Read the handle back and parse the answer to that instead. - Probes went out from an ephemeral source port. The reference binds 32323; consoles may answer to it rather than to whatever port we happened to get. Also normalize IPv4-mapped senders (::ffff:a.b.c.d): a dual-stack socket reports them in that form, which then reaches the driver as the literal string to connect to, and the console never answers on it. The read-back framing is a best reading of an ambiguous decompilation and wants confirming against hardware.
Qu was inheriting SQ's driver, but the two share a transport and nothing else. Per the Qu Mixer MIDI Protocol V1.9+ (Qu-16/24/32): - The NRPN halves are swapped. Qu puts the channel in the MSB and the parameter id in the LSB (BN 63 CH, BN 62 ID); SQ is the other way round. Every fader, mute and DCA message was going out with the two bytes backwards. - Fader is ID 0x17 with a 7-bit value (0 dB = 0x62), not SQ's 14-bit pair. - Mute is Note On/Off, not an NRPN, so the console ignored ours entirely. - 4 DCA groups (0x10..0x13) and 100 scenes, not 8 and 300. - Scene recall sets both Bank Select MSB and LSB. Fader values come from the doc's Fader / Send Level table, reproduced exactly at every printed anchor.
DCA_LEVEL_LSB_BASE was 0x67, citing the SQ Level table. That number is not in it: 0x67 is the SQ+ value (SQ+ MIDI Protocol V2.0.3, which covers SQ5+/6+/7+, different hardware). SQ Issue 5 p24 and Qu Issue 2 p25 agree on 0x20, so every DCA fader move was addressing the wrong parameter. Levels also went out as a plain level*16383, which under the console's Linear Taper means a fader at unity sent -24.5 dB rather than 0 dB. Encode through the console's own law instead: - Linear Taper is exactly linear in dB (0 dB = 15196, 118.7 steps/dB) and reproduces every printed anchor within the doc's own rounding. - Audio Taper is a curve, so it carries the printed table and interpolates. The law is a console setting (Utility > General > MIDI > NRPN Fader Law) that cannot be read back, so it is selectable and defaults to Linear, the doc's standard mode.
X32: a console that answers /info but not /xinfo could never connect, because the connect gates on the /xinfo reply. The reference probes with /info, and /xinfo appears nowhere in it. Send both and connect on whichever lands. Renew /xremote every 4.5s as the reference does, not 8s: the console stops sending updates ~10s after the last renew, so at 8s a single dropped packet silently stalls all updates until the next one. The staleness window keeps its own constant rather than riding on the renew interval. WING: /$info and /$xremote do not exist. Identity is "/?", which returns WING,<ip>,<name>,<model>,<serial>,<firmware>, and unsolicited updates need a "/*s~" subscription the console drops after 10s unless renewed. We were gating the connect on a reply that would never arrive and renewing a command that does not exist, so no updates could ever have arrived. Scene recall goes through the console's own $ctl/lib nodes; the OSC doc does not cover the show library, so that part is unconfirmed. Ports were right: X32 OSC/UDP 10023 and WING OSC/UDP 2223 are both documented.
A 0..1 position is not a level until a console applies its fader law, so the same show file played back at different levels per desk: a 0.75 cue meant 0 dB on a dLive and -24.5 dB on an SQ. Carry dB from the cue to the wire instead, and let each driver encode it the way its console wants. This is what the reference does: its Allen & Heath drivers take the level as dB text, clamped to +10 and -95, with -999 standing in for -inf. - Cue stores dB under channelLevelsDb. Shows written before this still load: their positions convert through the law that was in force then, which is the best that can be done, since what a position meant depended on the console. - setChannelFader/setDcaFader become setChannelFaderDb/setDcaFaderDb, so the compiler finds every call site rather than silently reinterpreting it. - Deletes three invented curves: ACE now passes dB straight to its table, Yamaha is dB*100 centi-dB, and WING sends dB to /fdr (its curve was the X32 law copy-pasted). X32 keeps a conversion because its wire format really is a position; the segments are the published law. - Fades ramp to the bottom of the throw and only then go -inf, rather than diving through -900 dB. - CueEditor edits dB; DCAWidget and the X32 parameter cache follow.
Test call sites follow the setChannelFaderDb/setDcaFaderDb rename.
The console maps NRPN levels through one of two curves, chosen at Utility > General > MIDI > NRPN Fader Law, and does not report which. Both curves were already implemented, but the driver always assumed Linear: on a desk set to Audio Taper every level was wrong. Carry the choice in MixerConfig so it travels with the show, expose it in the connection panel for SQ, and hand it to the driver on connect. Defaults to linear, the console's standard mode, so existing shows are unchanged. The combo only appears for SQ: Qu-16/24/32 has a single fixed curve and the setting means nothing to the other consoles.
GLD was driven over the ACE binary protocol on 51321, sharing dLive's driver. Allen & Heath document a different interface for it, and that is the one to speak: MIDI over TCP 51325, per the GLD MIDI and TCP/IP Protocol V1.4. GLD sits between SQ and Qu and matches neither: - Like Qu, the channel goes in the NRPN MSB and mutes are Note On, but the fader NRPN carries no data-entry LSB: three messages, not four. - Its level table is its own. 0 dB is 0x6B, where Qu's is 0x62. - Names and colours are SysEx (header 03/06), not NRPN. - Channels: DCA 1-16 = 10..1F, Input 1-48 = 20..4F. The doc also corrects two counts we had wrong: 16 DCAs (we said 8) and 500 scenes in 4 banks (we said 250). The console stamps its MIDI channel into every message and cannot report it, so it has to match Setup / Control on the desk: carry it in MixerConfig alongside the SQ fader law, expose it for GLD in the connection panel, and hand it to the driver on connect. Discovery is unchanged: GLD still does not answer the ACE identify handshake, so it stays a connect-time selection. Every frame and the whole level table are tested against the doc's printed values.
The SD driver spoke Yamaha DM7's MPRO/EEVT/MMIX protocol, pointed at Allen & Heath's port 51321, under a comment claiming it was reverse-engineered from a DiGiCo reference. It was not: the frames, the "TYPE:StageMix SYNCDIR:2" client registration and the object tree are all DM7's, and no DiGiCo console speaks them. It could never have worked. DiGiCo's actual network control is Generic OSC, and its shape is unusual: there is no published address map. The console's own feature is user-defined by design - the operator writes the messages, "*" stands for the channel - and the syntax varies by model and software version, with command sets handed out by DiGiCo support rather than documented. Inbound OSC only acts when External Control is enabled on the desk. So the driver is a template. It sends the patterns the operator entered and nothing for an operation they left blank, because an address we invented would look like it worked here and do nothing on the console. The patterns and the receive port live in MixerConfig, so they travel with the show, and the connection panel takes them for SD consoles. Capabilities follow: OSC over UDP, port 9000 (the common pairing, not a spec). The tests cover pattern expansion and, mainly, that an unconfigured operation stays silent.
Comment-only. The rationale for levels being dB was repeated across LevelDb.h, Cue.h, Show.h and three driver headers; it now lives once in LevelDb.h and the rest point there. Class headers come back to the length of the ones around them, and a few comments that described the edit rather than the code lost that framing. Byte layouts, doc provenance and the notes about console settings that cannot be read back all stay: none of them are visible in the code. One was wrong rather than just long: GLDProtocol said names longer than the limit were "the console's to reject, not ours to send", while buildName() truncates with left(). It now states what the protocol doc says - 8 accepted, 5 displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.