Adding show scale method to interact with lighted keyboards

This commit is contained in:
2023-10-12 01:23:16 +03:00
parent 6b8d1b4608
commit 9c0f231bdc
5 changed files with 142 additions and 7 deletions

View File

@ -39,7 +39,7 @@
"tone": "^14.8.49",
"unique-names-generator": "^4.7.1",
"vite-plugin-markdown": "^2.1.0",
"zifferjs": "^0.0.32",
"zifferjs": "^0.0.33",
"zzfx": "^1.2.0"
}
}

View File

@ -1,4 +1,4 @@
import { seededRandom } from "zifferjs";
import { getAllScaleNotes, seededRandom } from "zifferjs";
import {
MidiCCEvent,
MidiConnection,
@ -63,6 +63,7 @@ export class UserAPI {
private errorTimeoutID: number = 0;
private printTimeoutID: number = 0;
public MidiConnection: MidiConnection;
public scale_aid: string|number|undefined = undefined;
load: samples;
constructor(public app: Editor) {
@ -586,6 +587,61 @@ export class UserAPI {
else return this.MidiConnection.ccInputBuffer.shift();
};
public show_scale = (
root: number|string,
scale: number|string,
channel: number = 0,
port: number|string = (this.MidiConnection.currentOutputIndex || 0),
soundOff: boolean = false): void => {
/**
* Sends given scale to midi output for visual aid
*/
if (!this.scale_aid || scale !== this.scale_aid) {
this.hide_scale(root,scale,channel,port);
const scaleNotes = getAllScaleNotes(scale, root);
// Send each scale note to current midi out
scaleNotes.forEach(note => {
this.MidiConnection.sendMidiOn(note, channel, 1, port);
if(soundOff) this.MidiConnection.sendAllSoundOff(channel, port);
});
this.scale_aid = scale;
}
}
public hide_scale = (
// @ts-ignore
root: number|string=0,
// @ts-ignore
scale: number|string=0,
channel: number = 0,
port: number|string = (this.MidiConnection.currentOutputIndex || 0)): void => {
/**
* Hides all notes by sending all notes off to midi output
*/
const allNotes = Array.from(Array(128).keys());
// Send each scale note to current midi out
allNotes.forEach(note => {
this.MidiConnection.sendMidiOff(note, channel, port);
});
this.scale_aid = undefined;
}
midi_notes_off = (channel: number = 0, port: number|string = (this.MidiConnection.currentOutputIndex || 0)): void => {
/**
* Sends all notes off to midi output
*/
this.MidiConnection.sendAllNotesOff(channel, port);
}
midi_sound_off = (channel: number = 0, port: number|string = (this.MidiConnection.currentOutputIndex || 0)): void => {
/**
* Sends all sound off to midi output
*/
this.MidiConnection.sendAllSoundOff(channel, port);
}
// =============================================================
// Ziffers related functions
// =============================================================

View File

@ -31,7 +31,7 @@ export class MidiConnection {
private settings: AppSettings;
private midiAccess: MIDIAccess | null = null;
public midiOutputs: MIDIOutput[] = [];
private currentOutputIndex: number = 0;
public currentOutputIndex: number = 0;
private scheduledNotes: { [noteNumber: number]: number } = {}; // { noteNumber: timeoutId }
/* Midi input */
@ -625,6 +625,64 @@ export class MidiConnection {
}
}
public sendMidiOn(note: number, channel: number, velocity: number, port: number | string = this.currentOutputIndex) {
/**
* Sending Midi Note on message
*/
if(typeof port === "string") port = this.getMidiOutputIndex(port);
const output = this.midiOutputs[port];
note = Math.min(Math.max(note, 0), 127);
if (output) {
const noteOnMessage = [0x90 + channel, note, velocity];
output.send(noteOnMessage);
} else {
console.error("MIDI output not available.");
}
}
sendMidiOff(note: number, channel: number, port: number | string = this.currentOutputIndex) {
/**
* Sending Midi Note off message
*/
if(typeof port === "string") port = this.getMidiOutputIndex(port);
const output = this.midiOutputs[port];
note = Math.min(Math.max(note, 0), 127);
if (output) {
const noteOffMessage = [0x80 + channel, note, 0];
output.send(noteOffMessage);
} else {
console.error("MIDI output not available.");
}
}
sendAllNotesOff(channel: number, port: number | string = this.currentOutputIndex) {
/**
* Sending Midi Note off message
*/
if(typeof port === "string") port = this.getMidiOutputIndex(port);
const output = this.midiOutputs[port];
if (output) {
const noteOffMessage = [0xb0 + channel, 123, 0];
output.send(noteOffMessage);
} else {
console.error("MIDI output not available.");
}
}
sendAllSoundOff(channel: number, port: number | string = this.currentOutputIndex) {
/**
* Sending all sound off
*/
if(typeof port === "string") port = this.getMidiOutputIndex(port);
const output = this.midiOutputs[port];
if (output) {
const noteOffMessage = [0xb0 + channel, 120, 0];
output.send(noteOffMessage);
} else {
console.error("MIDI output not available.");
}
}
public sendSysExMessage(message: number[]): void {
/**
* Sends a SysEx message to the currently selected MIDI output.

View File

@ -149,5 +149,26 @@ beat(.25) :: sound('sine')
`,
true
)}
## Scale output for lighted keys
Topos can output scales to external keyboards lighted keys using the following functions:
- <ic>show_scale(key: string, scale: string|int, channel?: number, port?: string|number, soundOff?: boolean): void</ic>: sends the scale as midi on messages to specified port and channel to light the keys of external keyboard. If soundOff is true, all sound off message will be sent after every note on message. This can be useful with some keyboards not supporting external channel for lightning or routing for the midi in to suppress the sound from incoming note on messages.
${makeExample(
"Show scale on external keyboard",
`show_scale("F","aeolian",0,4)`,
true
)}
${makeExample(
"Hide scale",
`hide_scale("F","aeolian",0,4)`,
true
)}
`
}

View File

@ -1451,10 +1451,10 @@ yaml@^2.1.1:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
zifferjs@^0.0.32:
version "0.0.32"
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.32.tgz#825ab27f890f6fc9315e350e9d6b7ea40bcdaa3e"
integrity sha512-jY7Jx1lFIa7+I606G0+SSYID5hUe8nYlzebtlLKmbuE1Fb1utReb6064auLcpUcRz+zoSBm2GDr6/JlZ4HpKJg==
zifferjs@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.33.tgz#286e3c9ccaebe88d4acaad59f99db0cff5cc1884"
integrity sha512-sYD4wOSKTip4GTkK3epMNAzlRWN3YpQlgR9sCGvyj6xdHzaaEHdU8F/PvkI092UbfYi4NNM/pVZKF1RLUlJPAw==
zzfx@^1.2.0:
version "1.2.0"