@webaudiomodules/sdk-parammgr
Version:
Parameter Manager SDK for WebAudioModules Plugin
1,234 lines (1,225 loc) • 39.8 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// src/CompositeAudioNode.js
var CompositeAudioNode = class extends GainNode {
constructor() {
super(...arguments);
/**
* @type {AudioNode}
*/
__publicField(this, "_output");
/**
* @type {WamNode}
*/
__publicField(this, "_wamNode");
}
get groupId() {
return this.module.groupId;
}
get moduleId() {
return this.module.moduleId;
}
get instanceId() {
return this.module.instanceId;
}
get module() {
return this._wamNode.module;
}
/**
* @param {Parameters<WamNode['addEventListener']>} args
*/
addEventListener(...args) {
return this._wamNode.addEventListener(...args);
}
/**
* @param {Parameters<WamNode['removeEventListener']>} args
*/
removeEventListener(...args) {
return this._wamNode.removeEventListener(...args);
}
/**
* @param {Parameters<WamNode['dispatchEvent']>} args
*/
dispatchEvent(...args) {
return this._wamNode.dispatchEvent(...args);
}
/**
* @param {Parameters<WamNode['getParameterInfo']>} args
*/
getParameterInfo(...args) {
return this._wamNode.getParameterInfo(...args);
}
/**
* @param {Parameters<WamNode['getParameterValues']>} args
*/
getParameterValues(...args) {
return this._wamNode.getParameterValues(...args);
}
/**
* @param {Parameters<WamNode['setParameterValues']>} args
*/
setParameterValues(...args) {
return this._wamNode.setParameterValues(...args);
}
getState() {
return this._wamNode.getState();
}
/**
* @param {Parameters<WamNode['setState']>} args
*/
setState(...args) {
return this._wamNode.setState(...args);
}
getCompensationDelay() {
return this._wamNode.getCompensationDelay();
}
/**
* @param {Parameters<WamNode['scheduleEvents']>} args
*/
scheduleEvents(...args) {
return this._wamNode.scheduleEvents(...args);
}
clearEvents() {
return this._wamNode.clearEvents();
}
/**
* @param {Parameters<WamNode['connectEvents']>} args
*/
connectEvents(...args) {
return this._wamNode.connectEvents(...args);
}
/**
* @param {Parameters<WamNode['disconnectEvents']>} args
*/
disconnectEvents(...args) {
return this._wamNode.disconnectEvents(...args);
}
destroy() {
return this._wamNode.destroy();
}
set channelCount(count) {
if (this._output)
this._output.channelCount = count;
else
super.channelCount = count;
}
get channelCount() {
if (this._output)
return this._output.channelCount;
return super.channelCount;
}
set channelCountMode(mode) {
if (this._output)
this._output.channelCountMode = mode;
else
super.channelCountMode = mode;
}
get channelCountMode() {
if (this._output)
return this._output.channelCountMode;
return super.channelCountMode;
}
set channelInterpretation(interpretation) {
if (this._output)
this._output.channelInterpretation = interpretation;
else
super.channelInterpretation = interpretation;
}
get channelInterpretation() {
if (this._output)
return this._output.channelInterpretation;
return super.channelInterpretation;
}
get numberOfInputs() {
return super.numberOfInputs;
}
get numberOfOutputs() {
if (this._output)
return this._output.numberOfOutputs;
return super.numberOfOutputs;
}
get gain() {
return void 0;
}
connect(...args) {
if (this._output && this._output !== this)
return this._output.connect(...args);
return super.connect(...args);
}
disconnect(...args) {
if (this._output && this._output !== this)
return this._output.disconnect(...args);
return super.disconnect(...args);
}
};
// src/sdk/src/addFunctionModule.js
var addFunctionModule = (audioWorklet, processorFunction, ...injection) => {
const text = `(${processorFunction.toString()})(${injection.map((s) => JSON.stringify(s)).join(", ")});`;
const url = URL.createObjectURL(new Blob([text], { type: "text/javascript" }));
return audioWorklet.addModule(url);
};
var addFunctionModule_default = addFunctionModule;
// src/ParamMgrProcessor.js
var processor = (moduleId, paramsConfig) => {
const audioWorkletGlobalScope = globalThis;
const {
AudioWorkletProcessor,
registerProcessor,
webAudioModules
} = audioWorkletGlobalScope;
const ModuleScope = audioWorkletGlobalScope.webAudioModules.getModuleScope(moduleId);
const supportSharedArrayBuffer = !!globalThis.SharedArrayBuffer;
const SharedArrayBuffer = globalThis.SharedArrayBuffer || globalThis.ArrayBuffer;
const normExp = (x, e) => e === 0 ? x : x ** 1.5 ** -e;
const normalizeE = (x, min, max, e = 0) => min === 0 && max === 1 ? normExp(x, e) : normExp((x - min) / (max - min) || 0, e);
const normalize = (x, min, max) => min === 0 && max === 1 ? x : (x - min) / (max - min) || 0;
const denormalize = (x, min, max) => min === 0 && max === 1 ? x : x * (max - min) + min;
const mapValue = (x, eMin, eMax, sMin, sMax, tMin, tMax) => denormalize(
normalize(
normalize(
Math.min(sMax, Math.max(sMin, x)),
eMin,
eMax
),
normalize(sMin, eMin, eMax),
normalize(sMax, eMin, eMax)
),
tMin,
tMax
);
class ParamMgrProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return Object.entries(paramsConfig).map(([name, { defaultValue, minValue, maxValue }]) => ({
name,
defaultValue,
minValue,
maxValue
}));
}
/**
* @param {ParamMgrProcessorOptions} options
*/
constructor(options) {
super();
this.destroyed = false;
this.supportSharedArrayBuffer = supportSharedArrayBuffer;
const {
paramsMapping,
internalParamsMinValues,
internalParams,
groupId,
instanceId
} = options.processorOptions;
this.groupId = groupId;
this.moduleId = moduleId;
this.instanceId = instanceId;
this.internalParamsMinValues = internalParamsMinValues;
this.paramsConfig = paramsConfig;
this.paramsMapping = paramsMapping;
this.paramsValues = {};
Object.entries(paramsConfig).forEach(([name, { defaultValue }]) => {
this.paramsValues[name] = defaultValue;
});
this.internalParams = internalParams;
this.internalParamsCount = this.internalParams.length;
this.buffer = new SharedArrayBuffer((this.internalParamsCount + 1) * Float32Array.BYTES_PER_ELEMENT);
this.$lock = new Int32Array(this.buffer, 0, 1);
this.$internalParamsBuffer = new Float32Array(this.buffer, 4, this.internalParamsCount);
this.eventQueue = [];
this.handleEvent = null;
audioWorkletGlobalScope.webAudioModules.addWam(this);
if (!ModuleScope.paramMgrProcessors)
ModuleScope.paramMgrProcessors = {};
ModuleScope.paramMgrProcessors[this.instanceId] = this;
this.messagePortRequestId = -1;
const resolves = {};
const rejects = {};
this.call = (call, ...args) => new Promise((resolve, reject) => {
const id = this.messagePortRequestId--;
resolves[id] = resolve;
rejects[id] = reject;
this.port.postMessage({ id, call, args });
});
this.handleMessage = ({ data }) => {
var _a, _b;
const { id, call, args, value, error } = data;
if (call) {
const r = { id };
try {
r.value = this[call](...args);
} catch (e) {
r.error = e;
}
this.port.postMessage(r);
} else {
if (error)
(_a = rejects[id]) == null ? void 0 : _a.call(rejects, error);
else
(_b = resolves[id]) == null ? void 0 : _b.call(resolves, value);
delete resolves[id];
delete rejects[id];
}
};
this.port.start();
this.port.addEventListener("message", this.handleMessage);
}
/**
* @param {ParametersMapping} mapping
*/
setParamsMapping(mapping) {
this.paramsMapping = mapping;
}
getBuffer() {
return { lock: this.$lock, paramsBuffer: this.$internalParamsBuffer };
}
getCompensationDelay() {
return 128;
}
/**
* @param {string[]} parameterIdQuery
*/
getParameterInfo(...parameterIdQuery) {
if (parameterIdQuery.length === 0)
parameterIdQuery = Object.keys(this.paramsConfig);
const parameterInfo = {};
parameterIdQuery.forEach((parameterId) => {
parameterInfo[parameterId] = this.paramsConfig[parameterId];
});
return parameterInfo;
}
/**
* @param {boolean} [normalized]
* @param {string[]} parameterIdQuery
*/
getParameterValues(normalized, ...parameterIdQuery) {
if (parameterIdQuery.length === 0)
parameterIdQuery = Object.keys(this.paramsConfig);
const parameterValues = {};
parameterIdQuery.forEach((parameterId) => {
if (!(parameterId in this.paramsValues))
return;
const { minValue, maxValue, exponent } = this.paramsConfig[parameterId];
const value = this.paramsValues[parameterId];
parameterValues[parameterId] = {
id: parameterId,
value: normalized ? normalizeE(value, minValue, maxValue, exponent) : value,
normalized
};
});
return parameterValues;
}
/**
* @param {WamEvent[]} events
*/
scheduleEvents(...events) {
this.eventQueue.push(...events);
const { currentTime } = audioWorkletGlobalScope;
this.eventQueue.sort((a, b) => (a.time || currentTime) - (b.time || currentTime));
}
/**
* @param {WamEvent[]} events
*/
emitEvents(...events) {
webAudioModules.emitEvents(this, ...events);
}
clearEvents() {
this.eventQueue = [];
}
lock() {
if (globalThis.Atomics)
Atomics.store(this.$lock, 0, 1);
}
unlock() {
if (globalThis.Atomics)
Atomics.store(this.$lock, 0, 0);
}
/**
* Main process
*
* @param {Float32Array[][]} inputs
* @param {Float32Array[][]} outputs
* @param {Record<string, Float32Array>} parameters
*/
process(inputs, outputs, parameters) {
if (this.destroyed)
return false;
const outputOffset = 1;
this.lock();
Object.entries(this.paramsConfig).forEach(([name, { minValue, maxValue }]) => {
const raw = parameters[name];
if (name in this.paramsValues)
this.paramsValues[name] = raw[raw.length - 1];
if (!this.paramsMapping[name])
return;
Object.entries(this.paramsMapping[name]).forEach(([targetName, targetMapping]) => {
var _a, _b;
const j = this.internalParams.indexOf(targetName);
if (j === -1)
return;
const intrinsicValue = this.internalParamsMinValues[j];
const { sourceRange, targetRange } = targetMapping;
const [sMin, sMax] = sourceRange;
const [tMin, tMax] = targetRange;
let out;
if (minValue !== tMin || maxValue !== tMax || minValue !== sMin || maxValue !== sMax) {
out = raw.map((v) => {
const mappedValue = mapValue(v, minValue, maxValue, sMin, sMax, tMin, tMax);
return mappedValue - intrinsicValue;
});
} else if (intrinsicValue) {
out = raw.map((v) => v - intrinsicValue);
} else {
out = raw;
}
if (out.length === 1)
(_a = outputs[j + outputOffset][0]) == null ? void 0 : _a.fill(out[0]);
else
(_b = outputs[j + outputOffset][0]) == null ? void 0 : _b.set(out);
this.$internalParamsBuffer[j] = out[0];
});
});
this.unlock();
if (!this.supportSharedArrayBuffer) {
this.call("setBuffer", { lock: this.$lock, paramsBuffer: this.$internalParamsBuffer });
}
const { currentTime } = audioWorkletGlobalScope;
let $event;
for ($event = 0; $event < this.eventQueue.length; $event++) {
const event = this.eventQueue[$event];
if (event.time && event.time > currentTime)
break;
if (typeof this.handleEvent === "function")
this.handleEvent(event);
this.call("dispatchWamEvent", event);
}
if ($event)
this.eventQueue.splice(0, $event);
return true;
}
/**
* @param {string} wamInstanceId
* @param {number} [output]
*/
connectEvents(wamInstanceId, output) {
webAudioModules.connectEvents(this.groupId, this.instanceId, wamInstanceId, output);
}
/**
* @param {string} [wamInstanceId]
* @param {number} [output]
*/
disconnectEvents(wamInstanceId, output) {
if (typeof wamInstanceId === "undefined") {
webAudioModules.disconnectEvents(this.groupId, this.instanceId);
return;
}
webAudioModules.disconnectEvents(this.groupId, this.instanceId, wamInstanceId, output);
}
destroy() {
audioWorkletGlobalScope.webAudioModules.removeWam(this);
if (ModuleScope.paramMgrProcessors)
delete ModuleScope.paramMgrProcessors[this.instanceId];
this.destroyed = true;
this.port.close();
}
}
try {
registerProcessor(moduleId, ParamMgrProcessor);
} catch (error) {
console.warn(error);
}
};
var ParamMgrProcessor_default = processor;
// src/sdk/src/WamParameterInfo.js
var getWamParameterInfo = (moduleId) => {
const audioWorkletGlobalScope = globalThis;
const normExp = (x, e) => e === 0 ? x : x ** 1.5 ** -e;
const denormExp = (x, e) => e === 0 ? x : x ** 1.5 ** e;
const normalize = (x, min, max, e = 0) => min === 0 && max === 1 ? normExp(x, e) : normExp((x - min) / (max - min) || 0, e);
const denormalize = (x, min, max, e = 0) => min === 0 && max === 1 ? denormExp(x, e) : denormExp(x, e) * (max - min) + min;
const inRange = (x, min, max) => x >= min && x <= max;
class WamParameterInfo2 {
/**
* @param {string} id
* @param {WamParameterConfiguration} [config]
*/
constructor(id, config = {}) {
let {
type,
label,
defaultValue,
minValue,
maxValue,
discreteStep,
exponent,
choices,
units
} = config;
if (type === void 0)
type = "float";
if (label === void 0)
label = "";
if (defaultValue === void 0)
defaultValue = 0;
if (choices === void 0)
choices = [];
if (type === "boolean" || type === "choice") {
discreteStep = 1;
minValue = 0;
if (choices.length)
maxValue = choices.length - 1;
else
maxValue = 1;
} else {
if (minValue === void 0)
minValue = 0;
if (maxValue === void 0)
maxValue = 1;
if (discreteStep === void 0)
discreteStep = 0;
if (exponent === void 0)
exponent = 0;
if (units === void 0)
units = "";
}
const errBase = `Param config error | ${id}: `;
if (minValue >= maxValue)
throw Error(errBase.concat("minValue must be less than maxValue"));
if (!inRange(defaultValue, minValue, maxValue))
throw Error(errBase.concat("defaultValue out of range"));
if (discreteStep % 1 || discreteStep < 0) {
throw Error(errBase.concat("discreteStep must be a non-negative integer"));
} else if (discreteStep > 0 && (minValue % 1 || maxValue % 1 || defaultValue % 1)) {
throw Error(errBase.concat("non-zero discreteStep requires integer minValue, maxValue, and defaultValue"));
}
if (type === "choice" && !choices.length) {
throw Error(errBase.concat("choice type parameter requires list of strings in choices"));
}
this.id = id;
this.label = label;
this.type = type;
this.defaultValue = defaultValue;
this.minValue = minValue;
this.maxValue = maxValue;
this.discreteStep = discreteStep;
this.exponent = exponent;
this.choices = choices;
this.units = units;
}
/**
* Convert a value from the parameter's denormalized range
* `[minValue, maxValue]` to normalized range `[0, 1]`.
* @param {number} value
*/
normalize(value) {
return normalize(value, this.minValue, this.maxValue, this.exponent);
}
/**
* Convert a value from normalized range `[0, 1]` to the
* parameter's denormalized range `[minValue, maxValue]`.
* @param {number} valueNorm
*/
denormalize(valueNorm) {
return denormalize(valueNorm, this.minValue, this.maxValue, this.exponent);
}
/**
* Get a human-readable string representing the given value,
* including units if applicable.
* @param {number} value
*/
valueString(value) {
if (this.choices)
return this.choices[value];
if (this.units !== "")
return `${value} ${this.units}`;
return `${value}`;
}
}
if (audioWorkletGlobalScope.AudioWorkletProcessor) {
const ModuleScope = audioWorkletGlobalScope.webAudioModules.getModuleScope(moduleId);
if (!ModuleScope.WamParameterInfo)
ModuleScope.WamParameterInfo = WamParameterInfo2;
}
return WamParameterInfo2;
};
var WamParameterInfo_default = getWamParameterInfo;
// src/ParamConfigurator.js
var WamParameterInfo = WamParameterInfo_default();
var ParamMappingConfigurator = class {
/**
* @param {ParametersMappingConfiguratorOptions} [options = {}]
*/
constructor(options = {}) {
/**
* @private
* @type {Record<string, WamParameterConfiguration>}
*/
__publicField(this, "_paramsConfig");
/**
* @private
* @type {InternalParametersDescriptor}
*/
__publicField(this, "_internalParamsConfig");
/**
* @private
* @type {ParametersMapping}
*/
__publicField(this, "_paramsMapping", {});
const { paramsConfig, paramsMapping, internalParamsConfig } = options;
this._paramsConfig = paramsConfig;
this._paramsMapping = paramsMapping;
this._internalParamsConfig = internalParamsConfig;
}
/**
* Auto-completed `paramsConfig`:
*
* if no `paramsConfig` is defined while initializing, this will be be filled from the internalParamsConfig;
*
* if a parameter is not fully configured, the incompleted properties will have the same value as in the internalParamsConfig.
*
* @type {WamParameterInfoMap}
*/
get paramsConfig() {
const { internalParamsConfig } = this;
return Object.entries(this._paramsConfig || internalParamsConfig).reduce((configs, [id, config]) => {
var _a, _b, _c, _d;
const internalParam = internalParamsConfig[id];
configs[id] = new WamParameterInfo(id, __spreadProps(__spreadValues({}, config), {
label: (_a = config.label) != null ? _a : id,
defaultValue: (_b = config.defaultValue) != null ? _b : internalParam == null ? void 0 : internalParam.defaultValue,
minValue: (_c = config.minValue) != null ? _c : internalParam == null ? void 0 : internalParam.minValue,
maxValue: (_d = config.maxValue) != null ? _d : internalParam == null ? void 0 : internalParam.maxValue
}));
return configs;
}, {});
}
/**
* Auto-completed configuration of the `internalParamsConfig`
*
* Internal Parameters Config contains all the automatable parameters' information.
*
* An automatable parameter could be a `WebAudio` `AudioParam`
* or a config with an `onChange` callback that will be called while the value has been changed.
*
* @type {InternalParametersDescriptor}
*/
get internalParamsConfig() {
return Object.entries(this._internalParamsConfig || {}).reduce((configs, [name, config]) => {
if (config instanceof AudioParam)
configs[name] = config;
else {
const defaultConfig = {
minValue: 0,
maxValue: 1,
defaultValue: 0,
automationRate: 30
};
configs[name] = __spreadValues(__spreadValues({}, defaultConfig), config);
}
return configs;
}, {});
}
/**
* Auto-completed `paramsMapping`,
* the mapping can be omitted while initialized,
* but is useful when an exposed param (in the `paramsConfig`) should automate
* several internal params (in the `internalParamsConfig`) or has a different range there.
*
* If a parameter is present in both `paramsConfig` and `internalParamsConfig` (or the `paramsConfig` is not configured),
* a map of this parameter will be there automatically, if not declared explicitly.
*
* @type {ParametersMapping}
*/
get paramsMapping() {
const declared = this._paramsMapping || {};
const externalParams = this.paramsConfig;
const internalParams = this.internalParamsConfig;
return Object.entries(externalParams).reduce((mapping, [name, { minValue, maxValue }]) => {
const sourceRange = [minValue, maxValue];
const defaultMapping = { sourceRange, targetRange: [...sourceRange] };
if (declared[name]) {
const declaredTargets = Object.entries(declared[name]).reduce((targets, [targetName, targetMapping]) => {
if (internalParams[targetName]) {
targets[targetName] = __spreadValues(__spreadValues({}, defaultMapping), targetMapping);
}
return targets;
}, {});
mapping[name] = declaredTargets;
} else if (internalParams[name]) {
mapping[name] = { [name]: __spreadValues({}, defaultMapping) };
}
return mapping;
}, {});
}
};
// src/MgrAudioParam.js
var MgrAudioParam = class extends AudioParam {
constructor() {
super(...arguments);
/**
* @type {WamParameterInfo}
*/
__publicField(this, "_info");
}
get exponent() {
return this.info.exponent;
}
get info() {
return this._info;
}
set info(info) {
this._info = info;
}
set normalizedValue(valueIn) {
this.value = this.info.denormalize(valueIn);
}
get normalizedValue() {
return this.info.normalize(this.value);
}
setValueAtTime(value, startTime) {
return super.setValueAtTime(value, startTime);
}
setNormalizedValueAtTime(valueIn, startTime) {
const value = this.info.denormalize(valueIn);
return this.setValueAtTime(value, startTime);
}
linearRampToValueAtTime(value, endTime) {
return super.linearRampToValueAtTime(value, endTime);
}
linearRampToNormalizedValueAtTime(valueIn, endTime) {
const value = this.info.denormalize(valueIn);
return this.linearRampToValueAtTime(value, endTime);
}
exponentialRampToValueAtTime(value, endTime) {
return super.exponentialRampToValueAtTime(value, endTime);
}
exponentialRampToNormalizedValueAtTime(valueIn, endTime) {
const value = this.info.denormalize(valueIn);
return this.exponentialRampToValueAtTime(value, endTime);
}
setTargetAtTime(target, startTime, timeConstant) {
return super.setTargetAtTime(target, startTime, timeConstant);
}
setNormalizedTargetAtTime(targetIn, startTime, timeConstant) {
const target = this.info.denormalize(targetIn);
return this.setTargetAtTime(target, startTime, timeConstant);
}
setValueCurveAtTime(values, startTime, duration) {
return super.setValueCurveAtTime(values, startTime, duration);
}
setNormalizedValueCurveAtTime(valuesIn, startTime, duration) {
const values = Array.from(valuesIn).map((v) => this.info.denormalize(v));
return this.setValueCurveAtTime(values, startTime, duration);
}
cancelScheduledParamValues(cancelTime) {
return super.cancelScheduledValues(cancelTime);
}
cancelAndHoldParamAtTime(cancelTime) {
return super.cancelAndHoldAtTime(cancelTime);
}
};
// src/ParamMgrNode.js
var AudioWorkletNode = globalThis.AudioWorkletNode;
var ParamMgrNode = class extends AudioWorkletNode {
/**
* @param {WebAudioModule} module
* @param {ParamMgrOptions} options
*/
constructor(module, options) {
super(module.audioContext, module.moduleId, {
numberOfInputs: 0,
numberOfOutputs: 1 + options.processorOptions.internalParams.length,
parameterData: options.parameterData,
processorOptions: options.processorOptions
});
/**
* @param {string} name
*/
__publicField(this, "requestDispatchIParamChange", (name) => {
const config = this.internalParamsConfig[name];
if (!("onChange" in config))
return;
const { automationRate, onChange } = config;
if (typeof automationRate !== "number" || !automationRate)
return;
const interval = 1e3 / automationRate;
const i = this.internalParams.indexOf(name);
if (i === -1)
return;
if (i >= this.internalParams.length)
return;
if (typeof this.paramsUpdateCheckFnRef[i] === "number") {
window.clearTimeout(this.paramsUpdateCheckFnRef[i]);
}
this.paramsUpdateCheckFn[i] = () => {
const prev = this.$prevParamsBuffer[i];
const cur = this.$paramsBuffer[i];
if (cur !== prev) {
onChange(cur, prev);
this.$prevParamsBuffer[i] = cur;
}
this.paramsUpdateCheckFnRef[i] = window.setTimeout(this.paramsUpdateCheckFn[i], interval);
};
this.paramsUpdateCheckFn[i]();
});
const { processorOptions, internalParamsConfig } = options;
this.initialized = false;
this.module = module;
this.instanceId = options.processorOptions.instanceId;
this.groupId = options.processorOptions.groupId;
this.paramsConfig = processorOptions.paramsConfig;
this.internalParams = processorOptions.internalParams;
this.internalParamsConfig = internalParamsConfig;
this.$prevParamsBuffer = new Float32Array(this.internalParams.length);
this.paramsUpdateCheckFn = [];
this.paramsUpdateCheckFnRef = [];
this.messageRequestId = 0;
this.dummyGainNode = module.audioContext.createGain();
Object.entries(this.getParams()).forEach(([name, param]) => {
Object.setPrototypeOf(param, MgrAudioParam.prototype);
param._info = this.paramsConfig[name];
});
const resolves = {};
const rejects = {};
this.call = (call, ...args) => {
const id = this.messageRequestId;
this.messageRequestId += 1;
return new Promise((resolve, reject) => {
resolves[id] = resolve;
rejects[id] = reject;
this.port.postMessage({ id, call, args });
});
};
this.handleMessage = ({ data }) => {
var _a, _b;
const { id, call, args, value, error } = data;
if (call) {
const r = { id };
try {
r.value = this[call](...args);
} catch (e) {
r.error = e;
}
this.port.postMessage(r);
} else {
if (error)
(_a = rejects[id]) == null ? void 0 : _a.call(rejects, error);
else
(_b = resolves[id]) == null ? void 0 : _b.call(resolves, value);
delete resolves[id];
delete rejects[id];
}
};
this.port.start();
this.port.addEventListener("message", this.handleMessage);
}
/**
* @returns {ReadonlyMap<string, MgrAudioParam>}
*/
get parameters() {
return super.parameters;
}
get moduleId() {
return this.module.moduleId;
}
async initialize() {
const response = await this.call("getBuffer");
const { lock, paramsBuffer } = response;
this.$lock = lock;
this.$paramsBuffer = paramsBuffer;
const offset = 1;
Object.entries(this.internalParamsConfig).forEach(([name, config], i) => {
if (this.context.state === "suspended")
this.$paramsBuffer[i] = config.defaultValue;
if (config instanceof AudioParam) {
try {
config.automationRate = "a-rate";
} catch (e) {
} finally {
config.value = Math.max(0, config.minValue);
this.connect(config, offset + i);
this.connect(this.dummyGainNode, offset + i, 0);
}
} else if (config instanceof AudioNode) {
this.connect(config, offset + i);
} else {
this.requestDispatchIParamChange(name);
}
});
this.connect(this.module.audioContext.destination, 0, 0);
this.initialized = true;
return this;
}
/**
* @param {ReturnType<ParamMgrCallToProcessor['getBuffer']>} buffer
*/
setBuffer({ lock, paramsBuffer }) {
this.$lock = lock;
this.$paramsBuffer = paramsBuffer;
}
setParamsMapping(paramsMapping) {
return this.call("setParamsMapping", paramsMapping);
}
getCompensationDelay() {
return this.call("getCompensationDelay");
}
getParameterInfo(...parameterIdQuery) {
return this.call("getParameterInfo", ...parameterIdQuery);
}
getParameterValues(normalized, ...parameterIdQuery) {
return this.call("getParameterValues", normalized, ...parameterIdQuery);
}
/**
* @param {WamAutomationEvent} event
*/
scheduleAutomation(event) {
const time = event.time || this.context.currentTime;
const { id, normalized, value } = event.data;
const audioParam = this.getParam(id);
if (!audioParam)
return;
if (audioParam.info.type === "float") {
if (normalized)
audioParam.linearRampToNormalizedValueAtTime(value, time);
else
audioParam.linearRampToValueAtTime(value, time);
} else {
if (normalized)
audioParam.setNormalizedValueAtTime(value, time);
else
audioParam.setValueAtTime(value, time);
}
}
/**
* @param {WamEvent[]} events
*/
scheduleEvents(...events) {
events.forEach((event) => {
if (event.type === "wam-automation") {
this.scheduleAutomation(event);
}
});
this.call("scheduleEvents", ...events);
}
/**
* @param {WamEvent[]} events
*/
emitEvents(...events) {
this.call("emitEvents", ...events);
}
clearEvents() {
this.call("clearEvents");
}
/**
* @param {WamEvent} event
*/
dispatchWamEvent(event) {
if (event.type === "wam-automation") {
this.scheduleAutomation(event);
} else {
this.dispatchEvent(new CustomEvent(event.type, { detail: event }));
}
}
/**
* @param {WamParameterValueMap} parameterValues
*/
async setParameterValues(parameterValues) {
Object.keys(parameterValues).forEach((parameterId) => {
const parameterUpdate = parameterValues[parameterId];
const parameter = this.parameters.get(parameterId);
if (!parameter)
return;
if (!parameterUpdate.normalized)
parameter.value = parameterUpdate.value;
else
parameter.normalizedValue = parameterUpdate.value;
});
}
async getState() {
return this.getParamsValues();
}
async setState(state) {
this.setParamsValues(state);
}
convertTimeToFrame(time) {
return Math.round(time * this.context.sampleRate);
}
convertFrameToTime(frame) {
return frame / this.context.sampleRate;
}
/**
* @param {string} name
*/
getIParamIndex(name) {
const i = this.internalParams.indexOf(name);
return i === -1 ? null : i;
}
/**
* @param {string} name
* @param {AudioParam | AudioNode} dest
* @param {number} index
*/
connectIParam(name, dest, index) {
const offset = 1;
const i = this.getIParamIndex(name);
if (i !== null) {
if (dest instanceof AudioNode) {
if (typeof index === "number")
this.connect(dest, offset + i, index);
else
this.connect(dest, offset + i);
} else {
this.connect(dest, offset + i);
}
}
}
/**
* @param {string} name
* @param {AudioParam | AudioNode} dest
* @param {number} index
*/
disconnectIParam(name, dest, index) {
const offset = 1;
const i = this.getIParamIndex(name);
if (i !== null) {
if (dest instanceof AudioNode) {
if (typeof index === "number")
this.disconnect(dest, offset + i, index);
else
this.disconnect(dest, offset + i);
} else {
this.disconnect(dest, offset + i);
}
}
}
getIParamValue(name) {
const i = this.getIParamIndex(name);
return i !== null ? this.$paramsBuffer[i] : null;
}
getIParamsValues() {
const values = {};
this.internalParams.forEach((name, i) => {
values[name] = this.$paramsBuffer[i];
});
return values;
}
getParam(name) {
return this.parameters.get(name) || null;
}
getParams() {
return Object.fromEntries(this.parameters);
}
getParamValue(name) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.value;
}
setParamValue(name, value) {
const param = this.parameters.get(name);
if (!param)
return;
param.value = value;
}
getParamsValues() {
const values = {};
this.parameters.forEach((v, k) => {
values[k] = v.value;
});
return values;
}
/**
* @param {Record<string, number>} values
*/
setParamsValues(values) {
if (!values)
return;
Object.entries(values).forEach(([k, v]) => {
this.setParamValue(k, v);
});
}
getNormalizedParamValue(name) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.normalizedValue;
}
setNormalizedParamValue(name, value) {
const param = this.parameters.get(name);
if (!param)
return;
param.normalizedValue = value;
}
getNormalizedParamsValues() {
const values = {};
this.parameters.forEach((v, k) => {
values[k] = this.getNormalizedParamValue(k);
});
return values;
}
setNormalizedParamsValues(values) {
if (!values)
return;
Object.entries(values).forEach(([k, v]) => {
this.setNormalizedParamValue(k, v);
});
}
setParamValueAtTime(name, value, startTime) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.setValueAtTime(value, startTime);
}
setNormalizedParamValueAtTime(name, value, startTime) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.setNormalizedValueAtTime(value, startTime);
}
linearRampToParamValueAtTime(name, value, endTime) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.linearRampToValueAtTime(value, endTime);
}
linearRampToNormalizedParamValueAtTime(name, value, endTime) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.linearRampToNormalizedValueAtTime(value, endTime);
}
exponentialRampToParamValueAtTime(name, value, endTime) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.exponentialRampToValueAtTime(value, endTime);
}
exponentialRampToNormalizedParamValueAtTime(name, value, endTime) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.exponentialRampToNormalizedValueAtTime(value, endTime);
}
setParamTargetAtTime(name, target, startTime, timeConstant) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.setTargetAtTime(target, startTime, timeConstant);
}
setNormalizedParamTargetAtTime(name, target, startTime, timeConstant) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.setNormalizedTargetAtTime(target, startTime, timeConstant);
}
setParamValueCurveAtTime(name, values, startTime, duration) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.setValueCurveAtTime(values, startTime, duration);
}
setNormalizedParamValueCurveAtTime(name, values, startTime, duration) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.setNormalizedValueCurveAtTime(values, startTime, duration);
}
cancelScheduledParamValues(name, cancelTime) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.cancelScheduledValues(cancelTime);
}
cancelAndHoldParamAtTime(name, cancelTime) {
const param = this.parameters.get(name);
if (!param)
return null;
return param.cancelAndHoldAtTime(cancelTime);
}
/**
* @param {string} toId
* @param {number} [output]
*/
connectEvents(toId, output) {
this.call("connectEvents", toId, output);
}
/**
* @param {string} [toId]
* @param {number} [output]
*/
disconnectEvents(toId, output) {
this.call("disconnectEvents", toId, output);
}
async destroy() {
this.disconnect();
this.paramsUpdateCheckFnRef.forEach((ref) => {
if (typeof ref === "number")
window.clearTimeout(ref);
});
await this.call("destroy");
this.port.close();
}
};
// src/ParamMgrFactory.js
var ParamMgrFactory = class {
/**
* @param {WebAudioModule} module
* @param {ParametersMappingConfiguratorOptions} [optionsIn = {}]
*/
static async create(module, optionsIn = {}) {
const { audioContext, moduleId } = module;
const instanceId = optionsIn.instanceId || module.instanceId;
const groupId = optionsIn.groupId || module.groupId;
const { paramsConfig, paramsMapping, internalParamsConfig } = new ParamMappingConfigurator(optionsIn);
const initialParamsValue = Object.entries(paramsConfig).reduce((currentParams, [name, { defaultValue }]) => {
currentParams[name] = defaultValue;
return currentParams;
}, {});
const serializableParamsConfig = Object.entries(paramsConfig).reduce((currentParams, [name, { id, label, type, defaultValue, minValue, maxValue, discreteStep, exponent, choices, units }]) => {
currentParams[name] = { id, label, type, defaultValue, minValue, maxValue, discreteStep, exponent, choices, units };
return currentParams;
}, {});
await addFunctionModule_default(audioContext.audioWorklet, ParamMgrProcessor_default, moduleId, serializableParamsConfig);
const options = {
internalParamsConfig,
parameterData: initialParamsValue,
processorOptions: {
paramsConfig,
paramsMapping,
internalParamsMinValues: Object.values(internalParamsConfig).map((config) => Math.max(0, (config == null ? void 0 : config.minValue) || 0)),
internalParams: Object.keys(internalParamsConfig),
groupId,
instanceId,
moduleId
}
};
const node = new ParamMgrNode(module, options);
await node.initialize();
return node;
}
};
export {
CompositeAudioNode,
ParamMgrFactory
};
//# sourceMappingURL=index.js.map