Fade at the iPad app's rate; relay rate report in debug mode
Measured on the booth relay with debug logging on: while dragging a fader, the DiGiCo iPad app sends /Input_Channels/N/fader at most every 45 ms (about 22 per second; 16/s on average while moving). Fades now step every 45 ms to match (was 50 ms). Verified on the S31 with a switched-off matrix send: 23 steps in 1 s, landing exactly on target. New debug aid: with "Debug logging" on, the module logs every 5 s, per iPad, the busiest address patterns with their message rate and the shortest gap between two messages to the same address. The fade tests now derive their step counts from the exported FADE_STEP_MS. npm test: 542 checks pass on Node 25 and on Companion's Node 22. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+11
-10
@@ -115,6 +115,7 @@ function makeContext() {
|
||||
// 0. Imports, config fields, upgrade script
|
||||
// ---------------------------------------------------------------------------
|
||||
const mainMod = await import('../src/main.js')
|
||||
const FADE_STEP = mainMod.FADE_STEP_MS
|
||||
const Instance = mainMod.default
|
||||
check(typeof Instance === 'function' && Array.isArray(mainMod.UpgradeScripts), 'src/main.js imports and exports a default class + UpgradeScripts')
|
||||
|
||||
@@ -1735,10 +1736,10 @@ const idle = () => waitFor(() => inst.fades.size === 0, 3000)
|
||||
const dbs = w.map((x) => x.db)
|
||||
const pos = dbs.map((v) => faderPosition(v))
|
||||
const gaps = w.slice(1).map((x, k) => x.at - w[k].at).sort((a, b) => a - b)
|
||||
check(w.length === 10, `500 ms at 20 Hz is ten writes (${w.length})`)
|
||||
check(FADE_STEP === 45 && w.length === Math.ceil(500 / FADE_STEP), `500 ms at one step per ${FADE_STEP} ms is ${Math.ceil(500 / FADE_STEP)} writes (${w.length})`)
|
||||
check(dbs.every((v, k) => k === 0 || v > dbs[k - 1]) && dbs[0] > -90 && dbs[0] < 0 && dbs.at(-1) === 0, `monotonic, the first write is above -90 and below the target, the last is exactly 0 (${dbs.map((v) => Math.round(v * 10) / 10).join(', ')})`)
|
||||
check(pos.every((p, k) => near(p - (k ? pos[k - 1] : 0), 7.5, 0.01)), `the steps are even in fader position (7.5 each: ${pos.map((p) => Math.round(p * 10) / 10).join(', ')})`)
|
||||
check(w.at(-1).at - t0 >= 495 && w.at(-1).at - t0 <= 700 && gaps[Math.floor(gaps.length / 2)] >= 35 && gaps[Math.floor(gaps.length / 2)] <= 75, `it ends between 500 and 700 ms after the action, one step about every 50 ms (ended after ${w.at(-1).at - t0} ms, median step ${gaps[Math.floor(gaps.length / 2)]} ms)`)
|
||||
check(pos.every((p, k) => near(p - (k ? pos[k - 1] : 0), 75 / Math.ceil(500 / FADE_STEP), 0.01)), `the steps are even in fader position (${75 / Math.ceil(500 / FADE_STEP)} each: ${pos.map((p) => Math.round(p * 10) / 10).join(', ')})`)
|
||||
check(w.at(-1).at - t0 >= 495 && w.at(-1).at - t0 <= 700 && gaps[Math.floor(gaps.length / 2)] >= 35 && gaps[Math.floor(gaps.length / 2)] <= 75, `it ends between 500 and 700 ms after the action, one step about every ${FADE_STEP} ms (ended after ${w.at(-1).at - t0} ms, median step ${gaps[Math.floor(gaps.length / 2)]} ms)`)
|
||||
check(S.getFader('input', 11) === 0 && inst.fades.size === 0 && cb('fade_running', {}) === false, 'the state ends at 0 and fade_running is false again')
|
||||
await sleep(80)
|
||||
check(ctx.variables.fades_running === 0, 'fades_running reads 0 again')
|
||||
@@ -1751,7 +1752,7 @@ const idle = () => waitFor(() => inst.fades.size === 0, 3000)
|
||||
await go('input_fader_set', { channel: 12, level: 0, duration: 500, curve: 'db' })
|
||||
check(await idle(), 'the linear fade finishes')
|
||||
const dbs = writesTo(faderAddr(12), mark).map((x) => x.db)
|
||||
check(dbs.length === 10 && dbs.every((v, k) => near(v, -60 + 6 * (k + 1), 0.01)) && dbs.at(-1) === 0, `'Linear in dB' moves 6 dB per step (${dbs.map((v) => Math.round(v * 100) / 100).join(', ')})`)
|
||||
check(dbs.length === Math.ceil(500 / FADE_STEP) && dbs.every((v, k) => near(v, -60 + (60 / Math.ceil(500 / FADE_STEP)) * (k + 1), 0.01)) && dbs.at(-1) === 0, `'Linear in dB' moves the same dB every step (${dbs.map((v) => Math.round(v * 100) / 100).join(', ')})`)
|
||||
// the curve option is optional: a button made before fades existed (no duration, no curve) is an instant set
|
||||
const m2 = sent.length
|
||||
await go('input_fader_set', { channel: 12, level: -10 })
|
||||
@@ -1797,7 +1798,7 @@ const idle = () => waitFor(() => inst.fades.size === 0, 3000)
|
||||
check(inst.fades.size === 1, 'a second fade on the same fader replaces the first (one fade per address)')
|
||||
check(await idle(), 'the second fade finishes')
|
||||
w = writesTo(faderAddr(14), markB)
|
||||
check(w.length === 6 && w.at(-1).db === -70 && w.every((x, k) => k === 0 || x.db < w[k - 1].db) && w[0].db < lastOfA && w[0].db > -70, `the second fade takes over from where the first one was (${Math.round(lastOfA)} dB, rising) and runs down to -70 without a write from the first (${w.map((x) => Math.round(x.db)).join(', ')})`)
|
||||
check(w.length === Math.ceil(300 / FADE_STEP) && w.at(-1).db === -70 && w.every((x, k) => k === 0 || x.db < w[k - 1].db) && w[0].db < lastOfA && w[0].db > -70, `the second fade takes over from where the first one was (${Math.round(lastOfA)} dB, rising) and runs down to -70 without a write from the first (${w.map((x) => Math.round(x.db)).join(', ')})`)
|
||||
|
||||
mark = sent.length
|
||||
await go('input_fader_set', { channel: 14, level: -90 })
|
||||
@@ -1831,7 +1832,7 @@ const idle = () => waitFor(() => inst.fades.size === 0, 3000)
|
||||
await go('send_level_set', { channel: 16, buss: 2, level: -10, duration: 200, curve: 'db' })
|
||||
check(await idle(), 'an aux send level fades')
|
||||
const sw = writesTo('/Input_Channels/16/Aux_Send/2/send_level', mark).map((x) => x.db)
|
||||
check(sw.length === 4 && sw.at(-1) === -10 && sw.every((v, k) => near(v, -30 + 5 * (k + 1), 0.01)), `aux send fade: four steps of 5 dB (${sw.join(', ')})`)
|
||||
check(sw.length === Math.ceil(200 / FADE_STEP) && sw.at(-1) === -10 && sw.every((v, k) => near(v, -30 + (20 / Math.ceil(200 / FADE_STEP)) * (k + 1), 0.01)), `aux send fade: ${Math.ceil(200 / FADE_STEP)} even steps (${sw.map((v) => Math.round(v * 100) / 100).join(', ')})`)
|
||||
await go('buss_fader_set', { buss: 4, level: -30 })
|
||||
await go('matrix_fader_set', { matrix: 2, level: -30 })
|
||||
await go('cg_fader_set', { cg: 2, level: -30 })
|
||||
@@ -1853,12 +1854,12 @@ const idle = () => waitFor(() => inst.fades.size === 0, 3000)
|
||||
await go('matrix_send_level_set', { matrixIn: 4, matrixOut: 4, level: -90, duration: 300, curve: 'db' })
|
||||
check(await idle(), 'a matrix send fades down')
|
||||
let m = writesTo(addrM, mark).map((x) => x.db)
|
||||
check(m.length === 6 && m.at(-1) === -90 && m.every((v) => v >= -90) && m.every((v, k) => k === 0 || v < m[k - 1]), `down to -90: ${m.map((v) => Math.round(v * 10) / 10).join(', ')} (never below -90)`)
|
||||
check(m.length === Math.ceil(300 / FADE_STEP) && m.at(-1) === -90 && m.every((v) => v >= -90) && m.every((v, k) => k === 0 || v < m[k - 1]), `down to -90: ${m.map((v) => Math.round(v * 10) / 10).join(', ')} (never below -90)`)
|
||||
mark = sent.length
|
||||
await go('matrix_send_level_set', { matrixIn: 4, matrixOut: 4, level: 0, duration: 500, curve: 'fader' })
|
||||
check(await idle(), 'and up from -90 again')
|
||||
m = writesTo(addrM, mark).map((x) => x.db)
|
||||
check(m.length === 10 && m[0] > -90 && m.at(-1) === 0 && m.every((v) => v >= -90 && v <= 10), `from the -90 floor on the fader taper (${m.map((v) => Math.round(v)).join(', ')})`)
|
||||
check(m.length === Math.ceil(500 / FADE_STEP) && m[0] > -90 && m.at(-1) === 0 && m.every((v) => v >= -90 && v <= 10), `from the -90 floor on the fader taper (${m.map((v) => Math.round(v)).join(', ')})`)
|
||||
// a fade to a target below the floor stops at -90 for a matrix send, at -150 (-inf) for a fader
|
||||
mark = sent.length
|
||||
await inst.setMatrixSendLevel(4, 4, -150, 200, 'db')
|
||||
@@ -1959,13 +1960,13 @@ check(inst.fades.size === 0 && inst.fadeStarts.size === 0, 'no fade or fade star
|
||||
check(inst.fades.size === 1, 'a group fader fade is running')
|
||||
check(await idle(), 'the group fade finishes')
|
||||
const gw = writesTo('/Group_Outputs/25/fader', m2).map((x) => x.db)
|
||||
check(gw.length === 6 && gw.at(-1) === -10 && gw.every((v, k) => near(v, -40 + 5 * (k + 1), 0.01)), `group_fader_set fades on /Group_Outputs/25/fader (${gw.map((v) => Math.round(v * 100) / 100).join(', ')})`)
|
||||
check(gw.length === Math.ceil(300 / FADE_STEP) && gw.at(-1) === -10 && gw.every((v, k) => near(v, -40 + (30 / Math.ceil(300 / FADE_STEP)) * (k + 1), 0.01)), `group_fader_set fades on /Group_Outputs/25/fader (${gw.map((v) => Math.round(v * 100) / 100).join(', ')})`)
|
||||
await go('aux_fader_set', { buss: 5, level: -40 })
|
||||
const m3 = sent.length
|
||||
await go('aux_fader_set', { buss: 5, level: -10, duration: 200, curve: 'db' })
|
||||
check(await idle(), 'the aux master fade finishes')
|
||||
const aw = writesTo('/Aux_Outputs/5/fader', m3).map((x) => x.db)
|
||||
check(aw.length === 4 && aw.at(-1) === -10 && !sent.slice(m3).some((m) => m.address.startsWith('/Group_Outputs/5/')), `aux_fader_set fades on /Aux_Outputs/5/fader (${aw.map((v) => Math.round(v * 100) / 100).join(', ')})`)
|
||||
check(aw.length === Math.ceil(200 / FADE_STEP) && aw.at(-1) === -10 && !sent.slice(m3).some((m) => m.address.startsWith('/Group_Outputs/5/')), `aux_fader_set fades on /Aux_Outputs/5/fader (${aw.map((v) => Math.round(v * 100) / 100).join(', ')})`)
|
||||
void mark
|
||||
|
||||
// presets: Groups and Aux masters sections, group / aux meter sections, no "Busses" section
|
||||
|
||||
Reference in New Issue
Block a user