Now listening both clock and other events
This commit is contained in:
@ -265,7 +265,7 @@
|
|||||||
<div class="flex items-center mb-4 ml-6">
|
<div class="flex items-center mb-4 ml-6">
|
||||||
<label for="default-checkbox" class="ml-2 text-sm font-medium text-dark">MIDI input: </label>
|
<label for="default-checkbox" class="ml-2 text-sm font-medium text-dark">MIDI input: </label>
|
||||||
<select id="default-midi-input" class="w-32 h-8 text-sm font-medium text-black bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2">
|
<select id="default-midi-input" class="w-32 h-8 text-sm font-medium text-black bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2">
|
||||||
<option value="-1">Keystep</option>
|
<option value="-1">None</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mb-4 ml-20">
|
<div class="flex items-center mb-4 ml-20">
|
||||||
|
|||||||
@ -22,13 +22,13 @@ export class MidiConnection {
|
|||||||
private scheduledNotes: { [noteNumber: number]: number } = {}; // { noteNumber: timeoutId }
|
private scheduledNotes: { [noteNumber: number]: number } = {}; // { noteNumber: timeoutId }
|
||||||
|
|
||||||
/* MIDI clock stuff */
|
/* MIDI clock stuff */
|
||||||
|
private midiClockInputIndex: number|undefined = undefined;
|
||||||
private midiClockInput?: MIDIInput|undefined = undefined;
|
private midiClockInput?: MIDIInput|undefined = undefined;
|
||||||
private lastTimestamp: number = 0;
|
private lastTimestamp: number = 0;
|
||||||
private midiClockDelta: number = 0;
|
private midiClockDelta: number = 0;
|
||||||
private lastBPM: number;
|
private lastBPM: number;
|
||||||
private roundedBPM: number = 0;
|
private roundedBPM: number = 0;
|
||||||
private clockBuffer: number[] = [];
|
private clockBuffer: number[] = [];
|
||||||
private deltaBuffer: number[] = [];
|
|
||||||
private clockBufferLength = 24;
|
private clockBufferLength = 24;
|
||||||
private clockTicks = 0;
|
private clockTicks = 0;
|
||||||
private clockErrorCount = 0;
|
private clockErrorCount = 0;
|
||||||
@ -59,7 +59,7 @@ export class MidiConnection {
|
|||||||
if (this.midiInputs.length === 0) {
|
if (this.midiInputs.length === 0) {
|
||||||
console.warn("No MIDI inputs available.");
|
console.warn("No MIDI inputs available.");
|
||||||
} else {
|
} else {
|
||||||
this.updateMidiClockSelect();
|
this.updateInputSelects();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to initialize MIDI:", error);
|
console.error("Failed to initialize MIDI:", error);
|
||||||
@ -134,14 +134,15 @@ export class MidiConnection {
|
|||||||
*/
|
*/
|
||||||
const inputIndex = this.getMidiInputIndex(inputName);
|
const inputIndex = this.getMidiInputIndex(inputName);
|
||||||
if (inputIndex !== -1) {
|
if (inputIndex !== -1) {
|
||||||
|
this.midiClockInputIndex = inputIndex;
|
||||||
this.midiClockInput = this.midiInputs[inputIndex];
|
this.midiClockInput = this.midiInputs[inputIndex];
|
||||||
this.registerMidiClockListener();
|
this.registerMidiInputListener(inputIndex);
|
||||||
} else {
|
} else {
|
||||||
this.midiClockInput = undefined;
|
this.midiClockInput = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateMidiClockSelect() {
|
public updateInputSelects() {
|
||||||
/**
|
/**
|
||||||
* Updates the MIDI clock input select element with the available MIDI inputs.
|
* Updates the MIDI clock input select element with the available MIDI inputs.
|
||||||
*/
|
*/
|
||||||
@ -152,12 +153,18 @@ export class MidiConnection {
|
|||||||
midiClockSelect.innerHTML = "";
|
midiClockSelect.innerHTML = "";
|
||||||
midiInputSelect.innerHTML = "";
|
midiInputSelect.innerHTML = "";
|
||||||
|
|
||||||
// Defaults to internal clock
|
// Set Midi clock as Internal by default
|
||||||
const defaultOption = document.createElement("option");
|
const defaultOption = document.createElement("option");
|
||||||
defaultOption.value = "-1";
|
defaultOption.value = "-1";
|
||||||
defaultOption.text = "Internal";
|
defaultOption.text = "Internal";
|
||||||
midiClockSelect.appendChild(defaultOption);
|
midiClockSelect.appendChild(defaultOption);
|
||||||
|
|
||||||
|
// Set default input as None by default
|
||||||
|
const defaultInputOption = document.createElement("option");
|
||||||
|
defaultInputOption.value = "-1";
|
||||||
|
defaultInputOption.text = "None";
|
||||||
|
midiInputSelect.appendChild(defaultInputOption);
|
||||||
|
|
||||||
// Add MIDI inputs to clock select input and default midi input
|
// Add MIDI inputs to clock select input and default midi input
|
||||||
this.midiInputs.forEach((input, index) => {
|
this.midiInputs.forEach((input, index) => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
@ -172,7 +179,7 @@ export class MidiConnection {
|
|||||||
midiClockSelect.value = clockMidiInputIndex.toString();
|
midiClockSelect.value = clockMidiInputIndex.toString();
|
||||||
if(clockMidiInputIndex > 0) {
|
if(clockMidiInputIndex > 0) {
|
||||||
this.midiClockInput = this.midiInputs[clockMidiInputIndex];
|
this.midiClockInput = this.midiInputs[clockMidiInputIndex];
|
||||||
this.registerMidiClockListener();
|
this.registerMidiInputListener(clockMidiInputIndex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
midiClockSelect.value = "-1";
|
midiClockSelect.value = "-1";
|
||||||
@ -183,24 +190,25 @@ export class MidiConnection {
|
|||||||
midiInputSelect.value = defaultMidiInputIndex.toString();
|
midiInputSelect.value = defaultMidiInputIndex.toString();
|
||||||
if(defaultMidiInputIndex > 0) {
|
if(defaultMidiInputIndex > 0) {
|
||||||
this.currentInputIndex = defaultMidiInputIndex;
|
this.currentInputIndex = defaultMidiInputIndex;
|
||||||
this.registerMidiInputListener();
|
this.registerMidiInputListener(defaultMidiInputIndex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
midiInputSelect.value = "";
|
midiInputSelect.value = "-1";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add midi clock listener
|
// Add midi clock listener
|
||||||
midiClockSelect.addEventListener("change", (event) => {
|
midiClockSelect.addEventListener("change", (event) => {
|
||||||
const value = (event.target as HTMLSelectElement).value;
|
const value = (event.target as HTMLSelectElement).value;
|
||||||
if(value === "-1") {
|
if(value === "-1") {
|
||||||
if(this.midiClockInput) this.midiClockInput.onmidimessage = null;
|
if(this.midiClockInput && this.midiClockInputIndex!=this.currentInputIndex) this.midiClockInput.onmidimessage = null;
|
||||||
this.midiClockInput = undefined;
|
this.midiClockInput = undefined;
|
||||||
this.settings.midi_clock_input = undefined;
|
this.settings.midi_clock_input = undefined;
|
||||||
} else {
|
} else {
|
||||||
const clockInputIndex = parseInt(value);
|
const clockInputIndex = parseInt(value);
|
||||||
if(this.midiClockInput) this.midiClockInput.onmidimessage = null;
|
this.midiClockInputIndex = clockInputIndex;
|
||||||
|
if(this.midiClockInput && this.midiClockInputIndex!=this.currentInputIndex) this.midiClockInput.onmidimessage = null;
|
||||||
this.midiClockInput = this.midiInputs[clockInputIndex];
|
this.midiClockInput = this.midiInputs[clockInputIndex];
|
||||||
this.registerMidiClockListener();
|
this.registerMidiInputListener(clockInputIndex);
|
||||||
this.settings.midi_clock_input = this.midiClockInput.name || undefined;
|
this.settings.midi_clock_input = this.midiClockInput.name || undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -208,10 +216,14 @@ export class MidiConnection {
|
|||||||
// Add mini input listener
|
// Add mini input listener
|
||||||
midiInputSelect.addEventListener("change", (event) => {
|
midiInputSelect.addEventListener("change", (event) => {
|
||||||
const value = (event.target as HTMLSelectElement).value;
|
const value = (event.target as HTMLSelectElement).value;
|
||||||
if(value) {
|
if(value === "-1") {
|
||||||
this.unregisterMidiInputListener();
|
if(this.currentInputIndex && this.currentInputIndex!=this.midiClockInputIndex) this.unregisterMidiInputListener(this.currentInputIndex);
|
||||||
|
this.currentInputIndex = undefined;
|
||||||
|
this.settings.default_midi_input = undefined;
|
||||||
|
} else {
|
||||||
|
if(this.currentInputIndex && this.currentInputIndex!=this.midiClockInputIndex) this.unregisterMidiInputListener(this.currentInputIndex);
|
||||||
this.currentInputIndex = parseInt(value);
|
this.currentInputIndex = parseInt(value);
|
||||||
this.registerMidiInputListener();
|
this.registerMidiInputListener(this.currentInputIndex);
|
||||||
this.settings.default_midi_input = this.midiInputs[this.currentInputIndex].name || undefined;
|
this.settings.default_midi_input = this.midiInputs[this.currentInputIndex].name || undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -219,71 +231,62 @@ export class MidiConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerMidiClockListener(): void {
|
public registerMidiInputListener(inputIndex: number): void {
|
||||||
/**
|
|
||||||
* Registers a listener for MIDI clock messages on the currently selected MIDI input.
|
|
||||||
*/
|
|
||||||
if (this.midiClockInput) {
|
|
||||||
this.midiClockInput.onmidimessage = (event: Event) => {
|
|
||||||
const message = event as MIDIMessageEvent;
|
|
||||||
if (message.data[0] === 0xf8) {
|
|
||||||
if(this.skipOnError>0) {
|
|
||||||
this.skipOnError -= 1;
|
|
||||||
} else {
|
|
||||||
this.onMidiClock(event.timeStamp);
|
|
||||||
}
|
|
||||||
} else if(message.data[0] === 0xfa) {
|
|
||||||
console.log("MIDI start received");
|
|
||||||
this.api.stop();
|
|
||||||
this.api.play();
|
|
||||||
} else if(message.data[0] === 0xfc) {
|
|
||||||
console.log("MIDI stop received");
|
|
||||||
this.api.pause();
|
|
||||||
} else if(message.data[0] === 0xfb) {
|
|
||||||
console.log("MIDI continue received");
|
|
||||||
this.api.play();
|
|
||||||
} else if(message.data[0] === 0xfe) {
|
|
||||||
console.log("MIDI active sensing received");
|
|
||||||
} else {
|
|
||||||
// Ignore other MIDI messages
|
|
||||||
// console.log("Ignored MIDI message: ", message.data[0], message.data[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public registerMidiInputListener(): void {
|
|
||||||
/**
|
/**
|
||||||
* Register midi input listener and store last value as global parameter named channel_{number}
|
* Register midi input listener and store last value as global parameter named channel_{number}
|
||||||
*/
|
*/
|
||||||
if(this.currentInputIndex !== undefined) {
|
if(inputIndex !== undefined) {
|
||||||
const input = this.midiInputs[this.currentInputIndex];
|
const input = this.midiInputs[inputIndex];
|
||||||
if(input) {
|
if(input && !input.onmidimessage) {
|
||||||
input.onmidimessage = (event: Event) => {
|
input.onmidimessage = (event: Event) => {
|
||||||
const message = event as MIDIMessageEvent;
|
const message = event as MIDIMessageEvent;
|
||||||
|
/* MIDI CLOCK */
|
||||||
// List of all note_on messages from channels 1-16
|
if(input.name === this.settings.midi_clock_input) {
|
||||||
const all_note_ons = [0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,0x9E, 0x9F];
|
if (message.data[0] === 0xf8) {
|
||||||
// If message is one of note ons
|
if(this.skipOnError>0) {
|
||||||
if(all_note_ons.indexOf(message.data[0]) !== -1) {
|
this.skipOnError -= 1;
|
||||||
const channel = all_note_ons.indexOf(message.data[0])+1;
|
} else {
|
||||||
const note = message.data[1];
|
this.onMidiClock(event.timeStamp);
|
||||||
const velocity = message.data[2];
|
}
|
||||||
this.api.variable(`channel_${channel}_note`, note);
|
} else if(message.data[0] === 0xfa) {
|
||||||
this.api.variable(`channel_${channel}_velocity`, velocity);
|
console.log("MIDI start received");
|
||||||
if(this.settings.midi_channels_scripts) this.api.script(channel);
|
this.api.stop();
|
||||||
|
this.api.play();
|
||||||
|
} else if(message.data[0] === 0xfc) {
|
||||||
|
console.log("MIDI stop received");
|
||||||
|
this.api.pause();
|
||||||
|
} else if(message.data[0] === 0xfb) {
|
||||||
|
console.log("MIDI continue received");
|
||||||
|
this.api.play();
|
||||||
|
} else if(message.data[0] === 0xfe) {
|
||||||
|
console.log("MIDI active sensing received");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* DEFAULT MIDI INPUT */
|
||||||
|
if(input.name === this.settings.default_midi_input) {
|
||||||
|
// 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 {
|
public unregisterMidiInputListener(inputIndex: number): void {
|
||||||
/**
|
/**
|
||||||
* Unregister midi input listener
|
* Unregister midi input listener
|
||||||
*/
|
*/
|
||||||
if(this.currentInputIndex !== undefined) {
|
if(inputIndex !== undefined) {
|
||||||
const input = this.midiInputs[this.currentInputIndex];
|
const input = this.midiInputs[inputIndex];
|
||||||
if(input) {
|
if(input) {
|
||||||
input.onmidimessage = null;
|
input.onmidimessage = null;
|
||||||
}
|
}
|
||||||
@ -311,7 +314,6 @@ export class MidiConnection {
|
|||||||
console.log("Last delta: ", this.midiClockDelta);
|
console.log("Last delta: ", this.midiClockDelta);
|
||||||
console.log("Current delta: ", timestamp - this.lastTimestamp);
|
console.log("Current delta: ", timestamp - this.lastTimestamp);
|
||||||
console.log("BPMs", this.clockBuffer);
|
console.log("BPMs", this.clockBuffer);
|
||||||
console.log("Deltas", this.deltaBuffer);
|
|
||||||
this.clockErrorCount = 0;
|
this.clockErrorCount = 0;
|
||||||
/* I dont know why this happens. But when it does, deltas for the following messages are off.
|
/* 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 */
|
So skipping ~ quarted of clock resolution usually helps */
|
||||||
|
|||||||
Reference in New Issue
Block a user