Initial Commit
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
export {
|
||||
MixerChannelOptionsSchema,
|
||||
MixerChannelSchema,
|
||||
MixerPluginOptionsSchema,
|
||||
MixerPresetDataSchema,
|
||||
MixerSendsSchema,
|
||||
} from "./schemas.js";
|
||||
@@ -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.
|
||||
@@ -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();
|
||||
Reference in New Issue
Block a user