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:
+1
-1
@@ -110,7 +110,7 @@ never verified.
|
|||||||
level, matrix send level) has a **Fade time (ms, 0 = instant)**, 0 to 60000 ms. With 0 the level jumps to the target at once, as before; above 0
|
level, matrix send level) has a **Fade time (ms, 0 = instant)**, 0 to 60000 ms. With 0 the level jumps to the target at once, as before; above 0
|
||||||
the module moves the level there smoothly, starting from where it is now (it reads the level first when it is not known
|
the module moves the level there smoothly, starting from where it is now (it reads the level first when it is not known
|
||||||
yet; if the console does not answer, the target is set at once and a warning is logged). A new level is written 20 times a
|
yet; if the console does not answer, the target is set at once and a warning is logged). A new level is written 20 times a
|
||||||
second at most (every 50 ms, steps that would change the level by less than 0.05 dB are left out), and the last write is
|
second at most (every 45 ms, about 22 per second, the same rate the DiGiCo iPad app sends a fader drag at; steps that would change the level by less than 0.05 dB are left out), and the last write is
|
||||||
exactly the target. The **Fade curve** (shown once the time is above 0) is *Fader travel (natural)*, which moves the way a
|
exactly the target. The **Fade curve** (shown once the time is above 0) is *Fader travel (natural)*, which moves the way a
|
||||||
fader's travel does (the same taper as the gauges, so the level change sounds even; the default), *Fader travel, ease in/out*
|
fader's travel does (the same taper as the gauges, so the level change sounds even; the default), *Fader travel, ease in/out*
|
||||||
(the same with a slow start and a slow end), or *Linear in dB* (the same number of dB every step; a fade from or to −∞ runs between −90 dB, where sound
|
(the same with a slow start and a slow end), or *Linear in dB* (the same number of dB every step; a fade from or to −∞ runs between −90 dB, where sound
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export const opt = {
|
|||||||
const ADJUST_NOTE = 'From −∞ the first step up jumps to −60 dB.'
|
const ADJUST_NOTE = 'From −∞ the first step up jumps to −60 dB.'
|
||||||
|
|
||||||
/** Said in every level set action: the optional fade. */
|
/** Said in every level set action: the optional fade. */
|
||||||
const FADE_NOTE = 'With a fade time above 0 the level moves from where it is now to the target in steps of 50 ms; another set, adjust or fade on the same level ends it.'
|
const FADE_NOTE = 'With a fade time above 0 the level moves from where it is now to the target in steps of 45 ms (about 22 per second, the rate the DiGiCo iPad app sends fader moves at); another set, adjust or fade on the same level ends it.'
|
||||||
|
|
||||||
/** [fade time in ms (0 = instant), curve] from a set-level action's options; missing options (buttons made before fades existed) mean instant. */
|
/** [fade time in ms (0 = instant), curve] from a set-level action's options; missing options (buttons made before fades existed) mean instant. */
|
||||||
const fadeArgs = (options) => [Number(options?.duration) > 0 ? Math.min(60000, Math.round(Number(options.duration))) : 0, FADE_CURVES.includes(options?.curve) ? options.curve : 'fader']
|
const fadeArgs = (options) => [Number(options?.duration) > 0 ? Math.min(60000, Math.round(Number(options.duration))) : 0, FADE_CURVES.includes(options?.curve) ? options.curve : 'fader']
|
||||||
|
|||||||
+58
-3
@@ -12,7 +12,10 @@ import { getPresetDefinitions } from './definitions/presets.js'
|
|||||||
const LEARN_CAP = 400
|
const LEARN_CAP = 400
|
||||||
|
|
||||||
/** A fade writes a new level this often (20 steps per second at most) and skips steps that would change the level by less than FADE_MIN_DB. */
|
/** A fade writes a new level this often (20 steps per second at most) and skips steps that would change the level by less than FADE_MIN_DB. */
|
||||||
const FADE_STEP_MS = 50
|
/** Fade step: 45 ms (about 22 Hz), the fastest rate the DiGiCo iPad app sends fader moves at (measured 2026-10-20). */
|
||||||
|
export const FADE_STEP_MS = 45
|
||||||
|
/** Debug aid: how often the per-iPad relay rate report is logged (only while debug logging is on). */
|
||||||
|
const RATE_REPORT_MS = 5000
|
||||||
const FADE_MIN_DB = 0.05
|
const FADE_MIN_DB = 0.05
|
||||||
const FADE_MAX_MS = 60000
|
const FADE_MAX_MS = 60000
|
||||||
|
|
||||||
@@ -368,6 +371,55 @@ export default class DigicoS31Instance extends InstanceBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** One log line per error code + destination per `errorLogMs`, whatever the rate the errors come at. */
|
/** One log line per error code + destination per `errorLogMs`, whatever the rate the errors come at. */
|
||||||
|
/**
|
||||||
|
* Debug aid: how fast each iPad sends. While debug logging is on, every relayed iPad message is counted per
|
||||||
|
* address pattern, and every RATE_REPORT_MS an info line per iPad reports the busiest patterns with their message
|
||||||
|
* rate and the shortest gap between two messages to the same address (how granular a fader drag is).
|
||||||
|
*/
|
||||||
|
noteRelayRate(ip, msg) {
|
||||||
|
if (!msg?.address) return
|
||||||
|
const now = Date.now()
|
||||||
|
if (!this.relayRates) this.relayRates = new Map()
|
||||||
|
const key = `${ip}|${foldPattern(msg.address)}`
|
||||||
|
let r = this.relayRates.get(key)
|
||||||
|
if (!r) {
|
||||||
|
r = { ip, pattern: foldPattern(msg.address), count: 0, first: now, minGap: Infinity, last: new Map() }
|
||||||
|
this.relayRates.set(key, r)
|
||||||
|
}
|
||||||
|
r.count++
|
||||||
|
const prev = r.last.get(msg.address)
|
||||||
|
if (prev !== undefined) r.minGap = Math.min(r.minGap, now - prev)
|
||||||
|
r.last.set(msg.address, now)
|
||||||
|
if (!this.rateTimer) {
|
||||||
|
this.rateTimer = setInterval(() => this.reportRelayRates(), RATE_REPORT_MS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reportRelayRates() {
|
||||||
|
const rates = this.relayRates
|
||||||
|
this.relayRates = null
|
||||||
|
if (!rates || !rates.size || !this.config.debugLog) {
|
||||||
|
if (this.rateTimer && !this.config.debugLog) {
|
||||||
|
clearInterval(this.rateTimer)
|
||||||
|
this.rateTimer = null
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const byIp = new Map()
|
||||||
|
for (const r of rates.values()) {
|
||||||
|
if (!byIp.has(r.ip)) byIp.set(r.ip, [])
|
||||||
|
byIp.get(r.ip).push(r)
|
||||||
|
}
|
||||||
|
for (const [ip, list] of byIp) {
|
||||||
|
const total = list.reduce((n, r) => n + r.count, 0)
|
||||||
|
const top = list
|
||||||
|
.sort((a, b) => b.count - a.count)
|
||||||
|
.slice(0, 5)
|
||||||
|
.map((r) => `${r.pattern} ${r.count} (${(r.count / (RATE_REPORT_MS / 1000)).toFixed(1)}/s${Number.isFinite(r.minGap) ? `, min gap ${r.minGap} ms` : ''})`)
|
||||||
|
this.log('info', `Relay rate ${ip}: ${total} msgs in ${RATE_REPORT_MS / 1000} s; ${top.join('; ')}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reportError(source, err) {
|
reportError(source, err) {
|
||||||
const key = `${source}|${err.code || err.message}|${err.dest || ''}`
|
const key = `${source}|${err.code || err.message}|${err.dest || ''}`
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
@@ -488,6 +540,7 @@ export default class DigicoS31Instance extends InstanceBase {
|
|||||||
if (this.config.debugLog) {
|
if (this.config.debugLog) {
|
||||||
this.learn('ipad', msg, rinfo)
|
this.learn('ipad', msg, rinfo)
|
||||||
this.log('debug', `ipad ${rinfo.address} -> console: ${describe(msg)}`)
|
this.log('debug', `ipad ${rinfo.address} -> console: ${describe(msg)}`)
|
||||||
|
this.noteRelayRate(rinfo.address, msg)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
@@ -530,7 +583,9 @@ export default class DigicoS31Instance extends InstanceBase {
|
|||||||
async disconnect() {
|
async disconnect() {
|
||||||
this.loadToken++
|
this.loadToken++
|
||||||
this.loading = false
|
this.loading = false
|
||||||
for (const t of [this.keepaliveTimer, this.txTimer, this.meterTimer]) if (t) clearInterval(t)
|
for (const t of [this.keepaliveTimer, this.txTimer, this.meterTimer, this.rateTimer]) if (t) clearInterval(t)
|
||||||
|
this.rateTimer = null
|
||||||
|
this.relayRates = null
|
||||||
for (const t of [this.linkRetryTimer, this.relayRetryTimer]) if (t) clearTimeout(t)
|
for (const t of [this.linkRetryTimer, this.relayRetryTimer]) if (t) clearTimeout(t)
|
||||||
this.keepaliveTimer = null
|
this.keepaliveTimer = null
|
||||||
this.linkRetryTimer = null
|
this.linkRetryTimer = null
|
||||||
@@ -823,7 +878,7 @@ export default class DigicoS31Instance extends InstanceBase {
|
|||||||
// ---------- fades ----------
|
// ---------- fades ----------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move a level from where it is now to `targetDb` over `durationMs`: a new level every FADE_STEP_MS (20 Hz), each
|
* Move a level from where it is now to `targetDb` over `durationMs`: a new level every FADE_STEP_MS (about 22 Hz, like the iPad app), each
|
||||||
* a normal write(), the last one exactly the target. Curves are those of `fadeLevelAt`. There is one fade per
|
* a normal write(), the last one exactly the target. Curves are those of `fadeLevelAt`. There is one fade per
|
||||||
* address: a new fade, a plain set or an adjust on the same address ends the running one, and closing the console
|
* address: a new fade, a plain set or an adjust on the same address ends the running one, and closing the console
|
||||||
* link ends them all. When the current level cannot be read, the target is written at once.
|
* link ends them all. When the current level cannot be read, the target is written at once.
|
||||||
|
|||||||
+11
-10
@@ -115,6 +115,7 @@ function makeContext() {
|
|||||||
// 0. Imports, config fields, upgrade script
|
// 0. Imports, config fields, upgrade script
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
const mainMod = await import('../src/main.js')
|
const mainMod = await import('../src/main.js')
|
||||||
|
const FADE_STEP = mainMod.FADE_STEP_MS
|
||||||
const Instance = mainMod.default
|
const Instance = mainMod.default
|
||||||
check(typeof Instance === 'function' && Array.isArray(mainMod.UpgradeScripts), 'src/main.js imports and exports a default class + UpgradeScripts')
|
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 dbs = w.map((x) => x.db)
|
||||||
const pos = dbs.map((v) => faderPosition(v))
|
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)
|
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(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(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 50 ms (ended after ${w.at(-1).at - t0} ms, median step ${gaps[Math.floor(gaps.length / 2)]} ms)`)
|
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')
|
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)
|
await sleep(80)
|
||||||
check(ctx.variables.fades_running === 0, 'fades_running reads 0 again')
|
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' })
|
await go('input_fader_set', { channel: 12, level: 0, duration: 500, curve: 'db' })
|
||||||
check(await idle(), 'the linear fade finishes')
|
check(await idle(), 'the linear fade finishes')
|
||||||
const dbs = writesTo(faderAddr(12), mark).map((x) => x.db)
|
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
|
// the curve option is optional: a button made before fades existed (no duration, no curve) is an instant set
|
||||||
const m2 = sent.length
|
const m2 = sent.length
|
||||||
await go('input_fader_set', { channel: 12, level: -10 })
|
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(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')
|
check(await idle(), 'the second fade finishes')
|
||||||
w = writesTo(faderAddr(14), markB)
|
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
|
mark = sent.length
|
||||||
await go('input_fader_set', { channel: 14, level: -90 })
|
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' })
|
await go('send_level_set', { channel: 16, buss: 2, level: -10, duration: 200, curve: 'db' })
|
||||||
check(await idle(), 'an aux send level fades')
|
check(await idle(), 'an aux send level fades')
|
||||||
const sw = writesTo('/Input_Channels/16/Aux_Send/2/send_level', mark).map((x) => x.db)
|
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('buss_fader_set', { buss: 4, level: -30 })
|
||||||
await go('matrix_fader_set', { matrix: 2, level: -30 })
|
await go('matrix_fader_set', { matrix: 2, level: -30 })
|
||||||
await go('cg_fader_set', { cg: 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' })
|
await go('matrix_send_level_set', { matrixIn: 4, matrixOut: 4, level: -90, duration: 300, curve: 'db' })
|
||||||
check(await idle(), 'a matrix send fades down')
|
check(await idle(), 'a matrix send fades down')
|
||||||
let m = writesTo(addrM, mark).map((x) => x.db)
|
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
|
mark = sent.length
|
||||||
await go('matrix_send_level_set', { matrixIn: 4, matrixOut: 4, level: 0, duration: 500, curve: 'fader' })
|
await go('matrix_send_level_set', { matrixIn: 4, matrixOut: 4, level: 0, duration: 500, curve: 'fader' })
|
||||||
check(await idle(), 'and up from -90 again')
|
check(await idle(), 'and up from -90 again')
|
||||||
m = writesTo(addrM, mark).map((x) => x.db)
|
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
|
// a fade to a target below the floor stops at -90 for a matrix send, at -150 (-inf) for a fader
|
||||||
mark = sent.length
|
mark = sent.length
|
||||||
await inst.setMatrixSendLevel(4, 4, -150, 200, 'db')
|
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(inst.fades.size === 1, 'a group fader fade is running')
|
||||||
check(await idle(), 'the group fade finishes')
|
check(await idle(), 'the group fade finishes')
|
||||||
const gw = writesTo('/Group_Outputs/25/fader', m2).map((x) => x.db)
|
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 })
|
await go('aux_fader_set', { buss: 5, level: -40 })
|
||||||
const m3 = sent.length
|
const m3 = sent.length
|
||||||
await go('aux_fader_set', { buss: 5, level: -10, duration: 200, curve: 'db' })
|
await go('aux_fader_set', { buss: 5, level: -10, duration: 200, curve: 'db' })
|
||||||
check(await idle(), 'the aux master fade finishes')
|
check(await idle(), 'the aux master fade finishes')
|
||||||
const aw = writesTo('/Aux_Outputs/5/fader', m3).map((x) => x.db)
|
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
|
void mark
|
||||||
|
|
||||||
// presets: Groups and Aux masters sections, group / aux meter sections, no "Busses" section
|
// presets: Groups and Aux masters sections, group / aux meter sections, no "Busses" section
|
||||||
|
|||||||
Reference in New Issue
Block a user