Some functions
This commit is contained in:
207
src/API.ts
207
src/API.ts
@ -3,16 +3,121 @@ import { tryEvaluate } from "./Evaluator";
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { ZZFX, zzfx } from "zzfx";
|
import { ZZFX, zzfx } from "zzfx";
|
||||||
|
|
||||||
|
class MidiConnection{
|
||||||
|
private midiAccess: MIDIAccess | null = null;
|
||||||
|
private midiOutputs: MIDIOutput[] = [];
|
||||||
|
private currentOutputIndex: number = 0;
|
||||||
|
private scheduledNotes: { [noteNumber: number]: number } = {}; // { noteNumber: timeoutId }
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.initializeMidiAccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeMidiAccess(): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.midiAccess = await navigator.requestMIDIAccess();
|
||||||
|
this.midiOutputs = Array.from(this.midiAccess.outputs.values());
|
||||||
|
if (this.midiOutputs.length === 0) {
|
||||||
|
console.warn('No MIDI outputs available.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize MIDI:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCurrentMidiPort(): string | null {
|
||||||
|
if (this.midiOutputs.length > 0 && this.currentOutputIndex >= 0 && this.currentOutputIndex < this.midiOutputs.length) {
|
||||||
|
return this.midiOutputs[this.currentOutputIndex].name;
|
||||||
|
} else {
|
||||||
|
console.error('No MIDI output selected or available.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public sendMidiClock(): void {
|
||||||
|
const output = this.midiOutputs[this.currentOutputIndex];
|
||||||
|
if (output) {
|
||||||
|
output.send([0xF8]); // Send a single MIDI clock message
|
||||||
|
} else {
|
||||||
|
console.error('MIDI output not available.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public switchMidiOutput(outputName: string): boolean {
|
||||||
|
const index = this.midiOutputs.findIndex((output) => output.name === outputName);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.currentOutputIndex = index;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.error(`MIDI output "${outputName}" not found.`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public listMidiOutputs(): void {
|
||||||
|
console.log('Available MIDI Outputs:');
|
||||||
|
this.midiOutputs.forEach((output, index) => {
|
||||||
|
console.log(`${index + 1}. ${output.name}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendMidiNote(noteNumber: number, velocity: number, durationMs: number): void {
|
||||||
|
const output = this.midiOutputs[this.currentOutputIndex];
|
||||||
|
if (output) {
|
||||||
|
const noteOnMessage = [0x90, noteNumber, velocity];
|
||||||
|
const noteOffMessage = [0x80, noteNumber, 0];
|
||||||
|
|
||||||
|
// Send Note On
|
||||||
|
output.send(noteOnMessage);
|
||||||
|
|
||||||
|
// Schedule Note Off
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
output.send(noteOffMessage);
|
||||||
|
delete this.scheduledNotes[noteNumber];
|
||||||
|
}, durationMs);
|
||||||
|
|
||||||
|
this.scheduledNotes[noteNumber] = timeoutId;
|
||||||
|
} else {
|
||||||
|
console.error('MIDI output not available.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendMidiControlChange(controlNumber: number, value: number): void {
|
||||||
|
const output = this.midiOutputs[this.currentOutputIndex];
|
||||||
|
if (output) {
|
||||||
|
output.send([0xB0, controlNumber, value]); // Control Change
|
||||||
|
} else {
|
||||||
|
console.error('MIDI output not available.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public panic(): void {
|
||||||
|
const output = this.midiOutputs[this.currentOutputIndex];
|
||||||
|
if (output) {
|
||||||
|
for (const noteNumber in this.scheduledNotes) {
|
||||||
|
const timeoutId = this.scheduledNotes[noteNumber];
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
output.send([0x80, parseInt(noteNumber), 0]); // Note Off
|
||||||
|
}
|
||||||
|
this.scheduledNotes = {};
|
||||||
|
} else {
|
||||||
|
console.error('MIDI output not available.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class UserAPI {
|
export class UserAPI {
|
||||||
|
|
||||||
variables: { [key: string]: any } = {}
|
variables: { [key: string]: any } = {}
|
||||||
globalGain: GainNode
|
globalGain: GainNode
|
||||||
audioNodes: AudioNode[] = []
|
audioNodes: AudioNode[] = []
|
||||||
|
MidiConnection: MidiConnection = new MidiConnection()
|
||||||
|
|
||||||
constructor(public app: Editor) {
|
constructor(public app: Editor) {
|
||||||
this.globalGain = this.app.audioContext.createGain()
|
this.globalGain = this.app.audioContext.createGain()
|
||||||
// Give default parameters to the reverb
|
// Give default parameters to the reverb
|
||||||
|
|
||||||
this.globalGain.gain.value = 0.2;
|
this.globalGain.gain.value = 0.2;
|
||||||
this.globalGain.connect(this.app.audioContext.destination)
|
this.globalGain.connect(this.app.audioContext.destination)
|
||||||
}
|
}
|
||||||
@ -22,19 +127,99 @@ export class UserAPI {
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
killAll() {
|
// =============================================================
|
||||||
|
// Utility functions
|
||||||
|
// =============================================================
|
||||||
|
log = console.log
|
||||||
|
|
||||||
|
public killAll():void {
|
||||||
this.audioNodes.forEach(node => {
|
this.audioNodes.forEach(node => {
|
||||||
node.disconnect()
|
node.disconnect()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var(name: string, value: any) {
|
// Web Audio Gain and Node Management
|
||||||
this.variables[name] = value
|
mute():void {
|
||||||
|
this.globalGain.gain.value = 0
|
||||||
}
|
}
|
||||||
get(name: string) { return this.variables[name] }
|
|
||||||
|
volume(volume: number):void {
|
||||||
|
this.globalGain.gain.value = volume
|
||||||
|
}
|
||||||
|
|
||||||
|
vol = this.volume
|
||||||
|
|
||||||
|
|
||||||
|
// =============================================================
|
||||||
|
// MIDI related functions
|
||||||
|
// =============================================================
|
||||||
|
|
||||||
|
public midi_outputs(): void {
|
||||||
|
console.log(this.MidiConnection.listMidiOutputs())
|
||||||
|
}
|
||||||
|
|
||||||
|
public midi_output(outputName: string): void {
|
||||||
|
if (!outputName) {
|
||||||
|
console.log(this.MidiConnection.getCurrentMidiPort())
|
||||||
|
} else {
|
||||||
|
this.MidiConnection.switchMidiOutput(outputName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public midi_connect(outputName: string): void {
|
||||||
|
this.MidiConnection.switchMidiOutput(outputName)
|
||||||
|
}
|
||||||
|
|
||||||
|
public note(note: number, velocity: number, duration: number): void {
|
||||||
|
this.MidiConnection.sendMidiNote(
|
||||||
|
note, velocity, duration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public midi_clock(): void {
|
||||||
|
this.MidiConnection.sendMidiClock()
|
||||||
|
}
|
||||||
|
|
||||||
|
public cc(control: number, value: number): void {
|
||||||
|
this.MidiConnection.sendMidiControlChange(control, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
public midi_panic(): void {
|
||||||
|
this.MidiConnection.panic()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =============================================================
|
||||||
|
// Variable related functions
|
||||||
|
// =============================================================
|
||||||
|
|
||||||
|
public var(a: number | string, b?: number): number {
|
||||||
|
if (typeof a === 'string' && b === undefined) {
|
||||||
|
return this.variables[a]
|
||||||
|
} else {
|
||||||
|
this.variables[a] = b
|
||||||
|
return this.variables[a]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delVar(name: string): void {
|
||||||
|
delete this.variables[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
public cleanVar(): void {
|
||||||
|
this.variables = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================
|
||||||
|
// Small algorithmic functions
|
||||||
|
// =============================================================
|
||||||
|
|
||||||
pick<T>(...array: T[]): T { return array[Math.floor(Math.random() * array.length)] }
|
pick<T>(...array: T[]): T { return array[Math.floor(Math.random() * array.length)] }
|
||||||
|
|
||||||
|
seqbeat<T>(...array: T[]): T { return array[this.app.clock.time_position.beat % array.length] }
|
||||||
|
seqbar<T>(...array: T[]): T { return array[this.app.clock.time_position.bar % array.length] }
|
||||||
|
seqpulse<T>(...array: T[]): T { return array[this.app.clock.time_position.pulse % array.length] }
|
||||||
|
|
||||||
bpm(bpm: number) { this.app.clock.bpm = bpm }
|
bpm(bpm: number) { this.app.clock.bpm = bpm }
|
||||||
|
|
||||||
time_signature(numerator: number, denominator: number) {
|
time_signature(numerator: number, denominator: number) {
|
||||||
@ -66,10 +251,12 @@ export class UserAPI {
|
|||||||
beat(...beat: number[]): boolean {
|
beat(...beat: number[]): boolean {
|
||||||
return (
|
return (
|
||||||
beat.includes(this.app.clock.time_position.beat)
|
beat.includes(this.app.clock.time_position.beat)
|
||||||
&& this.app.clock.time_position.pulse == 1
|
&& this.app.clock.time_position.pulse == 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
every(n: number): boolean { return this.i % n === 0 }
|
||||||
|
|
||||||
pulse(...pulse: number[]) {
|
pulse(...pulse: number[]) {
|
||||||
return pulse.includes(this.app.clock.time_position.pulse) && this.app.clock.time_position.pulse == 1
|
return pulse.includes(this.app.clock.time_position.pulse) && this.app.clock.time_position.pulse == 1
|
||||||
}
|
}
|
||||||
@ -78,14 +265,6 @@ export class UserAPI {
|
|||||||
return this.app.clock.time_position.pulse % pulse === 0
|
return this.app.clock.time_position.pulse % pulse === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
mute() {
|
|
||||||
this.globalGain.gain.value = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
volume(volume: number) {
|
|
||||||
this.globalGain.gain.value = volume
|
|
||||||
}
|
|
||||||
vol = this.volume
|
|
||||||
|
|
||||||
|
|
||||||
beep(
|
beep(
|
||||||
|
|||||||
Reference in New Issue
Block a user