/** * What a scene *is*: a list of vibrating objects, a body for them to sound * over, and nothing else. Everything the app builds — the generated model .m, * the generated scene .m, the op instances, the sliders — is derived from * this description (src/studio/codegen.ts), so this file is the studio's * whole vocabulary. * * Two kinds of number live here, and the split decides what an edit costs: * * - `size` fields are baked into the grids and the coupling ops, so changing * one recompiles (a fresh session, like changing the air grid). * - `params` (model parameters: pitches, decays, gains) are GPU uniforms — * free to move — and `place` (placement in the air: an object's y offset * and height, the body's shape) are scene parameters, which cost one CPU * re-evaluation of the scene .m and a few buffer uploads. Neither * recompiles. * * Object ids are short and stable ('s1', 'm2'); the code generator suffixes * every per-object name with them, so the generated MATLAB stays readable in * the Lab view. */ export type Params = Record; /** A tunable scalar and its slider. */ export interface ParamSpec { key: string; label: string; value: number; min: number; max: number; step: number; /** Shown as a tooltip. */ hint?: string; /** Shown in the Play layer (the handful of knobs a player would reach * for); everything else appears only in Build. */ musical?: boolean; } export type ObjectType = 'string' | 'bar' | 'membrane'; export interface ObjectSpec { /** Unique in the scene: 's1', 'b1', 'm1', ... */ id: string; type: ObjectType; /** Baked extents, metres: L1 along x (and L2 along y, for a membrane). */ size: { L1: number; L2?: number }; /** Model parameter values, by unsuffixed key (f0, t60, ...). */ params: Params; /** Placement values, by unsuffixed key (oy, oz). Scene parameters. */ place: Params; } export type BodyType = 'none' | 'box' | 'shell'; export interface BodySpec { type: BodyType; params: Params; } export interface SceneSpec { objects: ObjectSpec[]; body: BodySpec; } export const defaultsOf = (specs: ParamSpec[]): Params => Object.fromEntries(specs.map((p) => [p.key, p.value])); /** The suffixed name a per-object thing gets in the generated MATLAB. */ export const suffixed = (key: string, id: string): string => `${key}_${id}`;