From 327bfc5086c9b0e15bf4c0cbdcb4cc0f0b56defc Mon Sep 17 00:00:00 2001 From: Miika Alonen Date: Wed, 4 Oct 2023 00:20:50 +0300 Subject: [PATCH] Store Midi clock stuff to localstorage --- src/API.ts | 2 +- src/AppSettings.ts | 14 ++++++++++++++ src/IO/MidiConnection.ts | 34 +++++++++++++++++++--------------- src/main.ts | 11 +++++++++++ 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/API.ts b/src/API.ts index 3627c8b..7d07c96 100644 --- a/src/API.ts +++ b/src/API.ts @@ -60,7 +60,7 @@ export class UserAPI { load: samples; constructor(public app: Editor) { - this.MidiConnection = new MidiConnection(this); + this.MidiConnection = new MidiConnection(this, app.settings); } _loadUniverseFromInterface = (universe: string) => { diff --git a/src/AppSettings.ts b/src/AppSettings.ts index 1caf8c0..608d5f6 100644 --- a/src/AppSettings.ts +++ b/src/AppSettings.ts @@ -45,6 +45,8 @@ export interface Settings { * @param time_position - Whether or not to show time position * @param tips - Whether or not to show tips * @param send_clock - Whether or not to send midi clock + * @param midi_clock_input - The name of the midi clock input + * @param midi_clock_ppqn - The pulses per quarter note for midi clock */ vimMode: boolean; theme: string; @@ -56,6 +58,8 @@ export interface Settings { time_position: boolean; tips: boolean; send_clock: boolean; + midi_clock_input: string|undefined; + midi_clock_ppqn: number; } export const template_universe = { @@ -113,6 +117,8 @@ export class AppSettings { * @param time_position - Whether or not to show time position * @param tips - Whether or not to show tips * @param send_clock - Whether or not to send midi clock + * @param midi_clock_input - The name of the midi clock input + * @param midi_clock_ppqn - The pulses per quarter note for midi clock */ @@ -126,6 +132,8 @@ export class AppSettings { public time_position: boolean = true; public tips: boolean = true; public send_clock: boolean = false; + public midi_clock_input: string|undefined = undefined; + public midi_clock_ppqn: number = 24; constructor() { const settingsFromStorage = JSON.parse( @@ -144,6 +152,8 @@ export class AppSettings { this.time_position = settingsFromStorage.time_position; this.tips = settingsFromStorage.tips; this.send_clock = settingsFromStorage.send_clock; + this.midi_clock_input = settingsFromStorage.midi_clock_input; + this.midi_clock_ppqn = settingsFromStorage.midi_clock_ppqn || 24; } else { this.universes = template_universes; } @@ -168,6 +178,8 @@ export class AppSettings { time_position: this.time_position, tips: this.tips, send_clock: this.send_clock, + midi_clock_input: this.midi_clock_input, + midi_clock_ppqn: this.midi_clock_ppqn, }; } @@ -190,6 +202,8 @@ export class AppSettings { this.time_position = settings.time_position; this.tips = settings.tips; this.send_clock = settings.send_clock; + this.midi_clock_input = settings.midi_clock_input; + this.midi_clock_ppqn = settings.midi_clock_ppqn; localStorage.setItem("topos", JSON.stringify(this.data)); } } diff --git a/src/IO/MidiConnection.ts b/src/IO/MidiConnection.ts index 3356962..977e440 100644 --- a/src/IO/MidiConnection.ts +++ b/src/IO/MidiConnection.ts @@ -1,4 +1,5 @@ import { UserAPI } from "../API"; +import { AppSettings } from "../AppSettings"; export class MidiConnection { /** @@ -12,6 +13,7 @@ export class MidiConnection { */ private api: UserAPI; + private settings: AppSettings; private midiAccess: MIDIAccess | null = null; public midiOutputs: MIDIOutput[] = []; public midiInputs: MIDIInput[] = []; @@ -28,13 +30,13 @@ export class MidiConnection { private clockBuffer: number[] = []; private deltaBuffer: number[] = []; private clockBufferLength = 24; - private clockPPQN = 24; private clockTicks = 0; private clockErrorCount = 0; private skipOnError = 0; - constructor(api: UserAPI) { + constructor(api: UserAPI, settings: AppSettings) { this.api = api; + this.settings = settings; this.lastBPM = api.bpm(); this.roundedBPM = this.lastBPM; this.initializeMidiAccess(); @@ -58,7 +60,6 @@ export class MidiConnection { console.warn("No MIDI inputs available."); } else { this.updateMidiClockSelect(); - this.clockPPQNSelect(); } } catch (error) { console.error("Failed to initialize MIDI:", error); @@ -156,34 +157,37 @@ export class MidiConnection { this.midiInputs.forEach((input, index) => { const option = document.createElement("option"); option.value = index.toString(); - option.text = input.name || "No name input"; + option.text = input.name || index.toString(); select.appendChild(option); }); - select.value = this.currentInputIndex ? this.currentInputIndex.toString() : "-1"; + if(this.settings.midi_clock_input) { + const clockMidiInputIndex = this.getMidiInputIndex(this.settings.midi_clock_input); + select.value = clockMidiInputIndex.toString(); + if(clockMidiInputIndex > 0) { + this.midiClockInput = this.midiInputs[clockMidiInputIndex]; + this.registerMidiClockListener(); + } + } else { + select.value = "-1"; + } // Add listener select.addEventListener("change", (event) => { const value = (event.target as HTMLSelectElement).value; if(value === "-1") { if(this.midiClockInput) this.midiClockInput.onmidimessage = null; this.midiClockInput = undefined; + this.settings.midi_clock_input = undefined; } else { this.currentInputIndex = parseInt(value); if(this.midiClockInput) this.midiClockInput.onmidimessage = null; this.midiClockInput = this.midiInputs[this.currentInputIndex]; this.registerMidiClockListener(); + this.settings.midi_clock_input = this.midiClockInput.name || undefined; } }); } } - clockPPQNSelect(): void { - const select = document.getElementById("midi-clock-ppqn-input") as HTMLSelectElement; - select.addEventListener("change", (event) => { - const value = (event.target as HTMLSelectElement).value; - this.clockPPQN = parseInt(value); - }); - } - public registerMidiClockListener(): void { /** * Registers a listener for MIDI clock messages on the currently selected MIDI input. @@ -241,12 +245,12 @@ export class MidiConnection { this.clockErrorCount = 0; /* I dont know why this happens. But when it does, deltas for the following messages are off. So skipping ~ quarted of clock resolution usually helps */ - this.skipOnError = this.clockPPQN/4; + this.skipOnError = this.settings.midi_clock_ppqn/4; timestamp = 0; // timestamp 0 == lastTimestamp 0 } else { this.midiClockDelta = timestamp - this.lastTimestamp; - this.lastBPM = 60 * (1000 / this.midiClockDelta / 24); + this.lastBPM = 60 * (1000 / this.midiClockDelta / this.settings.midi_clock_ppqn); this.clockBuffer.push(this.lastBPM); if(this.clockBuffer.length>this.clockBufferLength) this.clockBuffer.shift(); diff --git a/src/main.ts b/src/main.ts index a193c54..9f821d4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -197,6 +197,10 @@ export class Editor { "send-midi-clock" ) as HTMLInputElement; + midi_clock_ppqn: HTMLSelectElement = document.getElementById( + "midi-clock-ppqn-input" + ) as HTMLSelectElement; + // Editor mode selection normal_mode_button: HTMLButtonElement = document.getElementById( "normal-mode" @@ -246,6 +250,7 @@ export class Editor { this.time_position_checkbox.checked = this.settings.time_position; this.tips_checkbox.checked = this.settings.tips; this.midi_clock_checkbox.checked = this.settings.send_clock; + this.midi_clock_ppqn.value = this.settings.midi_clock_ppqn.toString(); if (!this.settings.time_position) { document.getElementById("timeviewer")!.classList.add("hidden"); } @@ -565,6 +570,7 @@ export class Editor { this.time_position_checkbox.checked = this.settings.time_position; this.tips_checkbox.checked = this.settings.tips; this.midi_clock_checkbox.checked = this.settings.send_clock; + this.midi_clock_ppqn.value = this.settings.midi_clock_ppqn.toString(); if (this.settings.vimMode) { let vim = document.getElementById("vim-mode-radio") as HTMLInputElement; @@ -665,6 +671,11 @@ export class Editor { this.settings.send_clock = checked; }); + this.midi_clock_ppqn.addEventListener("change", () => { + let value = parseInt(this.midi_clock_ppqn.value); + this.settings.midi_clock_ppqn = value; + }); + this.vim_mode_button.addEventListener("click", () => { this.settings.vimMode = true; this.view.dispatch({