Skip to content

Commit

Permalink
feat: Studer OnAir support
Browse files Browse the repository at this point in the history
  • Loading branch information
olzzon authored and olzzon committed Mar 25, 2020
1 parent b7ca75f commit 02acbbe
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 8 deletions.
4 changes: 2 additions & 2 deletions server/constants/MixerProtocolPresets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface IMessageProtocol {
value: any,
type: string
}
import { StuderVistaMaster } from './mixerProtocols/StuderVistaEmber';
import { StuderOnAirMaster } from './mixerProtocols/StuderOnAirEmber';
// Interface:
import { IMixerProtocolGeneric} from './MixerProtocolInterface'

Expand All @@ -32,7 +32,7 @@ export const MixerProtocolPresets: { [key: string]: IMixerProtocolGeneric } = Ob
dmxis: DMXIS,
yamahaQlCl: YamahaQLCL,
sslSystemT: SSLSystemT,
studerVistaMaster: StuderVistaMaster,
studerOnAirMaster: StuderOnAirMaster,
}, {
casparCGMaster: CasparCGMaster
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { IMixerProtocol, emptyMixerMessage } from '../MixerProtocolInterface';

export const StuderVistaMaster: IMixerProtocol = {
protocol: 'STUDER',
label: 'Studer Vista - NOT WORKING',
export const StuderOnAirMaster: IMixerProtocol = {
protocol: 'STUDER_ONAIR',
label: 'Studer OnAir 3000',
mode: "master",
FADE_DISPATCH_RESOLUTION: 50,
leadingZeros: false, //some OSC protocols needs channels to be 01, 02 etc.
Expand Down
6 changes: 3 additions & 3 deletions server/utils/MixerConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { MidiMixerConnection } from './mixerConnections/MidiMixerConnection';
import { QlClMixerConnection } from './mixerConnections/YamahaQlClConnection';
import { SSLMixerConnection } from './mixerConnections/SSLMixerConnection';
import { EmberMixerConnection } from './mixerConnections/EmberMixerConnection';
import { StuderMixerConnection } from './mixerConnections/StuderMixerConnection';
import { StuderOnAirMixerConnection } from './mixerConnections/StuderOnAirMixerConnection';
import { CasparCGConnection } from './mixerConnections/CasparCGConnection';
import { IChannel } from '../reducers/channelsReducer';
import { SET_OUTPUT_LEVEL, FADE_ACTIVE } from '../reducers/channelActions'
Expand Down Expand Up @@ -47,8 +47,8 @@ export class MixerGenericConnection {
this.mixerConnection = new CasparCGConnection(this.mixerProtocol as ICasparCGMixerGeometry);
} else if (this.mixerProtocol.protocol === 'EMBER') {
this.mixerConnection = new EmberMixerConnection(this.mixerProtocol as IMixerProtocol);
} else if (this.mixerProtocol.protocol === 'STUDER') {
this.mixerConnection = new StuderMixerConnection(this.mixerProtocol as IMixerProtocol);
} else if (this.mixerProtocol.protocol === 'STUDER_ONAIR') {
this.mixerConnection = new StuderOnAirMixerConnection(this.mixerProtocol as IMixerProtocol);
} else if (this.mixerProtocol.protocol === 'SSL') {
this.mixerConnection = new SSLMixerConnection(this.mixerProtocol as IMixerProtocol);
}
Expand Down
339 changes: 339 additions & 0 deletions server/utils/mixerConnections/StuderOnAirMixerConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,339 @@
//@ts-ignore
import { EmberClient } from 'node-emberplus'
import { store, state } from '../../reducers/store'
import { huiRemoteConnection } from '../../mainClasses'

//Utils:
import { IMixerProtocol } from '../../constants/MixerProtocolInterface';
import {
SET_FADER_LEVEL,
SET_CHANNEL_LABEL
} from '../../reducers/faderActions'
import { logger } from '../logger';


export class StuderOnAirMixerConnection {
mixerProtocol: IMixerProtocol
emberConnection: EmberClient
deviceRoot: any;
emberNodeObject: Array<any>;


constructor(mixerProtocol: IMixerProtocol) {
this.sendOutMessage = this.sendOutMessage.bind(this);
this.pingMixerCommand = this.pingMixerCommand.bind(this);

this.emberNodeObject = new Array(200);
this.mixerProtocol = mixerProtocol;

logger.info("Setting up Ember connection")
this.emberConnection = new EmberClient(
state.settings[0].deviceIp,
state.settings[0].devicePort
);

this.emberConnection.on('error', (error: any) => {
if (
(error.message + '').match(/econnrefused/i) ||
(error.message + '').match(/disconnected/i)
) {
logger.error('Ember connection not establised')
} else {
logger.error('Ember connection unknown error' + error.message)
}
})
this.emberConnection.on('disconnected', () => {
logger.error('Lost Ember connection')
})
logger.info('Connecting to Ember')
let deviceRoot: any;
this.emberConnection.connect()
.then(() => {
this.setupMixerConnection();
})
.catch((e: any) => {
console.log(e.stack);
});
}

setupMixerConnection() {
logger.info('Ember connection established - setting up subscription of channels')

let ch: number = 1;
state.settings[0].numberOfChannelsInType.forEach((numberOfChannels, typeIndex) => {
for (let channelTypeIndex=0; channelTypeIndex < numberOfChannels ; channelTypeIndex++) {
this.subscribeFaderLevel(ch, typeIndex, channelTypeIndex);
ch++;
}
})
/*
.CHANNEL_VU)){
store.dispatch({
type:SET_VU_LEVEL,
channel: ch - 1,
level: message.args[0]
});
*/


//Ping OSC mixer if mixerProtocol needs it.
if (this.mixerProtocol.pingTime > 0) {
let emberTimer = setInterval(
() => {
this.pingMixerCommand();
},
this.mixerProtocol.pingTime
);
}
}

subscribeFaderLevel(ch: number, typeIndex: number, channelTypeIndex: number) {
let command = this.mixerProtocol.channelTypes[typeIndex].fromMixer.CHANNEL_OUT_GAIN[0].mixerMessage.replace("{channel}", String(channelTypeIndex+1))
this.emberConnection.getElementByPath(command)
.then((node: any) => {
logger.info('Subscription of channel : ' + command)
this.emberNodeObject[ch-1] = node;
this.emberConnection.subscribe(node, (() => {
logger.verbose('Receiving Level from Ch ' + String(ch))
if (!state.channels[0].channel[ch-1].fadeActive
&& !state.channels[0].channel[ch - 1].fadeActive
&& node.contents.value > this.mixerProtocol.channelTypes[typeIndex].fromMixer.CHANNEL_OUT_GAIN[0].min) {
store.dispatch({
type: SET_FADER_LEVEL,
channel: ch-1,
level: node.contents.value
});
global.mainThreadHandler.updatePartialStore(ch-1)
if (huiRemoteConnection) {
huiRemoteConnection.updateRemoteFaderState(ch-1, node.contents.value);
}
}

})
)
})
.catch((error: any) => {
logger.error(error)
})

}

subscribeChannelName(ch: number, typeIndex: number, channelTypeIndex: number) {
this.emberConnection.getNodeByPath(this.mixerProtocol.channelTypes[typeIndex].fromMixer.CHANNEL_NAME[0].mixerMessage.replace("{channel}", String(channelTypeIndex+1)))
.then((node: any) => {
this.emberConnection.subscribe(node, (() => {
store.dispatch({
type: SET_CHANNEL_LABEL,
channel: ch-1,
level: node.contents.value
});
})
)
})
}

pingMixerCommand() {
//Ping Ember mixer if mixerProtocol needs it.
return;
this.mixerProtocol.pingCommand.map((command) => {
this.sendOutMessage(
command.mixerMessage,
0,
command.value,
command.type
);
});
}

sendOutMessage(mixerMessage: string, channel: number, value: string | number, type: string) {
let channelString = this.mixerProtocol.leadingZeros ? ("0"+channel).slice(-2) : channel.toString();

let message = mixerMessage.replace(
"{channel}",
channelString
)

/*
this.emberConnection.getElementByPath(message)
.then((element: any) => {
logger.verbose('Sending out message : ' + message)
this.emberConnection.setValue(
this.emberNodeObject[channel-1],
typeof value === 'number' ? value : parseFloat(value)
)
})
.catch((error: any) => {
console.log("Ember Error ", error)
})
*/
}

sendOutLevelMessage(channel: number, value: number) {
// logger.verbose('Sending out Level: ' + String(value) + ' To Path : ' + JSON.stringify(this.emberConnection.root))
let levelMessage: string
if (channel<10) {
levelMessage = '7f 8f ff fe d9 5c 80 30 80 a4 18 31 16 a2 14 31 12 {channel} 10 31 0e a6 0c 31 0a e1 08 31 06 63 04 02 02 {level} 00 00 00 00'
} else {
levelMessage = '7f 8f ff fe d9 5c 80 30 80 bf 83 90 80 00 1e 31 1c a4 1a 31 18 a2 16 31 14 {channel} 12 31 10 a6 0e 31 0c e1 0a 31 08 63 06 02 04 00 00 {level} 00 00 00 00'
}

let valueNumber = value
let valueByte = new Uint8Array([
(valueNumber & 0x0000ff00) >> 8,
(valueNumber & 0x000000ff),
])
let channelVal = 160 + channel
let channelByte = new Uint8Array([
(channelVal & 0x000000ff),
])

levelMessage = levelMessage.replace('{channel}', ('0' + channelByte[0].toString(16)).slice(-2))
levelMessage = levelMessage.replace('{level}', ('0' + valueByte[0].toString(16)).slice(-2) + ' ' + ('0' + valueByte[1].toString(16)).slice(-2))

let hexArray = levelMessage.split(' ')
let buf = new Buffer(hexArray.map((val:string) => { return parseInt(val, 16) }))
this.emberConnection._client.socket.write(buf)
console.log("Send HEX: " + levelMessage)

/*
7f 8f ff fe d9 5c 80 30 80 a4 18 31 16 a2 14 31 12 {CH} 10 31 0e a6 0c 31 0a e1 08 31 06 63 04 02 02 {LV} 00 00 00 00
Fader 1 - Maxvol:
7f 8f ff fe d9 5c 80 30 80 a4 18 31 16 a2 14 31 12 {a1} 10 31 0e a6 0c 31 0a e1 08 31 06 63 04 02 02 03 ca 00 00 00 00
7f 8f ff fe d9 5c 80 30 80 a4 18 31 16 a2 14 31 12 {a1} 10 31 0e a6 0c 31 0a e1 08 31 06 63 04 02 02 03 e8 00 00 00 00
Fader 1 - Min vol:
7f 8f ff fe d9 5c 80 30 80 a4 18 31 16 a2 14 31 12 {a1} 10 31 0e a6 0c 31 0a e1 08 31 06 63 04 02 02 00 be 00 00 00 00
Fader 2 - Min vol:
7f 8f ff fe d9 5c 80 30 80 a4 18 31 16 a2 14 31 12 {a2} 10 31 0e a6 0c 31 0a e1 08 31 06 63 04 02 02 03 48 00 00 00 00
Fader 2 - Max vol:
7f 8f ff fe d9 5c 80 30 80 a4 18 31 16 a2 14 31 12 {a2} 10 31 0e a6 0c 31 0a e1 08 31 06 63 04 02 02 03 e8 00 00 00 00
Fader 7:
7f 8f ff fe d9 5c 80 30 80 a4 18 31 16 a2 14 31 12 {a9} 10 31 0e a6 0c 31 0a e1 08 31 06 63 04 02 02 02 12 00 00 00 00
Fader 12:
7f 8f ff fe d9 5c 80 30 80 bf 83 90 80 00 1e 31 1c a4 1a 31 18 a2 16 31 14 {ac} 12 31 10 a6 0e 31 0c e1 0a 31 08 63 06 02 04 00 00 "00 c8" 00 00 00 00
Fader 23:
7f 8f ff fe d9 5c 80 30 80 bf 83 90 80 00 1e 31 1c a4 1a 31 18 a2 16 31 14 {b7} 12 31 10 a6 0e 31 0c e1 0a 31 08 63 06 02 04 00 00 "02 94" 00 00 00 00
*/
}

sendOutRequest(mixerMessage: string, channel: number) {
let channelString = this.mixerProtocol.leadingZeros ? ("0"+channel).slice(-2) : channel.toString();
let message = mixerMessage.replace(
"{channel}",
channelString
);
if (message != 'none') {
/*
this.oscConnection.send({
address: message
});
*/
}
}

updateOutLevel(channelIndex: number) {
let channelType = state.channels[0].channel[channelIndex].channelType;
let channelTypeIndex = state.channels[0].channel[channelIndex].channelTypeIndex;
let protocol = this.mixerProtocol.channelTypes[channelType].toMixer.CHANNEL_OUT_GAIN[0]
let level = (state.channels[0].channel[channelIndex].outputLevel - protocol.min) * (protocol.max - protocol.min)
this.sendOutLevelMessage(
channelTypeIndex+1,
level,
);
}

updateFadeIOLevel(channelIndex: number, outputLevel: number) {
let channelType = state.channels[0].channel[channelIndex].channelType;
let channelTypeIndex = state.channels[0].channel[channelIndex].channelTypeIndex;
let protocol = this.mixerProtocol.channelTypes[channelType].toMixer.CHANNEL_OUT_GAIN[0]
let level = (outputLevel - protocol.min) * (protocol.max - protocol.min)

this.sendOutLevelMessage(
channelTypeIndex+1,
level
)
}

updatePflState(channelIndex: number) {
let channelType = state.channels[0].channel[channelIndex].channelType;
let channelTypeIndex = state.channels[0].channel[channelIndex].channelTypeIndex;

if (state.faders[0].fader[channelIndex].pflOn === true) {
this.sendOutMessage(
this.mixerProtocol.channelTypes[channelType].toMixer.PFL_ON[0].mixerMessage,
channelTypeIndex+1,
this.mixerProtocol.channelTypes[channelType].toMixer.PFL_ON[0].value,
this.mixerProtocol.channelTypes[channelType].toMixer.PFL_ON[0].type
);
} else {
this.sendOutMessage(
this.mixerProtocol.channelTypes[channelType].toMixer.PFL_OFF[0].mixerMessage,
channelTypeIndex+1,
this.mixerProtocol.channelTypes[channelType].toMixer.PFL_OFF[0].value,
this.mixerProtocol.channelTypes[channelType].toMixer.PFL_OFF[0].type
);
}
}

updateMuteState(channelIndex: number, muteOn: boolean) {
return true
}

updateNextAux(channelIndex: number, level: number) {
return true
}


updateThreshold(channelIndex: number, level: number) {
return true
}
updateRatio(channelIndex: number, level: number) {
return true

}
updateDelayTime(channelIndex: number, level: number) {
return true
}
updateLow(channelIndex: number, level: number) {
return true
}
updateLoMid(channelIndex: number, level: number) {
return true
}
updateMid(channelIndex: number, level: number) {
return true
}
updateHigh(channelIndex: number, level: number) {
return true
}
updateAuxLevel(channelIndex: number, auxSendIndex: number, level: number) {
return true
}

updateChannelName(channelIndex: number) {
let channelType = state.channels[0].channel[channelIndex].channelType;
let channelTypeIndex = state.channels[0].channel[channelIndex].channelTypeIndex;
let channelName = state.faders[0].fader[channelIndex].label;
this.sendOutMessage(
this.mixerProtocol.channelTypes[channelType].toMixer.CHANNEL_NAME[0].mixerMessage,
channelTypeIndex+1,
channelName,
"string"
);
}

injectCommand(command: string[]) {
return true
}

}

0 comments on commit 02acbbe

Please sign in to comment.