88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
import type {
|
|
AudioFxPlugin,
|
|
ParameterCurve,
|
|
} from "@suaveplan/audio-fx-plugin-base";
|
|
import { useCallback, useSyncExternalStore } from "react";
|
|
import {
|
|
REACT_AUDIO_CODES,
|
|
ReactAudioError,
|
|
} from "../../error-codes/error-codes.js";
|
|
import { useProviderStateOrThrow } from "../use-provider-state.js";
|
|
|
|
export interface UseParameterResult {
|
|
readonly value: number;
|
|
set(value: number): void;
|
|
curve(curve: ParameterCurve): void;
|
|
scheduleAt(value: number, transportTimeSec: number): void;
|
|
}
|
|
|
|
/**
|
|
* Reads, writes, curves, and schedules a single plugin parameter.
|
|
*
|
|
* - `value` is sampled at the provider's `sampleRateHz` (default 60),
|
|
* matching the per-block parameter automation cadence to the UI's
|
|
* frame budget.
|
|
* - `set` calls `plugin.setParameter`.
|
|
* - `curve` calls `plugin.setParameterCurve`.
|
|
* - `scheduleAt` calls `plugin.setParameterAt`.
|
|
*
|
|
* Throws `REACT_AUDIO.UNKNOWN_PARAMETER` synchronously on first
|
|
* render when the supplied `paramId` is not present in the plugin's
|
|
* `parameterIndex`.
|
|
*/
|
|
export function useParameter(
|
|
plugin: AudioFxPlugin,
|
|
paramId: string
|
|
): UseParameterResult {
|
|
const { sampleRateHz } = useProviderStateOrThrow();
|
|
|
|
if (!plugin.parameterIndex.has(paramId)) {
|
|
throw new ReactAudioError(
|
|
REACT_AUDIO_CODES.UNKNOWN_PARAMETER,
|
|
"react-audio.unknown-parameter",
|
|
{
|
|
context: {
|
|
paramId,
|
|
pluginId: plugin.metadata.id,
|
|
available: Array.from(plugin.parameterIndex.keys()),
|
|
},
|
|
}
|
|
);
|
|
}
|
|
|
|
const subscribe = useCallback(
|
|
(listener: () => void) => {
|
|
const interval = Math.max(1, Math.floor(1000 / sampleRateHz));
|
|
const id = setInterval(() => {
|
|
listener();
|
|
}, interval);
|
|
return () => {
|
|
clearInterval(id);
|
|
};
|
|
},
|
|
[sampleRateHz]
|
|
);
|
|
|
|
const getSnapshot = useCallback(
|
|
() => plugin.getParameter(paramId),
|
|
[plugin, paramId]
|
|
);
|
|
|
|
const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
|
|
const set = useCallback<UseParameterResult["set"]>(
|
|
(v) => plugin.setParameter(paramId, v),
|
|
[plugin, paramId]
|
|
);
|
|
const curve = useCallback<UseParameterResult["curve"]>(
|
|
(c) => plugin.setParameterCurve(paramId, c),
|
|
[plugin, paramId]
|
|
);
|
|
const scheduleAt = useCallback<UseParameterResult["scheduleAt"]>(
|
|
(v, t) => plugin.setParameterAt(paramId, v, t),
|
|
[plugin, paramId]
|
|
);
|
|
|
|
return { value, set, curve, scheduleAt };
|
|
}
|