From 824fd815afe5443d038b85bf41a43821c1039144 Mon Sep 17 00:00:00 2001 From: RikSolo Date: Thu, 29 Jan 2026 02:04:17 +0100 Subject: [PATCH] add global try-catch --- src/index.ts | 703 ++++++++++++++++++++++++++------------------------- 1 file changed, 354 insertions(+), 349 deletions(-) diff --git a/src/index.ts b/src/index.ts index f631d94..fe6fbbc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 { - 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, - private options: Partial = {} - - ) { - 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 { - 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(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 { + 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, + private options: Partial = {} + + ) { + 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(pages: number | number[], data: Partial, noStore?: boolean): void { - if (typeof pages === 'number') pages = [pages]; - const newValue = { ...this.defaultData(), ...this.filter, ...data }; + private defaultData(): 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(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(pages: number | number[], data: Partial, 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> = []; + 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(type: T, filter: Partial, options: Partial = {}): MidiControl { - 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) => void; -} + public controls: Array> = []; -class OSCDevice { - private port: osc.UDPPort; - private listeners: Array = []; + 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(type: T, filter: Partial, options: Partial = {}): MidiControl { + 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) => void; + } + + class OSCDevice { + private port: osc.UDPPort; + private listeners: Array = []; + + 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 | 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; - } -} - -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 | 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') +} catch (e) { + console.error('Uncaught error', e) +} \ No newline at end of file