Write on/off switches as float 0/1, as the S31 requires
Verified on a live S31 against an unused, silent input (restored after): a float fader write is accepted and echoed, but a mute written as int 0/1 or as an OSC True/False is silently ignored, while a float 1.0/0.0 works. DiGiCo's 2014 SD command list says int, which is wrong for the S31. Every switch (mute, solo, send on, group assign, EQ/dynamics/filter/ insert/delay switches, phantom, phase, pad, GEQ in) now goes out as a float. The mock console now ignores int and T/F switch writes the same way the console does, so a regression to int fails 38 checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+63
-26
@@ -483,12 +483,12 @@ function expectWrite(list, address, tags, value, label) {
|
||||
expectWrite(r, '/Input_Channels/8/fader', ',f', -60, 'input_fader_adjust from -inf lands at -60 dB')
|
||||
const mute9 = S.getMute('input', 9)
|
||||
r = await run('input_mute', { channels: [9], state: 'toggle' })
|
||||
expectWrite(r, '/Input_Channels/9/mute', ',i', mute9 ? 0 : 1, 'input_mute toggle (known state)')
|
||||
expectWrite(r, '/Input_Channels/9/mute', ',f', mute9 ? 0 : 1, 'input_mute toggle (known state)')
|
||||
r = await run('input_mute', { channels: [10, 11], state: 'on' })
|
||||
expectWrite(r, '/Input_Channels/10/mute', ',i', 1, 'input_mute on, first of two inputs')
|
||||
expectWrite(r, '/Input_Channels/11/mute', ',i', 1, 'input_mute on, second of two inputs')
|
||||
expectWrite(r, '/Input_Channels/10/mute', ',f', 1, 'input_mute on, first of two inputs')
|
||||
expectWrite(r, '/Input_Channels/11/mute', ',f', 1, 'input_mute on, second of two inputs')
|
||||
r = await run('input_solo', { channels: [12], state: 'on' })
|
||||
expectWrite(r, '/Input_Channels/12/solo', ',i', 1, 'input_solo on')
|
||||
expectWrite(r, '/Input_Channels/12/solo', ',f', 1, 'input_solo on')
|
||||
r = await run('input_pan_set', { channel: 13, pan: 0.25 })
|
||||
expectWrite(r, '/Input_Channels/13/Panner/pan', ',f', 0.25, 'input_pan_set')
|
||||
r = await run('input_pan_adjust', { channel: 13, pdelta: 0.5 })
|
||||
@@ -498,7 +498,7 @@ function expectWrite(list, address, tags, value, label) {
|
||||
|
||||
// sends
|
||||
r = await run('send_on', { channel: 2, buss: 3, state: 'on' })
|
||||
expectWrite(r, '/Input_Channels/2/Aux_Send/3/send_on', ',i', 1, 'send_on on')
|
||||
expectWrite(r, '/Input_Channels/2/Aux_Send/3/send_on', ',f', 1, 'send_on on')
|
||||
r = await run('send_level_set', { channel: 2, buss: 3, level: -10 })
|
||||
expectWrite(r, '/Input_Channels/2/Aux_Send/3/send_level', ',f', -10, 'send_level_set')
|
||||
r = await run('send_level_adjust', { channel: 2, buss: 3, delta: 3 })
|
||||
@@ -514,25 +514,25 @@ function expectWrite(list, address, tags, value, label) {
|
||||
const q = r.findIndex((m) => m.address === '/Input_Channels/4/Aux_Send/5/send_on/?')
|
||||
const w = r.findIndex((m) => m.address === '/Input_Channels/4/Aux_Send/5/send_on')
|
||||
const replied = r[w]?.args?.[0]
|
||||
check(q === 0 && w > q && (replied === 0 || replied === 1) && r[w].tags === ',i', `toggle on unknown state queries first (query #${q}), then writes (${JSON.stringify(r[w]?.args)})`)
|
||||
check(q === 0 && w > q && (replied === 0 || replied === 1) && r[w].tags === ',f', `toggle on unknown state queries first (query #${q}), then writes (${JSON.stringify(r[w]?.args)})`)
|
||||
await sleep(60)
|
||||
check(S.getSend(4, 5).on === (replied === 1), 'state follows the toggle (echo or optimistic)')
|
||||
|
||||
// groups
|
||||
check(S.getGroup(1, 24) === undefined, 'group assign is not preloaded')
|
||||
r = await run('group_assign', { channel: 3, buss: 24, state: 'off' })
|
||||
expectWrite(r, '/Input_Channels/3/Group_Send/24/group', ',i', 0, 'group_assign off')
|
||||
expectWrite(r, '/Input_Channels/3/Group_Send/24/group', ',f', 0, 'group_assign off')
|
||||
r = await run('group_assign', { channel: 3, buss: 24, state: 'on' })
|
||||
expectWrite(r, '/Input_Channels/3/Group_Send/24/group', ',i', 1, 'group_assign on')
|
||||
expectWrite(r, '/Input_Channels/3/Group_Send/24/group', ',f', 1, 'group_assign on')
|
||||
|
||||
// busses: aux side vs group side
|
||||
r = await run('buss_mute', { busses: [3], state: 'on' })
|
||||
expectWrite(r, '/Aux_Outputs/3/mute', ',i', 1, 'buss_mute on an aux buss uses Aux_Outputs')
|
||||
expectWrite(r, '/Aux_Outputs/3/mute', ',f', 1, 'buss_mute on an aux buss uses Aux_Outputs')
|
||||
r = await run('buss_mute', { busses: [9, 24], state: 'on' })
|
||||
expectWrite(r, '/Group_Outputs/9/mute', ',i', 1, 'buss_mute on a group buss uses Group_Outputs')
|
||||
expectWrite(r, '/Group_Outputs/24/mute', ',i', 1, 'buss_mute second group buss')
|
||||
expectWrite(r, '/Group_Outputs/9/mute', ',f', 1, 'buss_mute on a group buss uses Group_Outputs')
|
||||
expectWrite(r, '/Group_Outputs/24/mute', ',f', 1, 'buss_mute second group buss')
|
||||
r = await run('buss_solo', { busses: [24], state: 'on' })
|
||||
expectWrite(r, '/Group_Outputs/24/solo', ',i', 1, 'buss_solo')
|
||||
expectWrite(r, '/Group_Outputs/24/solo', ',f', 1, 'buss_solo')
|
||||
r = await run('buss_fader_set', { buss: 24, level: -3 })
|
||||
expectWrite(r, '/Group_Outputs/24/fader', ',f', -3, 'buss_fader_set on a group buss')
|
||||
const auxFader = S.getFader('buss', 1)
|
||||
@@ -541,13 +541,13 @@ function expectWrite(list, address, tags, value, label) {
|
||||
|
||||
// matrix
|
||||
r = await run('matrix_mute', { matrix: 2, state: 'toggle' })
|
||||
check(writes(r).some((m) => m.address === '/Matrix_Outputs/2/mute' && m.tags === ',i'), 'matrix_mute toggle')
|
||||
check(writes(r).some((m) => m.address === '/Matrix_Outputs/2/mute' && m.tags === ',f'), 'matrix_mute toggle')
|
||||
r = await run('matrix_fader_set', { matrix: 2, level: -20 })
|
||||
expectWrite(r, '/Matrix_Outputs/2/fader', ',f', -20, 'matrix_fader_set')
|
||||
r = await run('matrix_fader_adjust', { matrix: 2, delta: 4 })
|
||||
expectWrite(r, '/Matrix_Outputs/2/fader', ',f', -16, 'matrix_fader_adjust')
|
||||
r = await run('matrix_send_on', { matrixIn: 2, matrixOut: 3, state: 'on' })
|
||||
expectWrite(r, '/Matrix_Inputs/2/Matrix_Send/3/send_on', ',i', 1, 'matrix_send_on')
|
||||
expectWrite(r, '/Matrix_Inputs/2/Matrix_Send/3/send_on', ',f', 1, 'matrix_send_on')
|
||||
r = await run('matrix_send_level_set', { matrixIn: 2, matrixOut: 3, level: -30 })
|
||||
expectWrite(r, '/Matrix_Inputs/2/Matrix_Send/3/send_level', ',f', -30, 'matrix_send_level_set')
|
||||
r = await run('matrix_send_level_adjust', { matrixIn: 3, matrixOut: 2, delta: 1 })
|
||||
@@ -556,9 +556,9 @@ function expectWrite(list, address, tags, value, label) {
|
||||
|
||||
// control groups (1-based)
|
||||
r = await run('cg_mute', { cg: 1, state: 'on' })
|
||||
expectWrite(r, '/Control_Groups/1/mute', ',i', 1, 'cg_mute')
|
||||
expectWrite(r, '/Control_Groups/1/mute', ',f', 1, 'cg_mute')
|
||||
r = await run('cg_solo', { cg: 10, state: 'on' })
|
||||
expectWrite(r, '/Control_Groups/10/solo', ',i', 1, 'cg_solo')
|
||||
expectWrite(r, '/Control_Groups/10/solo', ',f', 1, 'cg_solo')
|
||||
r = await run('cg_fader_set', { cg: 2, level: -6 })
|
||||
expectWrite(r, '/Control_Groups/2/fader', ',f', -6, 'cg_fader_set')
|
||||
r = await run('cg_fader_adjust', { cg: 2, delta: -2 })
|
||||
@@ -566,7 +566,7 @@ function expectWrite(list, address, tags, value, label) {
|
||||
|
||||
// graphic EQ
|
||||
r = await run('geq_on', { geq: 1, state: 'off' })
|
||||
expectWrite(r, '/Graphic_EQ/1/geq_in', ',i', 0, 'geq_on off')
|
||||
expectWrite(r, '/Graphic_EQ/1/geq_in', ',f', 0, 'geq_on off')
|
||||
r = await run('geq_band_set', { geq: 1, band: 5, gain: 3 })
|
||||
expectWrite(r, '/Graphic_EQ/1/geq_gain_5', ',f', 3, 'geq_band_set')
|
||||
r = await run('geq_band_adjust', { geq: 1, band: 5, delta: 1 })
|
||||
@@ -580,18 +580,18 @@ function expectWrite(list, address, tags, value, label) {
|
||||
r = await run('input_phantom', { channel: 14, state: 'toggle' })
|
||||
const pq = r.findIndex((m) => m.address === '/Input_Channels/14/Channel_Input/phantom/?')
|
||||
const pw = r.findIndex((m) => m.address === '/Input_Channels/14/Channel_Input/phantom')
|
||||
check(pq === 0 && pw > pq && r[pw].tags === ',i' && r[pw].args[0] === 1, `input_phantom toggle on an unloaded switch queries first, then writes the opposite of the replied 0 (${JSON.stringify(r[pw]?.args)})`)
|
||||
check(pq === 0 && pw > pq && r[pw].tags === ',f' && r[pw].args[0] === 1, `input_phantom toggle on an unloaded switch queries first, then writes the opposite of the replied 0 (${JSON.stringify(r[pw]?.args)})`)
|
||||
r = await run('input_lpf_in', { channel: 14, state: 'on' })
|
||||
expectWrite(r, '/Input_Channels/14/Filters/hi_filter_in', ',i', 1, 'input_lpf_in on (hi_filter_in is the low-pass filter)')
|
||||
expectWrite(r, '/Input_Channels/14/Filters/hi_filter_in', ',f', 1, 'input_lpf_in on (hi_filter_in is the low-pass filter)')
|
||||
r = await run('input_hpf_in', { channel: 14, state: 'on' })
|
||||
expectWrite(r, '/Input_Channels/14/Filters/lo_filter_in', ',i', 1, 'input_hpf_in on (lo_filter_in is the high-pass filter)')
|
||||
expectWrite(r, '/Input_Channels/14/Filters/lo_filter_in', ',f', 1, 'input_hpf_in on (lo_filter_in is the high-pass filter)')
|
||||
r = await run('input_pad', { channel: 14, state: 'off' })
|
||||
expectWrite(r, '/Input_Channels/14/Channel_Input/input_pad', ',i', 0, 'input_pad off')
|
||||
expectWrite(r, '/Input_Channels/14/Channel_Input/input_pad', ',f', 0, 'input_pad off')
|
||||
r = await run('input_phase', { channel: 14, state: 'on' })
|
||||
expectWrite(r, '/Input_Channels/14/Channel_Input/phase', ',i', 1, 'input_phase on')
|
||||
expectWrite(r, '/Input_Channels/14/Channel_Input/phase', ',f', 1, 'input_phase on')
|
||||
for (const [id, path] of [['eq_in', 'EQ/eq_in'], ['comp_in', 'Dynamics/comp_in'], ['gate_in', 'Dynamics/gate_in'], ['delay_on', 'Channel_Delay/delay_on'], ['insert_a', 'Insert/insert_A_in'], ['insert_b', 'Insert/insert_B_in']]) {
|
||||
r = await run(`input_${id}`, { channel: 14, state: 'on' })
|
||||
expectWrite(r, `/Input_Channels/14/${path}`, ',i', 1, `input_${id} on`)
|
||||
expectWrite(r, `/Input_Channels/14/${path}`, ',f', 1, `input_${id} on`)
|
||||
}
|
||||
r = await run('input_hpf_freq_set', { channel: 14, value: 80 })
|
||||
expectWrite(r, '/Input_Channels/14/Filters/lo_filter_freq', ',f', 80, 'input_hpf_freq_set')
|
||||
@@ -614,11 +614,11 @@ function expectWrite(list, address, tags, value, label) {
|
||||
r = await run('matrix_trim_set', { matrix: 2, value: 1.5 })
|
||||
expectWrite(r, '/Matrix_Outputs/2/Buss_Trim/trim', ',f', 1.5, 'matrix_trim_set')
|
||||
r = await run('buss_eq_in', { buss: 3, state: 'toggle' })
|
||||
check(r[0]?.address === '/Aux_Outputs/3/EQ/eq_in/?' && writes(r).some((m) => m.address === '/Aux_Outputs/3/EQ/eq_in' && m.tags === ',i'), 'buss_eq_in toggle on an unloaded aux buss queries first')
|
||||
check(r[0]?.address === '/Aux_Outputs/3/EQ/eq_in/?' && writes(r).some((m) => m.address === '/Aux_Outputs/3/EQ/eq_in' && m.tags === ',f'), 'buss_eq_in toggle on an unloaded aux buss queries first')
|
||||
r = await run('buss_insert_a', { buss: 9, state: 'on' })
|
||||
expectWrite(r, '/Group_Outputs/9/Insert/insert_A_in', ',i', 1, 'buss_insert_a on a group buss')
|
||||
expectWrite(r, '/Group_Outputs/9/Insert/insert_A_in', ',f', 1, 'buss_insert_a on a group buss')
|
||||
r = await run('buss_phase', { buss: 3, state: 'on' })
|
||||
expectWrite(r, '/Aux_Outputs/3/Buss_Trim/phase', ',i', 1, 'buss_phase on')
|
||||
expectWrite(r, '/Aux_Outputs/3/Buss_Trim/phase', ',f', 1, 'buss_phase on')
|
||||
|
||||
// names, snapshots, raw, relay
|
||||
r = await run('snapshot_fire', { snapshot: 5 })
|
||||
@@ -647,6 +647,43 @@ function expectWrite(list, address, tags, value, label) {
|
||||
check(seq.filter((a) => a === '/Control_Groups/3/fader/?').length === 1 && seq.indexOf('/Control_Groups/3/fader/?') > seq.indexOf('/Control_Groups/3/fader'), 'one confirm query of the same address follows when the write guard ends')
|
||||
}
|
||||
|
||||
// A 0/1 switch takes a FLOAT write only (verified on a real S31): the mock ignores int and T/F writes like the console does
|
||||
{
|
||||
const address = '/Input_Channels/30/mute'
|
||||
const before = S.getMute('input', 30)
|
||||
const want = before ? 0 : 1
|
||||
const seen = []
|
||||
const original = inst.onConsoleMessage.bind(inst)
|
||||
inst.onConsoleMessage = (msg, rinfo) => {
|
||||
if (msg.address === address) seen.push(msg)
|
||||
return original(msg, rinfo)
|
||||
}
|
||||
const raw = dgram.createSocket('udp4')
|
||||
const sendRaw = (args) => new Promise((resolve) => raw.send(encode(address, args), P.mock, '127.0.0.1', resolve))
|
||||
await sendRaw([{ i: want }])
|
||||
await sendRaw([true])
|
||||
await sendRaw([false])
|
||||
await sendRaw([{ i: before ? 0 : 1 }])
|
||||
await sleep(500)
|
||||
check(seen.length === 0 && S.getMute('input', 30) === before, 'int and T/F writes to a switch are ignored by the mock: no echo and no state change')
|
||||
inst.queryNow(address)
|
||||
check(await waitFor(() => seen.length === 1, 2000) && seen[0].tags === ',f' && seen[0].args[0] === (before ? 1 : 0), 'reading it back still gives the old value, as a float')
|
||||
await sendRaw([want])
|
||||
check(await waitFor(() => seen.length === 2, 2000) && seen[1].tags === ',f' && seen[1].args[0] === want && S.getMute('input', 30) === !before, 'a float write is accepted and echoed as a float')
|
||||
await sendRaw([before ? 1 : 0]) // restore
|
||||
await sleep(300)
|
||||
inst.onConsoleMessage = original
|
||||
raw.close()
|
||||
|
||||
// and the module's own switch writes are floats, so the console keeps them (the confirm query after the guard agrees)
|
||||
const mark = sent.length
|
||||
const was31 = S.getMute('input', 31)
|
||||
await run('input_mute', { channels: [31], state: 'toggle' })
|
||||
await sleep(inst.timing.writeGuardMs + 500)
|
||||
const w = sent.slice(mark).find((m) => m.address === '/Input_Channels/31/mute' && !m.address.endsWith('/?'))
|
||||
check(!!w && decodeArgs(w).tags === ',f' && S.getMute('input', 31) === !was31, 'a module switch write goes out as a float and the console accepted it (the confirm query returns the written value)')
|
||||
}
|
||||
|
||||
// Names: renames rebuild the definitions once (debounced) and the dropdowns pick them up
|
||||
{
|
||||
const before = ctx.rebuilds
|
||||
|
||||
Reference in New Issue
Block a user