From 99be04e4c007240d1dfd262dffc1825920b51fdb Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 18 Nov 2024 13:33:25 -0800 Subject: [PATCH] feat: Implement a `sceneRecalledTrigger` variable whose value changes every time a scene is recalled, suitable for use in a scene-recalled trigger. (#69) --- companion/HELP.md | 2 ++ src/instance.js | 6 ++++-- src/midi/session.ts | 30 +++++++++++++++++++++++------- src/variables.ts | 28 ++++++++++++++++++++++------ 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/companion/HELP.md b/companion/HELP.md index 313b3689..6de54924 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -18,6 +18,7 @@ Controls the Allen & Heath SQ. ## Special Functions: - Current scene display variable +- Scene recall variable (usable in triggers to respond to scene recalls) - Current dB Fader Level display variables - Current Pan level display variables - Talkback macro @@ -35,6 +36,7 @@ New in v.2.1.0 - Add an action to make active/inactive an FX return in LR/mixes - Define pan/balance variables on-demand as pan/balance change messages are sent by the mixer - Restart module instances in response to configuration changes only if absolutely required +- Add a `sceneRecalledTrigger` variable whose value changes every time a scene is recalled, suitable for use in triggers New in v.2.0.0 (not released) diff --git a/src/instance.js b/src/instance.js index 174c39e3..70139d9e 100644 --- a/src/instance.js +++ b/src/instance.js @@ -8,7 +8,7 @@ import { getFeedbacks } from './feedbacks/feedbacks.js' import { Mixer } from './mixer/mixer.js' import { canUpdateOptionsWithoutRestarting, noConnectionOptions, optionsFromConfig } from './options.js' import { getPresets } from './presets.js' -import { getVariables } from './variables.js' +import { CurrentSceneId, getVariables, SceneRecalledTriggerId } from './variables.js' import api from './api.js' @@ -97,10 +97,12 @@ export class sqInstance extends InstanceBase { this.setVariableDefinitions(getVariables(this, model)) this.setVariableValues({ + [SceneRecalledTriggerId]: 0, + // This value may very well be wrong, but there's no defined way to // query what the current scene is, nor to be updated if it changes // and this module didn't do it. - currentScene: 1, + [CurrentSceneId]: 1, }) } diff --git a/src/midi/session.ts b/src/midi/session.ts index db9b23d4..21b66549 100644 --- a/src/midi/session.ts +++ b/src/midi/session.ts @@ -8,6 +8,7 @@ import { MixerMessageParser } from './parse/parse.js' import { MidiTokenizer } from './tokenize/tokenize.js' import { prettyByte, prettyBytes } from '../utils/pretty.js' import { asyncSleep, sleep } from '../utils/sleep.js' +import { CurrentSceneId, SceneRecalledTriggerId } from '../variables.js' /** * The port number used for MIDI-over-TCP connections to SQ mixers. @@ -129,6 +130,26 @@ export class MidiSession { }) } + /** + * React to a mixer scene being recalled. + * + * @param newScene + * The zero-indexed scene that was recalled. + */ + #sceneRecalled(newScene: number): void { + this.#mixer.currentScene = newScene + + const instance = this.#instance + const sceneRecalledTrigger = Number(instance.getVariableValue(SceneRecalledTriggerId)!) + instance.setVariableValues({ + [SceneRecalledTriggerId]: sceneRecalledTrigger + 1, + + // The currentScene variable is 1-indexed, consistent with how + // the current scene is displayed to users in mixer UI. + [CurrentSceneId]: newScene + 1, + }) + } + /** Read and process mixer reply messages from `socket`. */ async #processMixerReplies(socket: TCPHelper) { const mixer = this.#mixer @@ -144,14 +165,9 @@ export class MidiSession { const mixerChannelParser = new MixerChannelParser(verboseLog) mixerChannelParser.on('scene', (newScene: number) => { - verboseLog(`Scene changed: ${newScene}`) + verboseLog(`Scene recalled: ${newScene}`) - mixer.currentScene = newScene - instance.setVariableValues({ - // The currentScene variable is 1-indexed, consistent with how - // the current scene is displayed to users in mixer UI. - currentScene: newScene + 1, - }) + this.#sceneRecalled(newScene) }) mixerChannelParser.on('mute', (msb: number, lsb: number, vf: number) => { verboseLog(`Mute received: MSB=${prettyByte(msb)}, LSB=${prettyByte(lsb)}, VF=${prettyByte(vf)}`) diff --git a/src/variables.ts b/src/variables.ts index 5cb97058..a447165e 100644 --- a/src/variables.ts +++ b/src/variables.ts @@ -2,13 +2,29 @@ import type { CompanionVariableDefinition } from '@companion-module/base' import type { SQInstanceInterface as sqInstance } from './instance-interface.js' import type { Model } from './mixer/model.js' -export function getVariables(instance: sqInstance, model: Model): CompanionVariableDefinition[] { - const variables: CompanionVariableDefinition[] = [] +/** + * The variable ID for the variable containing the last recalled scene + * (1-indexed). + */ +export const CurrentSceneId = 'currentScene' + +/** + * The variable ID for the variable updated every time a scene is recalled + * intended for use in triggers. + */ +export const SceneRecalledTriggerId = 'sceneRecalledTrigger' - variables.push({ - name: 'Scene - Current', - variableId: 'currentScene', - }) +export function getVariables(instance: sqInstance, model: Model): CompanionVariableDefinition[] { + const variables: CompanionVariableDefinition[] = [ + { + name: 'Scene - Scene Recalled', + variableId: SceneRecalledTriggerId, + }, + { + name: 'Scene - Current', + variableId: CurrentSceneId, + }, + ] model.forEachInputChannel((channel, channelLabel) => { model.forEachMixAndLR((mix, _mixLabel, mixDesc) => {