diff --git a/src/Clock.ts b/src/Clock.ts index 9ef51c9..6e8bb5c 100644 --- a/src/Clock.ts +++ b/src/Clock.ts @@ -127,7 +127,7 @@ export class Clock { } set bpm(bpm: number) { - if(bpm>0 && this._bpm !== bpm) { + if (bpm > 0 && this._bpm !== bpm) { this._bpm = bpm; this.transportNode?.setBPM(bpm); } @@ -138,7 +138,7 @@ export class Clock { } set ppqn(ppqn: number) { - if(ppqn>0 && this._ppqn !== ppqn) { + if (ppqn > 0 && this._ppqn !== ppqn) { this._ppqn = ppqn; this.transportNode?.setPPQN(ppqn); } @@ -154,23 +154,32 @@ export class Clock { public start(): void { /** * Starts the TransportNode (starts the clock). + * + * @remark also sends a MIDI message if a port is declared */ this.app.audioContext.resume(); + this.app.api.MidiConnection.sendStartMessage(); this.transportNode?.start(); } public pause(): void { /** * Pauses the TransportNode (pauses the clock). + * + * @remark also sends a MIDI message if a port is declared */ this.transportNode?.pause(); + this.app.api.MidiConnection.sendStopMessage(); } public stop(): void { /** * Stops the TransportNode (stops the clock). + * + * @remark also sends a MIDI message if a port is declared */ this.app.clock.tick = -1; + this.app.api.MidiConnection.sendStopMessage(); this.transportNode?.stop(); } } diff --git a/src/IO/MidiConnection.ts b/src/IO/MidiConnection.ts index acb81ef..d5b1ec7 100644 --- a/src/IO/MidiConnection.ts +++ b/src/IO/MidiConnection.ts @@ -54,6 +54,26 @@ export class MidiConnection { } } + public sendStartMessage(): void { + /** + * Sends a MIDI Start message to the currently selected MIDI output. + */ + const output = this.midiOutputs[this.currentOutputIndex]; + if (output) { + output.send([0xfa]); // Send MIDI Start message + } + } + + public sendStopMessage(): void { + /** + * Sends a MIDI Stop message to the currently selected MIDI output. + */ + const output = this.midiOutputs[this.currentOutputIndex]; + if (output) { + output.send([0xfc]); // Send MIDI Stop message + } + } + public getCurrentMidiPortIndex(): number { /** * Returns the index of the currently selected MIDI output. @@ -79,8 +99,6 @@ export class MidiConnection { const output = this.midiOutputs[this.currentOutputIndex]; if (output) { output.send([0xf8]); // Send a single MIDI clock message - } else { - console.error("MIDI output not available."); } } diff --git a/src/TransportNode.js b/src/TransportNode.js index 42c8a47..3d4d1e5 100644 --- a/src/TransportNode.js +++ b/src/TransportNode.js @@ -1,53 +1,52 @@ import { tryEvaluate } from "./Evaluator"; -const zeroPad = (num, places) => String(num).padStart(places, '0') +const zeroPad = (num, places) => String(num).padStart(places, "0"); export class TransportNode extends AudioWorkletNode { + constructor(context, options, application) { + super(context, "transport", options); + this.app = application; + this.port.addEventListener("message", this.handleMessage); + this.port.start(); + this.timeviewer = document.getElementById("timeviewer"); + } - constructor(context, options, application) { - super(context, "transport", options); - this.app = application - this.port.addEventListener("message", this.handleMessage); - this.port.start(); - this.timeviewer = document.getElementById("timeviewer"); + /** @type {(this: MessagePort, ev: MessageEvent) => any} */ + handleMessage = (message) => { + if (message.data && message.data.type === "bang") { + this.app.clock.tick++; + const futureTimeStamp = this.app.clock.convertTicksToTimeposition( + this.app.clock.tick + ); + this.app.clock.time_position = futureTimeStamp; + this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${ + futureTimeStamp.beat + 1 + }:${zeroPad(futureTimeStamp.pulse, 2)}`; + this.app.api.MidiConnection.sendMidiClock(); + if (this.app.exampleIsPlaying) { + tryEvaluate(this.app, this.app.example_buffer); + } else { + tryEvaluate(this.app, this.app.global_buffer); + } } + }; - /** @type {(this: MessagePort, ev: MessageEvent) => any} */ - handleMessage = (message) => { - if (message.data && message.data.type === "bang") { + start() { + this.port.postMessage("start"); + } - this.app.clock.tick++ - const futureTimeStamp = this.app.clock.convertTicksToTimeposition(this.app.clock.tick); - this.app.clock.time_position = futureTimeStamp; - this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${futureTimeStamp.beat+1}:${zeroPad(futureTimeStamp.pulse, 2)}`; - - if (this.app.exampleIsPlaying) { - tryEvaluate(this.app, this.app.example_buffer); - } else { - tryEvaluate(this.app, this.app.global_buffer); - } - - } - }; + pause() { + this.port.postMessage("pause"); + } + setBPM(bpm) { + this.port.postMessage({ type: "bpm", value: bpm }); + } - start() { - this.port.postMessage("start"); - } - - pause() { - this.port.postMessage("pause"); - } - - setBPM(bpm) { - this.port.postMessage({ type: "bpm", value: bpm }); - } - - setPPQN(ppqn) { - this.port.postMessage({ type: "ppqn", value: ppqn }); - } - - stop() { - this.port.postMessage("stop"); - } + setPPQN(ppqn) { + this.port.postMessage({ type: "ppqn", value: ppqn }); + } + stop() { + this.port.postMessage("stop"); + } } diff --git a/src/main.ts b/src/main.ts index c938639..ec39307 100644 --- a/src/main.ts +++ b/src/main.ts @@ -66,8 +66,6 @@ const bindings = Object.keys(classMap).map((key) => ({ replace: (match, p1) => `<${key} class="${classMap[key]}" ${p1}>`, })); - - export class Editor { universes: Universes = template_universes; selected_universe: string; @@ -487,10 +485,12 @@ export class Editor { this.setButtonHighlighting("pause", true); this.isPlaying = !this.isPlaying; this.clock.pause(); + this.api.MidiConnection.sendStopMessage(); } else { this.setButtonHighlighting("play", true); this.isPlaying = !this.isPlaying; this.clock.start(); + this.api.MidiConnection.sendStartMessage(); } }); }); @@ -562,12 +562,16 @@ export class Editor { if (this.settings.vimMode) { let vim = document.getElementById("vim-mode-radio") as HTMLInputElement; - let normal = document.getElementById("normal-mode-radio") as HTMLInputElement; + let normal = document.getElementById( + "normal-mode-radio" + ) as HTMLInputElement; vim.checked = true; normal.checked = false; } else { let vim = document.getElementById("vim-mode-radio") as HTMLInputElement; - let normal = document.getElementById("normal-mode-radio") as HTMLInputElement; + let normal = document.getElementById( + "normal-mode-radio" + ) as HTMLInputElement; normal.checked = true; vim.checked = false; }