import { AudibleEvent } from "./AbstractEvents"; import { type Editor } from "../main"; import { MidiConnection } from "../IO/MidiConnection"; import { resolvePitchClass } from "zifferjs"; import { filterObject, arrayOfObjectsToObjectWithArrays, objectWithArraysToArrayOfObjects, maybeAtomic, } from "../Utils/Generic"; export type MidiParams = { note: number; bend?: number; channel?: number; port?: number; sustain?: number; velocity?: number; }; export class MidiEvent extends AudibleEvent { midiConnection: MidiConnection; constructor( input: MidiParams, public app: Editor, ) { super(app); this.values = input; this.midiConnection = app.api.MidiConnection; } sustain = (value: number | number[]): this => { this.values["sustain"] = value; return this; }; velocity = (value: number | number[]): this => { this.values["velocity"] = value; return this; }; channel = (value: number | number[]): this => { this.values["channel"] = value; return this; }; port = (value: number | string | number[] | string[]): this => { if (typeof value === "string") { this.values["port"] = this.midiConnection.getMidiOutputIndex(value); } else if (Array.isArray(value)) { this.values["port"] = value.map((v) => typeof v === "string" ? this.midiConnection.getMidiOutputIndex(v) : v, ); } return this; }; add = (value: number): this => { this.values.note += value; return this; }; modify = (func: Function): this => { const funcResult = func(this); if (funcResult instanceof Object) { return funcResult; } else { func(this.values); return this.update(); } }; bend = (value: number): this => { this.values["bend"] = value; return this; }; random = (min: number = 0, max: number = 127): this => { min = Math.min(Math.max(min, 0), 127); max = Math.min(Math.max(max, 0), 127); this.values["note"] = Math.floor(this.randomGen() * (max - min + 1)) + min; return this; }; update = (): this => { const filteredValues = filterObject(this.values, [ "key", "pitch", "originalPitch", "parsedScale", "addedOctave" ]); const events = objectWithArraysToArrayOfObjects(filteredValues, [ "parsedScale", ]); events.forEach((soundEvent) => { const resolvedPitchClass = resolvePitchClass( (soundEvent.key || "C4"), (soundEvent.originalPitch || soundEvent.pitch || 0), (soundEvent.parsedScale || soundEvent.scale || "MAJOR"), (soundEvent.addedOctave || 0) ); soundEvent.note = resolvedPitchClass.note; soundEvent.pitch = resolvedPitchClass.pitch; soundEvent.octave = resolvedPitchClass.octave; }); const newArrays = arrayOfObjectsToObjectWithArrays(events) as MidiParams; this.values.note = maybeAtomic(newArrays.note); if (newArrays.bend) this.values.bend = maybeAtomic(newArrays.bend); return this; }; out = (): void => { function play(event: MidiEvent, params: MidiParams): void { const channel = params.channel ? params.channel : 0; const velocity = params.velocity ? params.velocity : 100; const note = params.note ? params.note : 60; const sustain = params.sustain ? params.sustain * event.app.clock.pulse_duration * event.app.api.ppqn() : event.app.clock.pulse_duration * event.app.api.ppqn(); const bend = params.bend ? params.bend : undefined; const port = params.port ? event.midiConnection.getMidiOutputIndex(params.port) : event.midiConnection.getCurrentMidiPortIndex() || 0; event.midiConnection.sendMidiNote( note, channel, velocity, sustain, port, bend, ); } const events = objectWithArraysToArrayOfObjects(this.values, [ "parsedScale", ]) as MidiParams[]; events.forEach((p: MidiParams) => { play(this, p); }); }; }