From 5ed83643e250f544cbf8c5323a746d0516ea6b31 Mon Sep 17 00:00:00 2001 From: olzzon Date: Fri, 31 May 2019 14:05:22 +0200 Subject: [PATCH] feat: RemoteFader support - preparing support --- src/components/App.tsx | 3 + src/constants/RemoteFaderPresets.ts | 123 ++++++++++++++++++++++++ src/utils/MidiRemoteConnection.ts | 142 ++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 src/constants/RemoteFaderPresets.ts create mode 100644 src/utils/MidiRemoteConnection.ts diff --git a/src/components/App.tsx b/src/components/App.tsx index bc7570fe..9d9262ce 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -10,11 +10,13 @@ import Settings from './Settings'; import { loadSnapshotState, saveSnapshotState } from '../utils/SettingsStorage'; import { MixerConnection } from '../utils/MixerConnection'; import { AutomationConnection } from '../utils/AutomationConnection'; +import { MidiRemoteConnection } from '../utils/MidiRemoteConnection'; class App extends Component { mixerConnection: any; automationConnection: any; + midiRemoteConnection: any; constructor(props: any) { super(props) @@ -23,6 +25,7 @@ class App extends Component { componentWillMount() { this.mixerConnection = new MixerConnection(this.props.store); this.automationConnection = new AutomationConnection(this.mixerConnection); + this.midiRemoteConnection = new MidiRemoteConnection(this.mixerConnection); this.snapShopStoreTimer(); loadSnapshotState(this.props.store.channels[0], this.props.store.settings[0].numberOfChannels); } diff --git a/src/constants/RemoteFaderPresets.ts b/src/constants/RemoteFaderPresets.ts new file mode 100644 index 00000000..6b070870 --- /dev/null +++ b/src/constants/RemoteFaderPresets.ts @@ -0,0 +1,123 @@ +//While developing mixer specific settings will be in one file. +//At first release these will be in seperate files +//So it´s easy to add new equipment. + +export interface IRemoteProtocol { + protocol: string, + label: string, + mode: string, + leadingZeros: boolean, + initializeCommands: [ + { + oscMessage: string, + value: string, + type: string + } + ], + fromRemote: { + CHANNEL_PGM_ON_OFF: string, + CHANNEL_PST_ON_OFF: string, + CHANNEL_FADER_LEVEL: string, + CHANNEL_VISIBLE: string, + GRP_FADER_PGM_ON_OFF: string, + GRP_FADER_PST_ON_OFF: string, + GRP_FADER_LEVEL: string, + GRP_FADER_VISIBLE: string, + X_MIX: string, + FADE_TO_BLACK: string, + SNAP_RECALL: string, + STATE_CHANNEL_PGM: string, + STATE_CHANNEL_PST: string, + STATE_CHANNEL_FADER_LEVEL: string, + STATE_GRP_FADER_PGM: string, + STATE_GRP_FADER_PST: string, + STATE_GRP_FADER_LEVEL: string, + }, + toRemote: { + STATE_CHANNEL_PGM: string, + STATE_CHANNEL_PST: string, + STATE_CHANNEL_FADER_LEVEL: string, + STATE_GRP_FADER_PGM: string, + STATE_GRP_FADER_PST: string, + STATE_GRP_FADER_LEVEL: string, + }, + fader: { + min: number, + max: number, + zero: number, + step: number, + fadeTime: number, + }, + meter: { + min: number, + max: number, + zero: number, + test: number, + }, +} + + +export const RemoteFaderPresets: { [key: string]: IRemoteProtocol } = { + + hui: { + protocol: 'MIDI', + label: 'HUI Midicontroller', + mode: "client", + leadingZeros: true, + initializeCommands: [ + { + oscMessage: "/info", + value: "", + type: "f" + } + ], + fromRemote: { + CHANNEL_PGM_ON_OFF: '/ch/{value1}/mix/pgm', + CHANNEL_PST_ON_OFF: '/ch/{value1}/mix/pst', + CHANNEL_FADER_LEVEL: '/ch/{value1}/mix/faderlevel', + CHANNEL_VISIBLE: '/ch/{value1}/visible', + GRP_FADER_PGM_ON_OFF: '/grp/{value1}/pgm', + GRP_FADER_PST_ON_OFF: '/grp/{value1}/pst', + GRP_FADER_LEVEL: '/grp/{value1}/faderlevel', + GRP_FADER_VISIBLE: '/grp/{value1}/visible', + X_MIX: '/take', + FADE_TO_BLACK: '/fadetoblack', + SNAP_RECALL: '/snap/{value1}', + STATE_CHANNEL_PGM: '/state/ch/{value1}/mix/pgm', + STATE_CHANNEL_PST: '/state/ch/{value1}/mix/pst', + STATE_CHANNEL_FADER_LEVEL: '/state/ch/{value1}/mix/faderlevel', + STATE_GRP_FADER_PGM: '/state/grp/{value1}/pgm', + STATE_GRP_FADER_PST: '/state/grp/{value1}/pst', + STATE_GRP_FADER_LEVEL: '/state/grp/{value1}/faderlevel', + }, + toRemote: { + STATE_CHANNEL_PGM: '/state/ch/{value1}/mix/pgm', + STATE_CHANNEL_PST: '/state/ch/{value1}/mix/pst', + STATE_CHANNEL_FADER_LEVEL: '/state/ch/{value1}/mix/faderlevel', + STATE_GRP_FADER_PGM: '/state/grp/{value1}/pgm', + STATE_GRP_FADER_PST: '/state/grp/{value1}/pst', + STATE_GRP_FADER_LEVEL: '/state/grp/{value1}/faderlevel', + }, + fader: { + min: 0, + max: 1, + zero: 0.75, + step: 0.01, + fadeTime: 40, + }, + meter: { + min: 0, + max: 1, + zero: 0.75, + test: 0.6, + }, + } +}; + + +export const RemoteFaderProtocolList = Object.getOwnPropertyNames(RemoteFaderPresets).map((preset) => { + return { + value: preset, + label: RemoteFaderPresets[preset].label + }; +}); diff --git a/src/utils/MidiRemoteConnection.ts b/src/utils/MidiRemoteConnection.ts new file mode 100644 index 00000000..c9aa4ee0 --- /dev/null +++ b/src/utils/MidiRemoteConnection.ts @@ -0,0 +1,142 @@ +//Node Modules: +import os from 'os'; // Used to display (log) network addresses on local machine +import WebMidi, { INoteParam, IMidiChannel } from 'webmidi'; + +//Utils: +import { IRemoteProtocol, RemoteFaderPresets } from '../constants/RemoteFaderPresets'; + +export class MidiRemoteConnection { + store: any; + mixerConnection: any; + remoteProtocol: IRemoteProtocol; + midiInput: any; + midiOutput:any; + + constructor() { + this.sendOutMessage = this.sendOutMessage.bind(this); + + this.store = window.storeRedux.getState(); + const unsubscribe = window.storeRedux.subscribe(() => { + this.store = window.storeRedux.getState(); + }); + + this.remoteProtocol = RemoteFaderPresets[this.store.settings[0].mixerProtocol] || RemoteFaderPresets.hui; + + + WebMidi.enable((err) => { + + if (err) { + console.log("WebMidi could not be enabled.", err); + } + + // Viewing available inputs and outputs + console.log("Midi inputs : ", WebMidi.inputs); + console.log("Midi outputs : ", WebMidi.outputs); + + // Display the current time + console.log("Midi time : ", WebMidi.time); + + this.midiInput = WebMidi.getInputByName("IAC-driver ProducersMixer"); + this.midiOutput = WebMidi.getOutputByName("IAC-driver ProducersMixer"); + + this.setupMixerConnection(); + }); + } + + setupMixerConnection() { + this.midiInput.addListener('controlchange', this.remoteProtocol.fromRemote.CHANNEL_FADER_LEVEL, + (error: any) => { + console.log("Received 'controlchange' message (" + error.data + ")."); + window.storeRedux.dispatch({ + type:'SET_FADER_LEVEL', + channel: error.channel - 1, + level: error.data[2] + }); + if (this.store.channels[0].channel[error.channel - 1].pgmOn && this.remoteProtocol.mode === 'master') + { + this.updateOutLevel(error.channel-1); + } + } + ); + this.midiInput.addListener('noteon', "all", + (error: any) => { + console.log("Received 'noteon' message (" + error.note.name + error.note.octave + ")."); + } + ); +/* + if ( + this.checkOscCommand(message.address, this.mixerProtocol.fromMixer.CHANNEL_VU) + ) { + if (this.store.settings[0].mixerProtocol === 'behringer') { + behringerMeter(message.args); + } else { + let ch = message.address.split("/")[2]; + window.storeRedux.dispatch({ + type:'SET_VU_LEVEL', + channel: ch - 1, + level: message.args[0] + }); + } + } + if ( + this.checkOscCommand(message.address, this.mixerProtocol.fromMixer.CHANNEL_NAME) + ) { + let ch = message.address.split("/")[2]; + window.storeRedux.dispatch({ + type:'SET_CHANNEL_LABEL', + channel: ch - 1, + label: message.args[0] + }); + console.log("OSC message: ", message.address); + } +*/ + } + + sendOutMessage(CtrlMessage: string, channel: number, value: string) { + this.midiOutput.sendControlChange(CtrlMessage, value, channel); + } + + updateOutLevel(channelIndex: number) { + if (this.remoteProtocol.mode === "master" && this.store.channels[0].channel[channelIndex].pgmOn) { + window.storeRedux.dispatch({ + type:'SET_OUTPUT_LEVEL', + channel: channelIndex, + level: this.store.channels[0].channel[channelIndex].faderLevel + }); + } + + this.sendOutMessage( + this.remoteProtocol.toRemote.STATE_CHANNEL_FADER_LEVEL, + channelIndex+1, + this.store.channels[0].channel[channelIndex].faderLevel + ); + } + + updatePflState(channelIndex: number) { + + if (this.store.channels[0].channel[channelIndex].pflOn = true) { + this.sendOutMessage( + this.remoteProtocol.toRemote.PFL_ON.oscMessage, + channelIndex+1, + this.remoteProtocol.toRemote.PFL_ON.value + ); + } else { + this.sendOutMessage( + this.remoteProtocol.toRemote.PFL_OFF.oscMessage, + channelIndex+1, + this.remoteProtocol.toRemote.PFL_OFF.value + ); + } + } + + + updateFadeIOLevel(channelIndex: number, outputLevel: number) { + this.sendOutMessage( + this.remoteProtocol.toRemote.STATE_CHANNEL_FADER_LEVEL, + channelIndex+1, + String(outputLevel) + ); + } + +} +