Initial Commit
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
import type { PluginParameter } from "@suaveplan/audio-fx-plugin-base/base";
|
||||
import {
|
||||
SPESSASYNTH_CODES,
|
||||
SpessaSynthError,
|
||||
} from "../error-codes/error-codes.js";
|
||||
|
||||
export const CHANNEL_COUNT = 16;
|
||||
export const MAX_CHANNEL_INDEX = CHANNEL_COUNT - 1;
|
||||
|
||||
/** Per-channel parameter kind. */
|
||||
export type ChannelParamKind =
|
||||
| "volume"
|
||||
| "pan"
|
||||
| "program"
|
||||
| "reverbSend"
|
||||
| "chorusSend";
|
||||
|
||||
/** Closed enum of per-channel kinds in declaration order. */
|
||||
export const CHANNEL_PARAM_KINDS: ReadonlyArray<ChannelParamKind> = [
|
||||
"volume",
|
||||
"pan",
|
||||
"program",
|
||||
"reverbSend",
|
||||
"chorusSend",
|
||||
];
|
||||
|
||||
/** Build a deterministic parameter id from a channel + kind. */
|
||||
export function channelParameterId(
|
||||
channel: number,
|
||||
kind: ChannelParamKind
|
||||
): string {
|
||||
return `channel/${channel}/${kind}`;
|
||||
}
|
||||
|
||||
/** Master-gain parameter id. */
|
||||
export const MASTER_GAIN_ID = "master/gain";
|
||||
|
||||
/**
|
||||
* Build the full 16-channel × 5-kind + master parameter declaration
|
||||
* the `AudioFxPlugin` constructor consumes. Every parameter is
|
||||
* `automatable: true` so the host chain can drive it.
|
||||
*/
|
||||
export function buildParameterDeclarations(): ReadonlyArray<PluginParameter> {
|
||||
const out: PluginParameter[] = [];
|
||||
for (let ch = 0; ch < CHANNEL_COUNT; ch++) {
|
||||
out.push({
|
||||
id: channelParameterId(ch, "volume"),
|
||||
name: `Ch ${ch + 1} Volume`,
|
||||
value: 1,
|
||||
min: 0,
|
||||
max: 1,
|
||||
default: 1,
|
||||
unit: "ratio",
|
||||
automatable: true,
|
||||
});
|
||||
out.push({
|
||||
id: channelParameterId(ch, "pan"),
|
||||
name: `Ch ${ch + 1} Pan`,
|
||||
value: 0,
|
||||
min: -1,
|
||||
max: 1,
|
||||
default: 0,
|
||||
unit: "ratio",
|
||||
automatable: true,
|
||||
});
|
||||
out.push({
|
||||
id: channelParameterId(ch, "program"),
|
||||
name: `Ch ${ch + 1} Program`,
|
||||
value: 0,
|
||||
min: 0,
|
||||
max: 127,
|
||||
default: 0,
|
||||
unit: "enum",
|
||||
automatable: false,
|
||||
});
|
||||
out.push({
|
||||
id: channelParameterId(ch, "reverbSend"),
|
||||
name: `Ch ${ch + 1} Reverb Send`,
|
||||
value: 0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
default: 0,
|
||||
unit: "ratio",
|
||||
automatable: true,
|
||||
});
|
||||
out.push({
|
||||
id: channelParameterId(ch, "chorusSend"),
|
||||
name: `Ch ${ch + 1} Chorus Send`,
|
||||
value: 0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
default: 0,
|
||||
unit: "ratio",
|
||||
automatable: true,
|
||||
});
|
||||
}
|
||||
out.push({
|
||||
id: MASTER_GAIN_ID,
|
||||
name: "Master Gain",
|
||||
value: 1,
|
||||
min: 0,
|
||||
max: 4,
|
||||
default: 1,
|
||||
unit: "ratio",
|
||||
automatable: true,
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a `channel/<n>/<kind>` parameter id into its components. Returns
|
||||
* `undefined` for the master id or any unrecognised pattern; the caller
|
||||
* decides whether that's an error.
|
||||
*/
|
||||
export function parseChannelParameterId(
|
||||
id: string
|
||||
): { channel: number; kind: ChannelParamKind } | undefined {
|
||||
if (!id.startsWith("channel/")) return undefined;
|
||||
const rest = id.slice("channel/".length);
|
||||
const slash = rest.indexOf("/");
|
||||
if (slash < 0) return undefined;
|
||||
const channelStr = rest.slice(0, slash);
|
||||
const kind = rest.slice(slash + 1) as ChannelParamKind;
|
||||
const channel = Number.parseInt(channelStr, 10);
|
||||
if (
|
||||
!Number.isInteger(channel) ||
|
||||
channel < 0 ||
|
||||
channel > MAX_CHANNEL_INDEX
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
if (!CHANNEL_PARAM_KINDS.includes(kind)) return undefined;
|
||||
return { channel, kind };
|
||||
}
|
||||
|
||||
/** Throws `SPESSASYNTH.INVALID_CHANNEL` when channel is out of range. */
|
||||
export function assertChannel(channel: number): void {
|
||||
if (
|
||||
!Number.isInteger(channel) ||
|
||||
channel < 0 ||
|
||||
channel > MAX_CHANNEL_INDEX
|
||||
) {
|
||||
throw new SpessaSynthError(
|
||||
SPESSASYNTH_CODES.INVALID_CHANNEL,
|
||||
"spessasynth.invalid-channel",
|
||||
{ context: { channel } }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user