react-native-audio-api
Version:
react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification
262 lines (261 loc) • 7.66 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _AudioParam = _interopRequireDefault(require("../../AudioParam.web"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const RAMP_SAMPLE_INTERVAL = 0.05;
class AudioStretcherParam extends _AudioParam.default {
_events = [];
constructor(context, initialValue, defaultValue, minValue, maxValue, onChange, onCancel) {
const source = new globalThis.ConstantSourceNode(context.context, {
offset: initialValue
});
source.start(0);
super(source.offset, context);
this.defaultValue = defaultValue;
this.minValue = minValue;
this.maxValue = maxValue;
this._initialValue = initialValue;
this._onChange = onChange ?? null;
this._onCancel = onCancel ?? null;
}
get value() {
return super.value;
}
set value(v) {
super.value = v;
this._events = [{
type: 'setValue',
value: v,
time: this.context.currentTime
}];
this._onChange?.({
type: 'setValue',
value: v,
time: this.context.currentTime
});
}
getValueAtTime(time) {
if (this._events.length === 0) {
return this._initialValue;
}
let value = this._initialValue;
let tPrev = -Infinity;
const sorted = [...this._events].sort((a, b) => a.time - b.time);
for (const event of sorted) {
if (event.type === 'setValue') {
if (time < event.time) {
return value;
}
value = event.value;
tPrev = event.time;
continue;
}
if (event.type === 'linearRamp') {
const rampStart = event.rampStartTime ?? tPrev;
if (time < rampStart) {
continue;
}
if (time >= event.time) {
value = event.value;
tPrev = event.time;
continue;
}
const v0 = event.startValue ?? value;
const fraction = (time - rampStart) / (event.time - rampStart);
return v0 + fraction * (event.value - v0);
}
if (event.type === 'exponentialRamp') {
const rampStart = event.rampStartTime ?? tPrev;
if (time < rampStart) {
continue;
}
if (time >= event.time) {
value = event.value;
tPrev = event.time;
continue;
}
const v0 = event.startValue ?? value;
if (v0 === 0 || event.value === 0) {
return v0;
}
const fraction = (time - rampStart) / (event.time - rampStart);
return v0 * Math.pow(event.value / v0, fraction);
}
if (event.type === 'setTarget') {
if (time < event.time) {
return value;
}
const dt = time - event.time;
const v0 = event.startValue;
return event.target + (v0 - event.target) * Math.exp(-dt / event.timeConstant);
}
if (event.type === 'valueCurve') {
if (time < event.time) {
return value;
}
if (time >= event.time + event.duration) {
value = event.values[event.values.length - 1];
tPrev = event.time + event.duration;
continue;
}
const progress = (time - event.time) / event.duration;
const idx = progress * (event.values.length - 1);
const i0 = Math.floor(idx);
const i1 = Math.min(i0 + 1, event.values.length - 1);
const frac = idx - i0;
return event.values[i0] + frac * (event.values[i1] - event.values[i0]);
}
}
return value;
}
getRampStartTime(endTime) {
let tPrev = -Infinity;
const sorted = [...this._events].sort((a, b) => a.time - b.time);
for (const event of sorted) {
if (event.time >= endTime) {
break;
}
tPrev = event.time;
}
if (tPrev === -Infinity) {
return this.context.currentTime;
}
return tPrev;
}
sampleAutomation(event, mapValue) {
const samples = [];
if (event.type === 'setValue') {
samples.push({
value: mapValue(event.value),
time: event.time
});
return samples;
}
if (event.type === 'linearRamp' || event.type === 'exponentialRamp') {
const startTime = event.rampStartTime ?? this.getRampStartTime(event.time);
const endTime = event.time;
const duration = endTime - startTime;
if (duration <= 0) {
samples.push({
value: mapValue(event.value),
time: endTime
});
return samples;
}
const stepCount = Math.max(2, Math.ceil(duration / RAMP_SAMPLE_INTERVAL));
for (let i = 0; i <= stepCount; i++) {
const t = startTime + duration * i / stepCount;
samples.push({
value: mapValue(this.getValueAtTime(t)),
time: t
});
}
return samples;
}
if (event.type === 'setTarget') {
const startTime = event.time;
const duration = Math.max(event.timeConstant * 5, RAMP_SAMPLE_INTERVAL);
const stepCount = Math.max(2, Math.ceil(duration / RAMP_SAMPLE_INTERVAL));
for (let i = 0; i <= stepCount; i++) {
const t = startTime + duration * i / stepCount;
samples.push({
value: mapValue(this.getValueAtTime(t)),
time: t
});
}
return samples;
}
if (event.type === 'valueCurve') {
const stepCount = Math.max(2, Math.ceil(event.duration / RAMP_SAMPLE_INTERVAL));
for (let i = 0; i <= stepCount; i++) {
const t = event.time + event.duration * i / stepCount;
samples.push({
value: mapValue(this.getValueAtTime(t)),
time: t
});
}
return samples;
}
return samples;
}
_recordEvent(event) {
this._events.push(event);
}
_removeEventsFrom(cancelTime) {
this._events = this._events.filter(event => event.time < cancelTime);
}
setValueAtTime(value, startTime) {
const event = {
type: 'setValue',
value,
time: startTime
};
this._recordEvent(event);
this._onChange?.(event);
return this;
}
linearRampToValueAtTime(value, endTime) {
const rampStartTime = this.getRampStartTime(endTime);
const event = {
type: 'linearRamp',
value,
time: endTime,
rampStartTime,
startValue: this.getValueAtTime(rampStartTime)
};
this._recordEvent(event);
this._onChange?.(event);
return this;
}
exponentialRampToValueAtTime(value, endTime) {
const rampStartTime = this.getRampStartTime(endTime);
const event = {
type: 'exponentialRamp',
value,
time: endTime,
rampStartTime,
startValue: this.getValueAtTime(rampStartTime)
};
this._recordEvent(event);
this._onChange?.(event);
return this;
}
setTargetAtTime(target, startTime, timeConstant) {
const event = {
type: 'setTarget',
target,
startValue: this.getValueAtTime(startTime),
time: startTime,
timeConstant
};
this._recordEvent(event);
this._onChange?.(event);
return this;
}
setValueCurveAtTime(values, startTime, duration) {
const event = {
type: 'valueCurve',
values,
time: startTime,
duration
};
this._recordEvent(event);
this._onChange?.(event);
return this;
}
cancelScheduledValues(cancelTime) {
this._removeEventsFrom(cancelTime);
this._onCancel?.(cancelTime);
return this;
}
cancelAndHoldAtTime(cancelTime) {
this._removeEventsFrom(cancelTime);
this._onCancel?.(cancelTime);
return this;
}
}
exports.default = AudioStretcherParam;
//# sourceMappingURL=AudioStretcherParam.js.map