improving midi clock ever so slightly

This commit is contained in:
2023-09-27 23:12:33 +02:00
parent 4669663ae4
commit f1072e4227
4 changed files with 80 additions and 50 deletions

View File

@ -127,7 +127,7 @@ export class Clock {
} }
set bpm(bpm: number) { set bpm(bpm: number) {
if(bpm>0 && this._bpm !== bpm) { if (bpm > 0 && this._bpm !== bpm) {
this._bpm = bpm; this._bpm = bpm;
this.transportNode?.setBPM(bpm); this.transportNode?.setBPM(bpm);
} }
@ -138,7 +138,7 @@ export class Clock {
} }
set ppqn(ppqn: number) { set ppqn(ppqn: number) {
if(ppqn>0 && this._ppqn !== ppqn) { if (ppqn > 0 && this._ppqn !== ppqn) {
this._ppqn = ppqn; this._ppqn = ppqn;
this.transportNode?.setPPQN(ppqn); this.transportNode?.setPPQN(ppqn);
} }
@ -154,23 +154,32 @@ export class Clock {
public start(): void { public start(): void {
/** /**
* Starts the TransportNode (starts the clock). * Starts the TransportNode (starts the clock).
*
* @remark also sends a MIDI message if a port is declared
*/ */
this.app.audioContext.resume(); this.app.audioContext.resume();
this.app.api.MidiConnection.sendStartMessage();
this.transportNode?.start(); this.transportNode?.start();
} }
public pause(): void { public pause(): void {
/** /**
* Pauses the TransportNode (pauses the clock). * Pauses the TransportNode (pauses the clock).
*
* @remark also sends a MIDI message if a port is declared
*/ */
this.transportNode?.pause(); this.transportNode?.pause();
this.app.api.MidiConnection.sendStopMessage();
} }
public stop(): void { public stop(): void {
/** /**
* Stops the TransportNode (stops the clock). * Stops the TransportNode (stops the clock).
*
* @remark also sends a MIDI message if a port is declared
*/ */
this.app.clock.tick = -1; this.app.clock.tick = -1;
this.app.api.MidiConnection.sendStopMessage();
this.transportNode?.stop(); this.transportNode?.stop();
} }
} }

View File

@ -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 { public getCurrentMidiPortIndex(): number {
/** /**
* Returns the index of the currently selected MIDI output. * Returns the index of the currently selected MIDI output.
@ -79,8 +99,6 @@ export class MidiConnection {
const output = this.midiOutputs[this.currentOutputIndex]; const output = this.midiOutputs[this.currentOutputIndex];
if (output) { if (output) {
output.send([0xf8]); // Send a single MIDI clock message output.send([0xf8]); // Send a single MIDI clock message
} else {
console.error("MIDI output not available.");
} }
} }

View File

@ -1,53 +1,52 @@
import { tryEvaluate } from "./Evaluator"; 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 { 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) { /** @type {(this: MessagePort, ev: MessageEvent<any>) => any} */
super(context, "transport", options); handleMessage = (message) => {
this.app = application if (message.data && message.data.type === "bang") {
this.port.addEventListener("message", this.handleMessage); this.app.clock.tick++;
this.port.start(); const futureTimeStamp = this.app.clock.convertTicksToTimeposition(
this.timeviewer = document.getElementById("timeviewer"); 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>) => any} */ start() {
handleMessage = (message) => { this.port.postMessage("start");
if (message.data && message.data.type === "bang") { }
this.app.clock.tick++ pause() {
const futureTimeStamp = this.app.clock.convertTicksToTimeposition(this.app.clock.tick); this.port.postMessage("pause");
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);
}
}
};
setBPM(bpm) {
this.port.postMessage({ type: "bpm", value: bpm });
}
start() { setPPQN(ppqn) {
this.port.postMessage("start"); this.port.postMessage({ type: "ppqn", value: ppqn });
} }
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");
}
stop() {
this.port.postMessage("stop");
}
} }

View File

@ -66,8 +66,6 @@ const bindings = Object.keys(classMap).map((key) => ({
replace: (match, p1) => `<${key} class="${classMap[key]}" ${p1}>`, replace: (match, p1) => `<${key} class="${classMap[key]}" ${p1}>`,
})); }));
export class Editor { export class Editor {
universes: Universes = template_universes; universes: Universes = template_universes;
selected_universe: string; selected_universe: string;
@ -487,10 +485,12 @@ export class Editor {
this.setButtonHighlighting("pause", true); this.setButtonHighlighting("pause", true);
this.isPlaying = !this.isPlaying; this.isPlaying = !this.isPlaying;
this.clock.pause(); this.clock.pause();
this.api.MidiConnection.sendStopMessage();
} else { } else {
this.setButtonHighlighting("play", true); this.setButtonHighlighting("play", true);
this.isPlaying = !this.isPlaying; this.isPlaying = !this.isPlaying;
this.clock.start(); this.clock.start();
this.api.MidiConnection.sendStartMessage();
} }
}); });
}); });
@ -562,12 +562,16 @@ export class Editor {
if (this.settings.vimMode) { if (this.settings.vimMode) {
let vim = document.getElementById("vim-mode-radio") as HTMLInputElement; 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; vim.checked = true;
normal.checked = false; normal.checked = false;
} else { } else {
let vim = document.getElementById("vim-mode-radio") as HTMLInputElement; 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; normal.checked = true;
vim.checked = false; vim.checked = false;
} }