-
Notifications
You must be signed in to change notification settings - Fork 8
Added Assignment updates/ variables #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ import { | |
| AssignNRPNCalculator, | ||
| BalanceNRPNCalculator, | ||
| forEachSourceSinkLevel, | ||
| forEachSourceSinkAssign, | ||
| LevelNRPNCalculator, | ||
| type SourceForSourceInMixAndLRForNRPN, | ||
| type SourceSinkForNRPN, | ||
|
|
@@ -308,6 +309,10 @@ export class Mixer { | |
| forEachSourceSinkLevel(model, getLevel) | ||
| forEachOutputLevel(model, getLevel) | ||
|
|
||
| // Request assign NRPNs as well so assignment variables are populated at startup | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dislike the system of variables that only exist sometimes, and others you will into existence as I think this is better served by making these extra variables, that are only added/created as assign NRPN messages are received -- similar to what's done for pan/balance changes. My guess is that dynamic changes to assignments are closer to "as unlikely as pan/balance changes" than to "as likely as level or muting changes", and therefore defining these as extra variables is more sensible than defining them as variables that are always present. ...which, ugh, I guess means |
||
| const getAssign = (nrpn: NRPN<'assign'>) => buff.push(this.getNRPNValue(nrpn)) | ||
| forEachSourceSinkAssign(model, getAssign) | ||
|
|
||
| const delayStatusRetrieval = instance.config.retrieveStatusAtStartup === RetrieveStatusAtStartup.Delayed | ||
|
|
||
| if (buff.length > 0 && this.#socket !== null) { | ||
|
|
@@ -423,6 +428,14 @@ export class Mixer { | |
| const variableValue = vcvfToReadablePanBalance(vc, vf) | ||
| instance.setExtraVariable(variableId, name, variableValue) | ||
| }) | ||
| mixerChannelParser.on('assign', (nrpn: NRPN<'assign'>, vc: number, vf: number) => { | ||
| verboseLog(`Assign received: ${prettyNRPN(nrpn)}, VC=${prettyByte(vc)}, VF=${prettyByte(vf)}`) | ||
|
|
||
| const { MSB, LSB } = splitNRPN(nrpn) | ||
| const variableId = `assign_${MSB}.${LSB}` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At some point these variables -- or some other storage mechanism that makes use of variables' new (as of the 2.0 module API) ability to store any JSON value -- need to not directly incorporate NRPN or MSB/LSB, but instead should be like But the hard reality is that redoing all the variable naming/structuring will break every user anyway when it happens. And I don't yet have a way to work backwards from NRPN to the particular relationship it refers to. So I guess just doubling down on the existing approach is acceptable. |
||
| // Use '1'/'0' string values for Companion variables | ||
| instance.setVariableValues({ [variableId]: vf !== 0 ? '1' : '0' }) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assignment being exposed as |
||
| }) | ||
|
|
||
| return parseMidi(instance.config.midiChannel, verboseLog, tokenizer, mixerChannelParser) | ||
| } | ||
|
|
@@ -583,6 +596,8 @@ export class Mixer { | |
| */ | ||
| #assignToSink(source: number, active: boolean, sink: number, calc: AssignNRPNCalculator): NRPNDataMessage { | ||
| const nrpn = calc.calculate(source, sink) | ||
| const { MSB, LSB } = splitNRPN(nrpn) | ||
| this.#instance.setVariableValues({ [`assign_${MSB}.${LSB}`]: active ? '1' : '0' }) | ||
| return this.#nrpnData(nrpn, 0, active ? 1 : 0) | ||
| } | ||
|
|
||
|
|
@@ -608,16 +623,15 @@ export class Mixer { | |
| active: boolean, | ||
| mixes: readonly MixOrLR[], | ||
| ): void { | ||
| const mixNrpn = AssignNRPNCalculator.get(this.model, [sourceType, 'mix']) | ||
| const lrNrpn = AssignNRPNCalculator.get(this.model, [sourceType, 'lr']) | ||
| const mixNrpnCalc = AssignNRPNCalculator.get(this.model, [sourceType, 'mix']) | ||
| const lrNrpnCalc = AssignNRPNCalculator.get(this.model, [sourceType, 'lr']) | ||
|
|
||
| const commands = mixes.map((mixOrLR) => { | ||
| const commands: readonly number[][] = mixes.map((mixOrLR) => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're changing this at all (it shouldn't be necessary to change anything here), make it |
||
| return mixOrLR === LR | ||
| ? this.#assignToSink(source, active, 0, lrNrpn) | ||
| : this.#assignToSink(source, active, mixOrLR, mixNrpn) | ||
| ? this.#assignToSink(source, active, 0, lrNrpnCalc) | ||
| : this.#assignToSink(source, active, mixOrLR, mixNrpnCalc) | ||
| }) | ||
|
|
||
| // XXX | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please leave this as-is, as noted in the subsequent comment. |
||
| void this.sendCommands(commands) | ||
| } | ||
|
|
||
|
|
@@ -646,13 +660,10 @@ export class Mixer { | |
| sinks: readonly number[], | ||
| sourceSink: SourceSinkForNRPN<'assign'>, | ||
| ): void { | ||
| const nrpn = AssignNRPNCalculator.get(this.model, sourceSink) | ||
| const calc = AssignNRPNCalculator.get(this.model, sourceSink) | ||
|
|
||
| const commands = sinks.map((sink) => { | ||
| return this.#assignToSink(source, active, sink, nrpn) | ||
| }) | ||
| const commands = sinks.map((sink) => this.#assignToSink(source, active, sink, calc)) | ||
|
|
||
| // XXX | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please leave this as-is -- it's alluding to how the promise returned here is dropped on the floor and if it rejects, that rejection goes entirely unhandled, a problem I haven't really figured out how to properly solve yet. |
||
| void this.sendCommands(commands) | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -389,3 +389,27 @@ export function forEachSourceSinkLevel(model: Model, f: SourceSinkLevelFunctor): | |
| } | ||
| } | ||
| } | ||
|
|
||
| type SourceSinkAssignFunctor = (nrpn: NRPN<'assign'>, sourceDesc: string, sinkDesc: string) => void | ||
|
|
||
| /** | ||
| * For each source-sink relationship with assign parameters, invoke the given | ||
| * function. | ||
| */ | ||
| export function forEachSourceSinkAssign(model: Model, f: SourceSinkAssignFunctor): void { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is sensible-ish enough and all, but it's also avoidable copying. 🙂 I landed a rev generalizing the original function -- please rebase this atop that and make use of the new functionality. ...or wait, see later comments, I don't think you'll need to do this. |
||
| for (const [sourceType, sinks] of Object.entries(SourceToSinkParameterBase)) { | ||
| for (const sinkType of Object.entries(sinks).flatMap(([sinkType, params]) => { | ||
| return 'assign' in params ? sinkType : [] | ||
| })) { | ||
| const sourceSink = [sourceType, sinkType] as SourceSinkForNRPN<'assign'> | ||
|
|
||
| const calc = AssignNRPNCalculator.get(model, sourceSink) | ||
| model.forEach(sourceSink[0], (source, _sourceLabel, sourceDesc) => { | ||
| model.forEach(sourceSink[1], (sink, _sinkLabel, sinkDesc) => { | ||
| const nrpn = calc.calculate(source, sink) | ||
| f(nrpn, sourceDesc, sinkDesc) | ||
| }) | ||
| }) | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the upper bound
0x6ehere, please -- the protocol docs go from60 00to6E 6E, and we ought not pass along info for NRPNs outside the actual range. (Tho if distant memory of this code serves, nothing is actually going to go wrong, exactly, for nonexistent NRPNs here.)