diff --git a/index.html b/index.html index f67fa6f..5c4e15d 100644 --- a/index.html +++ b/index.html @@ -261,6 +261,18 @@ +
+
+ + +
+
+ + +
+
diff --git a/src/AppSettings.ts b/src/AppSettings.ts index 97f414c..c40c628 100644 --- a/src/AppSettings.ts +++ b/src/AppSettings.ts @@ -45,8 +45,10 @@ 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_channels_scripts - Whether midi input channels fires scripts * @param midi_clock_input - The name of the midi clock input * @param midi_clock_ppqn - The pulses per quarter note for midi clock + * @param default_midi_input - The default midi input for incoming messages */ vimMode: boolean; theme: string; @@ -59,8 +61,10 @@ export interface Settings { load_demo_songs: boolean; tips: boolean; send_clock: boolean; + midi_channels_scripts: boolean; midi_clock_input: string|undefined; midi_clock_ppqn: number; + default_midi_input: string|undefined; } export const template_universe = { @@ -118,9 +122,10 @@ 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_channels_scripts - Whether midi input channels fires scripts * @param midi_clock_input - The name of the midi clock input * @param midi_clock_ppqn - The pulses per quarter note for midi clock - + * @param default_midi_input - The default midi input for incoming messages */ public vimMode: boolean = false; @@ -133,7 +138,9 @@ export class AppSettings { public time_position: boolean = true; public tips: boolean = true; public send_clock: boolean = false; + public midi_channels_scripts: boolean = true; public midi_clock_input: string|undefined = undefined; + public default_midi_input: string|undefined = undefined; public midi_clock_ppqn: number = 24; public load_demo_songs: boolean = true; @@ -154,8 +161,10 @@ export class AppSettings { this.time_position = settingsFromStorage.time_position; this.tips = settingsFromStorage.tips; this.send_clock = settingsFromStorage.send_clock; + this.midi_channels_scripts = settingsFromStorage.midi_channels_scripts; this.midi_clock_input = settingsFromStorage.midi_clock_input; this.midi_clock_ppqn = settingsFromStorage.midi_clock_ppqn || 24; + this.default_midi_input = settingsFromStorage.default_midi_input; this.load_demo_songs = settingsFromStorage.load_demo_songs; } else { this.universes = template_universes; @@ -181,8 +190,10 @@ export class AppSettings { time_position: this.time_position, tips: this.tips, send_clock: this.send_clock, + midi_channels_scripts: this.midi_channels_scripts, midi_clock_input: this.midi_clock_input, midi_clock_ppqn: this.midi_clock_ppqn, + default_midi_input: this.default_midi_input, load_demo_songs: this.load_demo_songs, }; } @@ -206,8 +217,10 @@ export class AppSettings { this.time_position = settings.time_position; this.tips = settings.tips; this.send_clock = settings.send_clock; + this.midi_channels_scripts = settings.midi_channels_scripts; this.midi_clock_input = settings.midi_clock_input; this.midi_clock_ppqn = settings.midi_clock_ppqn; + this.default_midi_input = settings.default_midi_input; this.load_demo_songs = settings.load_demo_songs; localStorage.setItem("topos", JSON.stringify(this.data)); } diff --git a/src/IO/MidiConnection.ts b/src/IO/MidiConnection.ts index 977e440..a7fec62 100644 --- a/src/IO/MidiConnection.ts +++ b/src/IO/MidiConnection.ts @@ -146,45 +146,76 @@ export class MidiConnection { * Updates the MIDI clock input select element with the available MIDI inputs. */ if(this.midiInputs.length > 0) { - const select = document.getElementById("midi-clock-input") as HTMLSelectElement; - select.innerHTML = ""; + const midiClockSelect = document.getElementById("midi-clock-input") as HTMLSelectElement; + const midiInputSelect = document.getElementById("default-midi-input") as HTMLSelectElement; + + midiClockSelect.innerHTML = ""; + midiInputSelect.innerHTML = ""; + // Defaults to internal clock const defaultOption = document.createElement("option"); defaultOption.value = "-1"; defaultOption.text = "Internal"; - select.appendChild(defaultOption); - // Add MIDI inputs to clock select input + midiClockSelect.appendChild(defaultOption); + + // Add MIDI inputs to clock select input and default midi input this.midiInputs.forEach((input, index) => { const option = document.createElement("option"); option.value = index.toString(); option.text = input.name || index.toString(); - select.appendChild(option); + midiClockSelect.appendChild(option); + midiInputSelect.appendChild(option.cloneNode(true)); }); + if(this.settings.midi_clock_input) { const clockMidiInputIndex = this.getMidiInputIndex(this.settings.midi_clock_input); - select.value = clockMidiInputIndex.toString(); + midiClockSelect.value = clockMidiInputIndex.toString(); if(clockMidiInputIndex > 0) { this.midiClockInput = this.midiInputs[clockMidiInputIndex]; this.registerMidiClockListener(); } } else { - select.value = "-1"; + midiClockSelect.value = "-1"; } - // Add listener - select.addEventListener("change", (event) => { + + if(this.settings.default_midi_input) { + const defaultMidiInputIndex = this.getMidiInputIndex(this.settings.default_midi_input); + midiInputSelect.value = defaultMidiInputIndex.toString(); + if(defaultMidiInputIndex > 0) { + this.currentInputIndex = defaultMidiInputIndex; + this.registerMidiInputListener(); + } + } else { + midiInputSelect.value = ""; + } + + // Add midi clock listener + midiClockSelect.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); + const clockInputIndex = parseInt(value); if(this.midiClockInput) this.midiClockInput.onmidimessage = null; - this.midiClockInput = this.midiInputs[this.currentInputIndex]; + this.midiClockInput = this.midiInputs[clockInputIndex]; this.registerMidiClockListener(); this.settings.midi_clock_input = this.midiClockInput.name || undefined; } }); + + // Add mini input listener + midiInputSelect.addEventListener("change", (event) => { + const value = (event.target as HTMLSelectElement).value; + if(value) { + this.unregisterMidiInputListener(); + this.currentInputIndex = parseInt(value); + this.registerMidiInputListener(); + this.settings.default_midi_input = this.midiInputs[this.currentInputIndex].name || undefined; + } + }); + } } @@ -221,6 +252,45 @@ export class MidiConnection { } } + public registerMidiInputListener(): void { + /** + * Register midi input listener and store last value as global parameter named channel_{number} + */ + if(this.currentInputIndex !== undefined) { + const input = this.midiInputs[this.currentInputIndex]; + if(input) { + input.onmidimessage = (event: Event) => { + const message = event as MIDIMessageEvent; + + // List of all note_on messages from channels 1-16 + const all_note_ons = [0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,0x9E, 0x9F]; + // If message is one of note ons + if(all_note_ons.indexOf(message.data[0]) !== -1) { + const channel = all_note_ons.indexOf(message.data[0])+1; + const note = message.data[1]; + const velocity = message.data[2]; + this.api.variable(`channel_${channel}_note`, note); + this.api.variable(`channel_${channel}_velocity`, velocity); + if(this.settings.midi_channels_scripts) this.api.script(channel); + } + } + } + } + } + + public unregisterMidiInputListener(): void { + /** + * Unregister midi input listener + */ + if(this.currentInputIndex !== undefined) { + const input = this.midiInputs[this.currentInputIndex]; + if(input) { + input.onmidimessage = null; + } + } + } + + public onMidiClock(timestamp: number): void { /** * Called when a MIDI clock message is received. diff --git a/src/main.ts b/src/main.ts index 8a60818..629eb9a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -212,6 +212,10 @@ export class Editor { "send-midi-clock" ) as HTMLInputElement; + midi_channels_scripts: HTMLInputElement = document.getElementById( + "midi-channels-scripts" + ) as HTMLInputElement; + midi_clock_ppqn: HTMLSelectElement = document.getElementById( "midi-clock-ppqn-input" ) as HTMLSelectElement; @@ -262,6 +266,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_channels_scripts.checked = this.settings.midi_channels_scripts; this.midi_clock_ppqn.value = this.settings.midi_clock_ppqn.toString(); if (!this.settings.time_position) { document.getElementById("timeviewer")!.classList.add("hidden"); @@ -605,6 +610,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_channels_scripts.checked = this.settings.midi_channels_scripts; this.midi_clock_ppqn.value = this.settings.midi_clock_ppqn.toString(); this.load_demo_songs.checked = this.settings.load_demo_songs; @@ -707,6 +713,11 @@ export class Editor { this.settings.send_clock = checked; }); + this.midi_channels_scripts.addEventListener("change", () => { + let checked = this.midi_channels_scripts.checked ? true : false; + this.settings.midi_channels_scripts = checked; + }); + this.midi_clock_ppqn.addEventListener("change", () => { let value = parseInt(this.midi_clock_ppqn.value); this.settings.midi_clock_ppqn = value;