Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
export {
MixerChannelOptionsSchema,
MixerChannelSchema,
MixerPluginOptionsSchema,
MixerPresetDataSchema,
MixerSendsSchema,
} from "./schemas.js";

View File

@@ -0,0 +1,65 @@
# `mixer/schemas`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Zod schemas validating the construction-time options, channel strips, and preset payload of `MixerPlugin`. These run on the public boundary between user code and the plugin: any malformed object hitting the constructor or `applyPreset` is rejected with `AUDIO_FX.INVALID_OPTIONS` / `AUDIO_FX.INVALID_PRESET` before it can corrupt runtime state.
Separating schemas from the plugin module lets `index.ts` re-export them so callers can introspect the option surface (for forms, RPC validation, or storybook controls) without importing the audio implementation.
## Features
- `MixerSendsSchema` — `z.record(string.min(1), number.finite().min(0).max(2))`. Send amounts are bounded above at `2` to allow modest gain at the bus, but not unbounded amplification.
- `MixerChannelSchema` — full live strip schema: id, gain (0..4), pan (-1..1), sends.
- `MixerChannelOptionsSchema` — boundary schema with `initialX` half-twins of the live fields.
- `MixerPluginOptionsSchema` — `channels` array constrained to 1..64 plus optional `initialMasterGain`.
- `MixerPresetDataSchema` — `.strict()` object carrying the full live channel strips.
## Usage: Basic Example
```ts
import { MixerPluginOptionsSchema } from "@suaveplan/audio-fx-plugin-mixer";
const parsed = MixerPluginOptionsSchema.parse({
channels: [{ id: "drums" }, { id: "vocal" }],
});
```
## Usage: Advanced Example
```ts
import {
MixerChannelSchema,
MixerPresetDataSchema,
MixerSendsSchema,
} from "@suaveplan/audio-fx-plugin-mixer";
// Validate an external send-map blob before forwarding to the plugin.
function isSendMap(blob: unknown): boolean {
return MixerSendsSchema.safeParse(blob).success;
}
// Strict preset validation at the IPC boundary.
function isMixerPreset(blob: unknown): boolean {
return MixerPresetDataSchema.safeParse(blob).success;
}
```
## API Reference
- `MixerSendsSchema: z.ZodRecord<string, number>`
- `MixerChannelSchema: z.ZodObject<{ id, gain, pan, sends }>`
- `MixerChannelOptionsSchema: z.ZodObject<{ id, initialGain?, initialPan?, initialSends? }>`
- `MixerPluginOptionsSchema: z.ZodObject<{ channels, initialMasterGain? }>`
- `MixerPresetDataSchema: z.ZodObject<{ channels }, "strict">`
## Implementation Notes
The channel-count cap of 64 reflects two practical constraints: a typical mixing console UI struggles to render more than 32 strips legibly, and a per-channel loop over 64 entries per sample is already at the upper end of what a real-time block-processing budget can absorb on a typical CPU. Going past 64 strips is usually a sign that the caller should be using a submix architecture instead.
`MixerPresetDataSchema` uses `.strict()` so unknown fields are rejected — matching the sibling-plugin policy. The schema validates the full live `MixerChannel` shape (not just the construction-time half) because presets restore the runtime state directly, bypassing the `initialX → gain` mapping the constructor does.
The send-amount upper bound of `2` lets a bus send slightly boost the signal (useful for parallel-compression busses where the dry sum is too quiet) but blocks pathological values that would silently drown the master. The lower bound of `0` allows a "mute send" without removing the entry from the sends map, so a UI animating a fader to zero can resume from the same state.

View File

@@ -0,0 +1,31 @@
import z from "zod";
export const MixerSendsSchema = z.record(
z.string().min(1),
z.number().finite().min(0).max(2)
);
export const MixerChannelSchema = z.object({
id: z.string().min(1),
gain: z.number().finite().min(0).max(4),
pan: z.number().finite().min(-1).max(1),
sends: MixerSendsSchema,
});
export const MixerChannelOptionsSchema = z.object({
id: z.string().min(1),
initialGain: z.number().finite().min(0).max(4).optional(),
initialPan: z.number().finite().min(-1).max(1).optional(),
initialSends: MixerSendsSchema.optional(),
});
export const MixerPluginOptionsSchema = z.object({
channels: z.array(MixerChannelOptionsSchema).min(1).max(64),
initialMasterGain: z.number().finite().min(0).max(4).optional(),
});
export const MixerPresetDataSchema = z
.object({
channels: z.array(MixerChannelSchema).min(1).max(64),
})
.strict();