add global try-catch

This commit is contained in:
Rik Berkelder 2026-01-29 02:04:17 +01:00
parent 27e755cbc2
commit 824fd815af

View file

@ -2,398 +2,403 @@ import * as midi from 'easymidi';
import osc from 'osc';
console.log('inputs', midi.getInputs())
console.log('outputs', midi.getOutputs())
try {
console.log('inputs', midi.getInputs())
console.log('outputs', midi.getOutputs())
function mapNumber(value: number, fromMin: number, fromMax: number, toMin: number, toMax: number): number {
if (value <= fromMin) return toMin;
if (value >= fromMax) return toMax;
function mapNumber(value: number, fromMin: number, fromMax: number, toMin: number, toMax: number): number {
if (value <= fromMin) return toMin;
if (value >= fromMax) return toMax;
const absFromMax = fromMax - fromMin;
const absToMax = toMax - toMin;
const absFromMax = fromMax - fromMin;
const absToMax = toMax - toMin;
const mapped = (value / absFromMax) * absToMax;
return mapped + toMin;
}
enum MessageType {
NoteOn = 'noteon',
NoteOff = 'noteoff',
Pitch = 'pitch'
}
type DataLookup = {
[MessageType.NoteOn]: midi.Note,
[MessageType.NoteOff]: midi.Note,
[MessageType.Pitch]: midi.Pitch
}
const defaultData = {
[MessageType.NoteOn]: { channel: 0, note: 0, velocity: 0 },
[MessageType.NoteOff]: { channel: 0, note: 0, velocity: 0 },
[MessageType.Pitch]: { channel: 0, value: 0 }
}
interface IMidiControlOptions {
feedbackInput: boolean,
noPageFeedback: boolean,
noStoreInput: boolean
}
class MidiControl<T extends MessageType> {
public values: { [page: number]: DataLookup[T] } = [];
private outputs: { [page: number]: (data: any) => void } = [];
private page: number = 0;
constructor(
private device: MidiDevice,
public type: MessageType,
private filter: Partial<DataLookup[T]>,
private options: Partial<IMidiControlOptions> = {}
) {
this.device.input.setMaxListeners(50);
this.device.input.addListener(type, (data: DataLookup[T]) => {
// Check incoming data against filter
for (const [k, v] of Object.entries(this.filter)) {
const rawData = data as unknown as { [key: string]: string | number };
if (typeof rawData !== 'object' || rawData === null) return;
if (!(k in rawData)) return;
if (rawData[k] !== v) return;
}
//console.debug('matched data', data);
// store new value
if (!this.options.noStoreInput) this.values[this.page] = data;
if (this.options.feedbackInput) this.feedback(data);
const output = this.outputs[this.page];
if (!output || output === undefined) return;
output(data);
})
const mapped = (value / absFromMax) * absToMax;
return mapped + toMin;
}
private defaultData<D extends DataLookup[T]>(): D {
return defaultData[this.type] as D;
enum MessageType {
NoteOn = 'noteon',
NoteOff = 'noteoff',
Pitch = 'pitch'
}
private feedback(data: DataLookup[T]) {
// ugly typing here, but library overloads are a little annoying to work around. worst case scenario nothing happens
this.device.output.send(this.type as any, { ...data, ...this.filter } as any);
type DataLookup = {
[MessageType.NoteOn]: midi.Note,
[MessageType.NoteOff]: midi.Note,
[MessageType.Pitch]: midi.Pitch
}
public getPage(): number { return this.page };
public getValue(page: number): DataLookup[T] | undefined {
return this.values[page];
const defaultData = {
[MessageType.NoteOn]: { channel: 0, note: 0, velocity: 0 },
[MessageType.NoteOff]: { channel: 0, note: 0, velocity: 0 },
[MessageType.Pitch]: { channel: 0, value: 0 }
}
public setPage(page: number) {
this.page = page;
const values = this.values[page] ?? this.defaultData();
if (!this.options.noPageFeedback) this.feedback(values);
interface IMidiControlOptions {
feedbackInput: boolean,
noPageFeedback: boolean,
noStoreInput: boolean
}
public addOutput<D extends DataLookup[T]>(pages: number | number[], cb: (D: D) => void): void {
if (typeof pages === 'number') pages = [pages];
for (const page of pages) {
this.outputs[page] = cb;
class MidiControl<T extends MessageType> {
public values: { [page: number]: DataLookup[T] } = [];
private outputs: { [page: number]: (data: any) => void } = [];
private page: number = 0;
constructor(
private device: MidiDevice,
public type: MessageType,
private filter: Partial<DataLookup[T]>,
private options: Partial<IMidiControlOptions> = {}
) {
this.device.input.setMaxListeners(50);
this.device.input.addListener(type, (data: DataLookup[T]) => {
// Check incoming data against filter
for (const [k, v] of Object.entries(this.filter)) {
const rawData = data as unknown as { [key: string]: string | number };
if (typeof rawData !== 'object' || rawData === null) return;
if (!(k in rawData)) return;
if (rawData[k] !== v) return;
}
//console.debug('matched data', data);
// store new value
if (!this.options.noStoreInput) this.values[this.page] = data;
if (this.options.feedbackInput) this.feedback(data);
const output = this.outputs[this.page];
if (!output || output === undefined) return;
output(data);
})
}
}
public handleFeedback<D extends DataLookup[T]>(pages: number | number[], data: Partial<D>, noStore?: boolean): void {
if (typeof pages === 'number') pages = [pages];
const newValue = { ...this.defaultData(), ...this.filter, ...data };
private defaultData<D extends DataLookup[T]>(): D {
return defaultData[this.type] as D;
}
for (const page of pages) {
if (!noStore) this.values[page] = newValue;
private feedback(data: DataLookup[T]) {
// ugly typing here, but library overloads are a little annoying to work around. worst case scenario nothing happens
this.device.output.send(this.type as any, { ...data, ...this.filter } as any);
}
if (page === this.page) {
this.feedback(newValue)
public getPage(): number { return this.page };
public getValue(page: number): DataLookup[T] | undefined {
return this.values[page];
}
public setPage(page: number) {
this.page = page;
const values = this.values[page] ?? this.defaultData();
if (!this.options.noPageFeedback) this.feedback(values);
}
public addOutput<D extends DataLookup[T]>(pages: number | number[], cb: (D: D) => void): void {
if (typeof pages === 'number') pages = [pages];
for (const page of pages) {
this.outputs[page] = cb;
}
}
}
}
public handleFeedback<D extends DataLookup[T]>(pages: number | number[], data: Partial<D>, noStore?: boolean): void {
if (typeof pages === 'number') pages = [pages];
const newValue = { ...this.defaultData(), ...this.filter, ...data };
class MidiDevice {
public input: midi.Input;
public output: midi.Output;
private _page: number = 0;
public controls: Array<MidiControl<MessageType>> = [];
for (const page of pages) {
if (!noStore) this.values[page] = newValue;
constructor(device: string) {
this.input = new midi.Input(device);
this.output = new midi.Output(device);
}
if (page === this.page) {
this.feedback(newValue)
}
}
public addControl<T extends MessageType>(type: T, filter: Partial<DataLookup[T]>, options: Partial<IMidiControlOptions> = {}): MidiControl<T> {
const control = new MidiControl(this, type, filter, options);
this.controls.push(control);
return control;
}
public setPage(page: number) {
this._page = page;
for (const control of this.controls) {
control.setPage(page);
}
}
public get page(): number {
return this._page;
}
}
class MidiDevice {
public input: midi.Input;
public output: midi.Output;
private _page: number = 0;
interface IOSCEvent {
address: string;
handler: (message: osc.ResponseMessage<osc.MessageArg>) => void;
}
public controls: Array<MidiControl<MessageType>> = [];
class OSCDevice {
private port: osc.UDPPort;
private listeners: Array<IOSCEvent> = [];
constructor(device: string) {
this.input = new midi.Input(device);
this.output = new midi.Output(device);
}
constructor(localHost: string, remoteHost: string, port: number) {
this.port = new osc.UDPPort({
localAddress: localHost,
remoteAddress: remoteHost,
localPort: port,
remotePort: port
});
public addControl<T extends MessageType>(type: T, filter: Partial<DataLookup[T]>, options: Partial<IMidiControlOptions> = {}): MidiControl<T> {
const control = new MidiControl(this, type, filter, options);
this.controls.push(control);
return control;
}
this.port.on('message', msg => {
// console.log('msg', msg)
for (const listener of this.listeners) {
if (msg.address !== listener.address) continue;
listener.handler(msg);
public setPage(page: number) {
this._page = page;
for (const control of this.controls) {
control.setPage(page);
}
}
})
this.port.open();
public get page(): number {
return this._page;
}
}
interface IOSCEvent {
address: string;
handler: (message: osc.ResponseMessage<osc.MessageArg>) => void;
}
class OSCDevice {
private port: osc.UDPPort;
private listeners: Array<IOSCEvent> = [];
constructor(localHost: string, remoteHost: string, port: number) {
this.port = new osc.UDPPort({
localAddress: localHost,
remoteAddress: remoteHost,
localPort: port,
remotePort: port
});
this.port.on('message', msg => {
// console.log('msg', msg)
for (const listener of this.listeners) {
if (msg.address !== listener.address) continue;
listener.handler(msg);
}
})
this.port.open();
this.pollFeedback()
setInterval(() => {
this.pollFeedback()
}, 5000);
setInterval(() => {
this.pollFeedback()
}, 5000);
}
private pollFeedback() {
this.port.send({
address: '/xremote',
args: [{ type: 'i', value: 1 }]
})
}
public sendInt(address: string, value: number) {
this.port.send({ address, args: [{ type: 'i', value }] });
}
public sendFloat(address: string, value: number) {
this.port.send({ address, args: [{ type: 'f', value }] });
}
public sendString(address: string, value: string) {
this.port.send({ address, args: [{ type: 's', value }] });
}
public sendBytes(address: string, value: osc.Uint8Array) {
this.port.send({ address, args: [{ type: 's', value }] });
}
public sendNull(address: string) {
this.port.send({ address });
}
public addListener(address: string, handler: IOSCEvent['handler']): number {
const newLength = this.listeners.push({
address,
handler
});
return newLength - 1;
}
}
private pollFeedback() {
const deviceName = midi.getInputs().find(i => i.includes('Platform X'));
if (!deviceName) {
console.log('midi device not found')
process.exit(1);
}
this.port.send({
address: '/xremote',
args: [{ type: 'i', value: 1 }]
const mdev = new MidiDevice(deviceName);
const odev = new OSCDevice('0.0.0.0', '192.168.0.47', 10024);
// const client = new osc.OSCClient('192.168.0.26', 9889);
// DEBUG OUT
//['noteon', 'pitch'].map((v) => mdev.input.addListener(v, (data) => { console.log('data', data) }))
// MM MM MMMM MMMMMM MMMMMM MMMMMM MM MM MMMM MMMM
// MMMM MMMM MM MM MM MM MM MM MM MMMM MM MM MM MM MM
// MM MM MM MMMMMMMM MM MM MM MM MM MM MMMM MM MM
// MM MM MM MM MMMMMM MMMMMM MM MM MM MM MMMM MM
// MM MM MM MM MM MM MM MM MM MM MM MM MM
// MM MM MM MM MM MM MMMMMM MM MM MMMM MMMM
const PAGE_MAIN = 0;
const PAGE_HP = 1;
const PAGE_PC = 2;
const ALL_PAGES = [PAGE_MAIN, PAGE_HP, PAGE_PC];
const OSC_LEVEL_0 = 0.75;
// TURN OFF ALL LEDS
const feedbackOff = () => {
for (const control of mdev.controls) {
if (control.type === MessageType.NoteOn) {
control.handleFeedback(ALL_PAGES, { velocity: 0 }, true);
}
}
}
const offButton = mdev.addControl(MessageType.NoteOn, { channel: 0, note: 39 }, { noPageFeedback: true })
offButton.addOutput(ALL_PAGES, (d) => feedbackOff());
odev.addListener('/offleds', () => feedbackOff());
// PAGE SWITCHING
const pageFeedback = () => {
const page = mdev.page;
buttonSel1.handleFeedback(ALL_PAGES, { velocity: page === PAGE_MAIN ? 127 : 0 })
buttonSel2.handleFeedback(ALL_PAGES, { velocity: page === PAGE_HP ? 127 : 0 })
buttonSel3.handleFeedback(ALL_PAGES, { velocity: page === PAGE_PC ? 127 : 0 })
}
const buttonSel1 = mdev.addControl(MessageType.NoteOn, { channel: 0, note: 8 }, { noPageFeedback: true })
buttonSel1.addOutput(ALL_PAGES, (d) => {
if (d.velocity === 127) mdev.setPage(PAGE_MAIN)
pageFeedback();
});
const buttonSel2 = mdev.addControl(MessageType.NoteOn, { channel: 0, note: 9 }, { noPageFeedback: true })
buttonSel2.addOutput(ALL_PAGES, (d) => {
if (d.velocity === 127) mdev.setPage(PAGE_HP)
pageFeedback();
});
const buttonSel3 = mdev.addControl(MessageType.NoteOn, { channel: 0, note: 10 }, { noPageFeedback: true })
buttonSel3.addOutput(ALL_PAGES, (d) => {
if (d.velocity === 127) mdev.setPage(PAGE_PC)
pageFeedback();
});
// FADERS
const faders = [0, 1, 2, 3, 4, 5, 6, 7].map((f) => mdev.addControl(MessageType.Pitch, { channel: f as midi.Channel }, { feedbackInput: true }));
const setLevel = (addr: string, value: number, max: number = 1) => odev.sendFloat(addr, mapNumber(value, 0, 16383, 0, max));
const levelFeedback = (fader: number, page: number, value: number, max: number = 1) => {
faders[fader]?.handleFeedback(page, { value: mapNumber(value, 0, max, 0, 16383) });
}
const fader2way = (fader: number, page: number, addr: string, max: number = 1) => {
faders[fader]?.addOutput(page, d => setLevel(addr, d.value, max));
odev.addListener(addr, d => { levelFeedback(fader, page, (d.args as any)[0], max) });
odev.sendNull(addr);
}
// PAGE 1: Main
fader2way(0, PAGE_MAIN, '/dca/1/fader', OSC_LEVEL_0)
fader2way(1, PAGE_MAIN, '/rtn/1/mix/fader', OSC_LEVEL_0)
fader2way(2, PAGE_MAIN, '/bus/1/mix/fader', OSC_LEVEL_0)
fader2way(3, PAGE_MAIN, '/ch/01/mix/fader')
fader2way(4, PAGE_MAIN, '/ch/03/mix/fader')
fader2way(5, PAGE_MAIN, '/ch/05/mix/fader')
fader2way(6, PAGE_MAIN, '/ch/07/mix/fader')
fader2way(7, PAGE_MAIN, '/ch/09/mix/fader')
// PAGE 2: Headphones
fader2way(0, PAGE_HP, '/bus/1/mix/fader', OSC_LEVEL_0)
fader2way(1, PAGE_HP, '/ch/15/mix/01/level')
fader2way(2, PAGE_HP, '/ch/16/mix/01/level')
fader2way(3, PAGE_HP, '/ch/01/mix/01/level')
fader2way(4, PAGE_HP, '/ch/03/mix/01/level')
fader2way(5, PAGE_HP, '/ch/05/mix/01/level')
fader2way(6, PAGE_HP, '/ch/07/mix/01/level')
fader2way(7, PAGE_HP, '/ch/09/mix/01/level')
// PAGE 3: PC
fader2way(0, PAGE_PC, '/bus/1/mix/fader', OSC_LEVEL_0)
fader2way(1, PAGE_PC, '/ch/15/mix/03/level')
fader2way(2, PAGE_PC, '/ch/16/mix/03/level')
fader2way(3, PAGE_PC, '/ch/01/mix/03/level')
fader2way(4, PAGE_PC, '/ch/03/mix/03/level')
fader2way(5, PAGE_PC, '/ch/05/mix/03/level')
fader2way(6, PAGE_PC, '/ch/07/mix/03/level')
fader2way(7, PAGE_PC, '/ch/09/mix/03/level')
const button = (note: number) => mdev.addControl(MessageType.NoteOn, { channel: 0, note }, { feedbackInput: false, noStoreInput: true });
const mutes = [16, 17, 18, 19, 20, 21, 22, 23].map(n => button(n));
// MUTE BUTTONS
const buttonToggle = (control: MidiControl<MessageType.NoteOn> | undefined, page: number, addr: string) => {
if (!control) return;
control.addOutput(page, (d) => {
if (d.velocity !== 127) return;
const value = control.getValue(page);
if (!value || value.velocity > 1) {
control.handleFeedback(page, { velocity: 0 });
odev.sendInt(addr, 1);
} else {
control.handleFeedback(page, { velocity: 127 });
odev.sendInt(addr, 0);
}
})
odev.addListener(addr, d => {
control.handleFeedback(page, { velocity: (d.args as any)[0] === 0 ? 127 : 0 });
})
odev.sendNull(addr);
}
public sendInt(address: string, value: number) {
this.port.send({ address, args: [{ type: 'i', value }] });
}
// PAGE 1: Main
buttonToggle(mutes[0], PAGE_MAIN, '/dca/1/on')
buttonToggle(mutes[1], PAGE_MAIN, '/rtn/1/mix/on')
buttonToggle(mutes[2], PAGE_MAIN, '/bus/1/mix/on')
buttonToggle(mutes[3], PAGE_MAIN, '/ch/01/mix/on')
buttonToggle(mutes[4], PAGE_MAIN, '/ch/03/mix/on')
buttonToggle(mutes[5], PAGE_MAIN, '/ch/05/mix/on')
buttonToggle(mutes[6], PAGE_MAIN, '/ch/07/mix/on')
buttonToggle(mutes[7], PAGE_MAIN, '/ch/09/mix/on')
// PAGE 2: Headphones
buttonToggle(mutes[0], PAGE_HP, '/bus/1/mix/on')
buttonToggle(mutes[1], PAGE_HP, '/ch/15/mix/on')
buttonToggle(mutes[2], PAGE_HP, '/ch/16/mix/on')
buttonToggle(mutes[3], PAGE_HP, '/ch/01/mix/on')
buttonToggle(mutes[4], PAGE_HP, '/ch/03/mix/on')
buttonToggle(mutes[5], PAGE_HP, '/ch/05/mix/on')
buttonToggle(mutes[6], PAGE_HP, '/ch/07/mix/on')
buttonToggle(mutes[7], PAGE_HP, '/ch/09/mix/on')
public sendFloat(address: string, value: number) {
this.port.send({ address, args: [{ type: 'f', value }] });
}
// PAGE 3: PC
buttonToggle(mutes[0], PAGE_PC, '/bus/1/mix/on')
buttonToggle(mutes[1], PAGE_PC, '/ch/15/mix/on')
buttonToggle(mutes[2], PAGE_PC, '/ch/16/mix/on')
buttonToggle(mutes[3], PAGE_PC, '/ch/01/mix/on')
buttonToggle(mutes[4], PAGE_PC, '/ch/03/mix/on')
buttonToggle(mutes[5], PAGE_PC, '/ch/05/mix/on')
buttonToggle(mutes[6], PAGE_PC, '/ch/07/mix/on')
buttonToggle(mutes[7], PAGE_PC, '/ch/09/mix/on')
public sendString(address: string, value: string) {
this.port.send({ address, args: [{ type: 's', value }] });
}
public sendBytes(address: string, value: osc.Uint8Array) {
this.port.send({ address, args: [{ type: 's', value }] });
}
public sendNull(address: string) {
this.port.send({ address });
}
public addListener(address: string, handler: IOSCEvent['handler']): number {
const newLength = this.listeners.push({
address,
handler
});
return newLength - 1;
}
} catch (e) {
console.error('Uncaught error', e)
}
const deviceName = midi.getInputs().find(i => i.includes('Platform X'));
if (!deviceName) {
console.log('midi device not found')
process.exit(1);
}
const mdev = new MidiDevice(deviceName);
const odev = new OSCDevice('0.0.0.0', '192.168.0.47', 10024);
// const client = new osc.OSCClient('192.168.0.26', 9889);
// DEBUG OUT
//['noteon', 'pitch'].map((v) => mdev.input.addListener(v, (data) => { console.log('data', data) }))
// MM MM MMMM MMMMMM MMMMMM MMMMMM MM MM MMMM MMMM
// MMMM MMMM MM MM MM MM MM MM MM MMMM MM MM MM MM MM
// MM MM MM MMMMMMMM MM MM MM MM MM MM MMMM MM MM
// MM MM MM MM MMMMMM MMMMMM MM MM MM MM MMMM MM
// MM MM MM MM MM MM MM MM MM MM MM MM MM
// MM MM MM MM MM MM MMMMMM MM MM MMMM MMMM
const PAGE_MAIN = 0;
const PAGE_HP = 1;
const PAGE_PC = 2;
const ALL_PAGES = [PAGE_MAIN, PAGE_HP, PAGE_PC];
const OSC_LEVEL_0 = 0.75;
// TURN OFF ALL LEDS
const feedbackOff = () => {
for (const control of mdev.controls) {
if (control.type === MessageType.NoteOn) {
control.handleFeedback(ALL_PAGES, { velocity: 0 }, true);
}
}
}
const offButton = mdev.addControl(MessageType.NoteOn, { channel: 0, note: 39 }, { noPageFeedback: true })
offButton.addOutput(ALL_PAGES, (d) => feedbackOff());
odev.addListener('/offleds', () => feedbackOff());
// PAGE SWITCHING
const pageFeedback = () => {
const page = mdev.page;
buttonSel1.handleFeedback(ALL_PAGES, { velocity: page === PAGE_MAIN ? 127 : 0 })
buttonSel2.handleFeedback(ALL_PAGES, { velocity: page === PAGE_HP ? 127 : 0 })
buttonSel3.handleFeedback(ALL_PAGES, { velocity: page === PAGE_PC ? 127 : 0 })
}
const buttonSel1 = mdev.addControl(MessageType.NoteOn, { channel: 0, note: 8 }, { noPageFeedback: true })
buttonSel1.addOutput(ALL_PAGES, (d) => {
if (d.velocity === 127) mdev.setPage(PAGE_MAIN)
pageFeedback();
});
const buttonSel2 = mdev.addControl(MessageType.NoteOn, { channel: 0, note: 9 }, { noPageFeedback: true })
buttonSel2.addOutput(ALL_PAGES, (d) => {
if (d.velocity === 127) mdev.setPage(PAGE_HP)
pageFeedback();
});
const buttonSel3 = mdev.addControl(MessageType.NoteOn, { channel: 0, note: 10 }, { noPageFeedback: true })
buttonSel3.addOutput(ALL_PAGES, (d) => {
if (d.velocity === 127) mdev.setPage(PAGE_PC)
pageFeedback();
});
// FADERS
const faders = [0, 1, 2, 3, 4, 5, 6, 7].map((f) => mdev.addControl(MessageType.Pitch, { channel: f as midi.Channel }, { feedbackInput: true }));
const setLevel = (addr: string, value: number, max: number = 1) => odev.sendFloat(addr, mapNumber(value, 0, 16383, 0, max));
const levelFeedback = (fader: number, page: number, value: number, max: number = 1) => {
faders[fader]?.handleFeedback(page, { value: mapNumber(value, 0, max, 0, 16383) });
}
const fader2way = (fader: number, page: number, addr: string, max: number = 1) => {
faders[fader]?.addOutput(page, d => setLevel(addr, d.value, max));
odev.addListener(addr, d => { levelFeedback(fader, page, (d.args as any)[0], max) });
odev.sendNull(addr);
}
// PAGE 1: Main
fader2way(0, PAGE_MAIN, '/dca/1/fader', OSC_LEVEL_0)
fader2way(1, PAGE_MAIN, '/rtn/1/mix/fader', OSC_LEVEL_0)
fader2way(2, PAGE_MAIN, '/bus/1/mix/fader', OSC_LEVEL_0)
fader2way(3, PAGE_MAIN, '/ch/01/mix/fader')
fader2way(4, PAGE_MAIN, '/ch/03/mix/fader')
fader2way(5, PAGE_MAIN, '/ch/05/mix/fader')
fader2way(6, PAGE_MAIN, '/ch/07/mix/fader')
fader2way(7, PAGE_MAIN, '/ch/09/mix/fader')
// PAGE 2: Headphones
fader2way(0, PAGE_HP, '/bus/1/mix/fader', OSC_LEVEL_0)
fader2way(1, PAGE_HP, '/ch/15/mix/01/level')
fader2way(2, PAGE_HP, '/ch/16/mix/01/level')
fader2way(3, PAGE_HP, '/ch/01/mix/01/level')
fader2way(4, PAGE_HP, '/ch/03/mix/01/level')
fader2way(5, PAGE_HP, '/ch/05/mix/01/level')
fader2way(6, PAGE_HP, '/ch/07/mix/01/level')
fader2way(7, PAGE_HP, '/ch/09/mix/01/level')
// PAGE 3: PC
fader2way(0, PAGE_PC, '/bus/1/mix/fader', OSC_LEVEL_0)
fader2way(1, PAGE_PC, '/ch/15/mix/03/level')
fader2way(2, PAGE_PC, '/ch/16/mix/03/level')
fader2way(3, PAGE_PC, '/ch/01/mix/03/level')
fader2way(4, PAGE_PC, '/ch/03/mix/03/level')
fader2way(5, PAGE_PC, '/ch/05/mix/03/level')
fader2way(6, PAGE_PC, '/ch/07/mix/03/level')
fader2way(7, PAGE_PC, '/ch/09/mix/03/level')
const button = (note: number) => mdev.addControl(MessageType.NoteOn, { channel: 0, note }, { feedbackInput: false, noStoreInput: true });
const mutes = [16, 17, 18, 19, 20, 21, 22, 23].map(n => button(n));
// MUTE BUTTONS
const buttonToggle = (control: MidiControl<MessageType.NoteOn> | undefined, page: number, addr: string) => {
if (!control) return;
control.addOutput(page, (d) => {
if (d.velocity !== 127) return;
const value = control.getValue(page);
if (!value || value.velocity > 1) {
control.handleFeedback(page, { velocity: 0 });
odev.sendInt(addr, 1);
} else {
control.handleFeedback(page, { velocity: 127 });
odev.sendInt(addr, 0);
}
})
odev.addListener(addr, d => {
control.handleFeedback(page, { velocity: (d.args as any)[0] === 0 ? 127 : 0 });
})
odev.sendNull(addr);
}
// PAGE 1: Main
buttonToggle(mutes[0], PAGE_MAIN, '/dca/1/on')
buttonToggle(mutes[1], PAGE_MAIN, '/rtn/1/mix/on')
buttonToggle(mutes[2], PAGE_MAIN, '/bus/1/mix/on')
buttonToggle(mutes[3], PAGE_MAIN, '/ch/01/mix/on')
buttonToggle(mutes[4], PAGE_MAIN, '/ch/03/mix/on')
buttonToggle(mutes[5], PAGE_MAIN, '/ch/05/mix/on')
buttonToggle(mutes[6], PAGE_MAIN, '/ch/07/mix/on')
buttonToggle(mutes[7], PAGE_MAIN, '/ch/09/mix/on')
// PAGE 2: Headphones
buttonToggle(mutes[0], PAGE_HP, '/bus/1/mix/on')
buttonToggle(mutes[1], PAGE_HP, '/ch/15/mix/on')
buttonToggle(mutes[2], PAGE_HP, '/ch/16/mix/on')
buttonToggle(mutes[3], PAGE_HP, '/ch/01/mix/on')
buttonToggle(mutes[4], PAGE_HP, '/ch/03/mix/on')
buttonToggle(mutes[5], PAGE_HP, '/ch/05/mix/on')
buttonToggle(mutes[6], PAGE_HP, '/ch/07/mix/on')
buttonToggle(mutes[7], PAGE_HP, '/ch/09/mix/on')
// PAGE 3: PC
buttonToggle(mutes[0], PAGE_PC, '/bus/1/mix/on')
buttonToggle(mutes[1], PAGE_PC, '/ch/15/mix/on')
buttonToggle(mutes[2], PAGE_PC, '/ch/16/mix/on')
buttonToggle(mutes[3], PAGE_PC, '/ch/01/mix/on')
buttonToggle(mutes[4], PAGE_PC, '/ch/03/mix/on')
buttonToggle(mutes[5], PAGE_PC, '/ch/05/mix/on')
buttonToggle(mutes[6], PAGE_PC, '/ch/07/mix/on')
buttonToggle(mutes[7], PAGE_PC, '/ch/09/mix/on')