diff --git a/backend/src/devices/yamaha-01v96/device-config.ts b/backend/src/devices/yamaha-01v96/device-config.ts index c0c5fea..5666eca 100644 --- a/backend/src/devices/yamaha-01v96/device-config.ts +++ b/backend/src/devices/yamaha-01v96/device-config.ts @@ -13,21 +13,21 @@ export const deviceConfig: DeviceConfiguration = { { key: 'value', label: 'CH' }, ...arrayRange(1, 8, it => ({ key: 'aux' + it, label: 'AUX' + it })), ], - additionalProperties: ['on'], + additionalProperties: ['on', 'paired'], }, { key: 'aux', label: 'AUX', count: 8, namePrefix: 'AUX', - additionalProperties: ['on'], + additionalProperties: ['on', 'paired'], }, { key: 'bus', label: 'BUS', count: 8, namePrefix: 'BUS', - additionalProperties: ['on'], + additionalProperties: ['on', 'paired'], }, { key: 'sum', diff --git a/backend/src/devices/yamaha-01v96/index.ts b/backend/src/devices/yamaha-01v96/index.ts index 1dc0a39..2c69a42 100644 --- a/backend/src/devices/yamaha-01v96/index.ts +++ b/backend/src/devices/yamaha-01v96/index.ts @@ -56,8 +56,11 @@ export default class Yamaha01v96DeviceController implements DeviceController { ) if (message) sendMessage(message) - if (category === 'ch' && (property === 'value' || property === 'on')) { - refreshDependentChannels(id, property) + if ( + (category === 'ch' || category === 'aux' || category === 'bus') && + (property === 'value' || property === 'on') + ) { + refreshDependentChannels(category, id, property) } } diff --git a/backend/src/devices/yamaha-01v96/mapping.ts b/backend/src/devices/yamaha-01v96/mapping.ts index 9c227ff..ec068f3 100644 --- a/backend/src/devices/yamaha-01v96/mapping.ts +++ b/backend/src/devices/yamaha-01v96/mapping.ts @@ -215,9 +215,26 @@ export const messageMapping: MessageMapping[] = [ // pairs { incoming: message => { - if (message.type !== 'kInputPair/kPair' || !message.data) return null - updateChannelPair(String(message.channel + 1), data2On(message.data)) - return true + if (!message.type?.endsWith('Pair/kPair') || !message.data) return null + + const category = message.type.startsWith('kInput') + ? 'ch' + : message.type.startsWith('kBus') + ? 'bus' + : 'aux' + + const paired = data2On(message.data) + const channelId = String(message.channel + 1) + + updateChannelPair(category, channelId, paired) + + return { + type: 'change', + category, + id: channelId, + property: 'paired', + value: paired, + } }, }, diff --git a/backend/src/devices/yamaha-01v96/pairs-groups.ts b/backend/src/devices/yamaha-01v96/pairs-groups.ts index 60ae90a..8d7ef27 100644 --- a/backend/src/devices/yamaha-01v96/pairs-groups.ts +++ b/backend/src/devices/yamaha-01v96/pairs-groups.ts @@ -4,7 +4,7 @@ import { sendMessage } from './connection' import { message } from './message' import { getRequestMessage } from './protocol' -const pairedChannels = new Set() +const pairedChannels = new Map>() const activeGroups = new Set() const channelsByGroup = new Map>() const groupsByChannel = new Map>() @@ -12,6 +12,7 @@ const groupsByChannel = new Map>() const groupTypes = ['Fader', 'Mute'] async function refreshChannels( + category: string, channelIds: Set, properties = ['value', 'on'] ) { @@ -19,7 +20,7 @@ async function refreshChannels( for (const channelId of channelIds) { for (const property of properties) { - const message = getRequestMessage('ch', channelId, property) + const message = getRequestMessage(category, channelId, property) if (message) { sendMessage(message) messageCount++ @@ -30,25 +31,21 @@ async function refreshChannels( } export async function syncPairsAndGroups(): Promise { - for (let channel = 1; channel <= 32; channel++) { - sendMessage( - message({ - type: 'kInputPair/kPair', - channel: channel - 1, - isRequest: true, - }) - ) + const categories = { + kInputPair: 32, + kBusPair: 8, + kAUXPair: 8, + } - for (let groupIndex = 1; groupIndex <= 8; groupIndex++) { - for (const groupType of groupTypes) { - sendMessage( - message({ - type: `kInputGroup/kInGroup${groupType}${groupIndex}`, - channel: channel - 1, - isRequest: true, - }) - ) - } + for (const [category, count] of Object.entries(categories)) { + for (let channel = 1; channel <= count; channel++) { + sendMessage( + message({ + type: `${category}/kPair`, + channel: channel - 1, + isRequest: true, + }) + ) } await delay(20) @@ -69,13 +66,14 @@ export async function syncPairsAndGroups(): Promise { } export async function refreshDependentChannels( + category: string, channelId: string, property?: string ): Promise { const channelsToRefresh = new Set() // pairs - if (pairedChannels.has(channelId)) { + if (pairedChannels.get(category)?.has(channelId)) { const channelIdNumber = Number(channelId) const dependentChannel = String( channelIdNumber + (channelIdNumber % 2 === 0 ? -1 : 1) @@ -102,12 +100,21 @@ export async function refreshDependentChannels( await delay(20) - await refreshChannels(channelsToRefresh, property ? [property] : undefined) + await refreshChannels( + category, + channelsToRefresh, + property ? [property] : undefined + ) } -export function updateChannelPair(channelId: string, paired: boolean): void { - if (paired) pairedChannels.add(channelId) - else pairedChannels.delete(channelId) +export function updateChannelPair( + category: string, + channelId: string, + paired: boolean +): void { + if (!pairedChannels.has(category)) pairedChannels.set(category, new Set()) + if (paired) pairedChannels.get(category)!.add(channelId) + else pairedChannels.get(category)!.delete(channelId) } export function updateGroupActive(group: string, active: boolean): void { diff --git a/frontend/src/controls/entry-control.tsx b/frontend/src/controls/entry-control.tsx index 31e25bc..54ec0da 100644 --- a/frontend/src/controls/entry-control.tsx +++ b/frontend/src/controls/entry-control.tsx @@ -51,6 +51,8 @@ export function EntryControl({ subLabel={state.name} color={state.color ?? undefined} meterRef={meterRef} + category={category} + id={id} /> ))} diff --git a/frontend/src/ui/controls/fader/fader.tsx b/frontend/src/ui/controls/fader/fader.tsx index a04d2fb..d0ae976 100644 --- a/frontend/src/ui/controls/fader/fader.tsx +++ b/frontend/src/ui/controls/fader/fader.tsx @@ -8,6 +8,7 @@ import { useDelayedState } from '../../../hooks/delayed-state' import { FaderBase } from './fader-base' import { FaderButton } from './fader-button' +import { useEntryState } from '../../../api/state' export interface FaderProps { value: number @@ -19,9 +20,29 @@ export interface FaderProps { color?: string meterRef?: React.RefObject onChange: (value: number) => void - className?: string + category: string + id: string } +const pairColors = [ + '#e6194B', + '#3cb44b', + '#ffe119', + '#4363d8', + '#f58231', + '#911eb4', + '#42d4f4', + '#f032e6', + '#bfef45', + '#fabed4', + '#469990', + '#dcbeff', + '#9A6324', + '#fffac8', + '#800000', + '#aaffc3', +] + export const Fader = ({ value, min = 0, @@ -31,19 +52,25 @@ export const Fader = ({ subLabel, color, onChange, - ...passThrough + meterRef, + category, + id, }: FaderProps) => { const [localValue, setLocalValue] = useDelayedState(null) const valueToUse = localValue ?? value + const state = useEntryState(category, id) + + let pairColor = color + if (state?.paired) { + const pairIndex = Math.floor((parseInt(id) - 1) / 2) + pairColor = pairColors[pairIndex % pairColors.length] + } return ( { const newRawValue = fractionToValue(fraction, min, max) - if (newRawValue === valueToUse) { - return - } setLocalValue(newRawValue) const roundedValue = roundToStep(newRawValue, step) onChange(roundedValue) @@ -54,7 +81,7 @@ export const Fader = ({ fraction={valueToFraction(valueToUse, min, max)} label={label} subLabel={subLabel} - color={color} + color={pairColor} /> )