// The track-render instrument contract shared between midiboot and the // instrument web apps. Each simulated-instrument repo carries its own // structurally identical copy of these types (there is no shared package; // TypeScript's structural typing keeps the copies interchangeable), and // midiboot's src/instruments/types.ts is the reference. // // An instrument receives an entire MIDI track per call: every note with // times already in seconds. It owns its rendering strategy end to end, // including any internal caching (per-note, per-stage, or none), progress // reporting, and cancellation. The optional fields at the bottom exist for // the instrument's own web app, which drives the full parameter surface // and live-edited MATLAB sources; midiboot never sets them. export interface NoteSpec { timeSec: number; durationSec: number; pitch: number; // MIDI note number velocity: number; // 0..1 } /** A user-editable instrument parameter, declared by the instrument. */ export interface KnobSpec { key: string; label: string; min: number; max: number; default: number; step?: number; scale?: 'log'; } /** One MATLAB source file, as numbl consumes it. */ export interface MFile { name: string; source: string; } export interface RenderProgress { /** 0..1 through the whole request. */ frac: number; /** Short human-readable stage label, e.g. "note 3/8: solve". */ stage: string; } export interface TrackRenderRequest { /** Every note of the track, sorted by timeSec. */ notes: NoteSpec[]; sampleRate: number; /** Song length incl. tail, as a minimum: the returned buffer may be * longer, so a ring that outlives the last note (a cymbal crash, a * long bass tail) is kept in full; the host mixes buffers of unequal * lengths. */ lengthSamples: number; /** Fast preview for playback, full quality for export. */ quality: 'preview' | 'full'; /** Values for the instrument's knobs; missing keys mean the default. */ settings?: Record; /** Cooperative cancellation; checked between stages and notes. */ signal?: AbortSignal; onProgress?: (p: RenderProgress) => void; // --- fields used only by the instrument's own web app --- /** Full model parameter overrides, applied after the defaults (and after * any note mapping). midiboot never sets this. */ params?: Record; /** Live-edited MATLAB sources replacing the shipped ones. */ mfiles?: MFile[]; /** Also compute the instrument-specific diagnostics payload. */ wantDiagnostics?: boolean; } export interface TrackRenderResult { audio: Float32Array; /** Instrument-specific diagnostics; present only when requested. */ diag?: unknown; } export interface LiveVoice { noteOff(): void; // begin release cut(): void; // stop immediately } export interface Instrument { id: string; name: string; /** Curated editable parameters; tracks store their values in settings. */ knobs?: KnobSpec[]; renderTrack(req: TrackRenderRequest): Promise; noteOn?(ctx: BaseAudioContext, dest: AudioNode, pitch: number, velocity: number, whenSec: number): LiveVoice; }