react-native-audio-api
Version:
react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification
208 lines (207 loc) • 6.17 kB
JavaScript
"use strict";
import { InvalidStateError, RangeError } from "../errors/index.js";
import AudioParam from "./AudioParam.js";
import AudioBuffer from "./AudioBuffer.js";
import AudioScheduledSourceNode from "./AudioScheduledSourceNode.js";
import { clamp } from "../utils/index.js";
import { globalTag } from "./custom/LoadCustomWasm.js";
class IStretcherNodeAudioParam {
constructor(value, setter, automationRate, minValue, maxValue, defaultValue) {
this._value = value;
this.automationRate = automationRate;
this.minValue = minValue;
this.maxValue = maxValue;
this.defaultValue = defaultValue;
this._setter = setter;
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
this._setter(value);
}
cancelAndHoldAtTime(cancelTime) {
this._setter(this.defaultValue, cancelTime);
return this;
}
cancelScheduledValues(cancelTime) {
this._setter(this.defaultValue, cancelTime);
return this;
}
exponentialRampToValueAtTime(_value, _endTime) {
console.warn('exponentialRampToValueAtTime is not implemented for pitch correction mode');
return this;
}
linearRampToValueAtTime(_value, _endTime) {
console.warn('linearRampToValueAtTime is not implemented for pitch correction mode');
return this;
}
setTargetAtTime(_target, _startTime, _timeConstant) {
console.warn('setTargetAtTime is not implemented for pitch correction mode');
return this;
}
setValueAtTime(value, startTime) {
this._setter(value, startTime);
return this;
}
setValueCurveAtTime(_values, _startTime, _duration) {
console.warn('setValueCurveAtTime is not implemented for pitch correction mode');
return this;
}
}
export default class AudioBufferSourceNode extends AudioScheduledSourceNode {
_loop = false;
_loopStart = -1;
_loopEnd = -1;
_buffer = null;
constructor(context, node, pitchCorrection) {
super(context, node);
this._pitchCorrection = pitchCorrection;
if (pitchCorrection) {
this.detune = new AudioParam(new IStretcherNodeAudioParam(0, this.setDetune.bind(this), 'a-rate', -1200, 1200, 0), context);
this.playbackRate = new AudioParam(new IStretcherNodeAudioParam(1, this.setPlaybackRate.bind(this), 'a-rate', 0, Infinity, 1), context);
} else {
this.detune = new AudioParam(node.detune, context);
this.playbackRate = new AudioParam(node.playbackRate, context);
}
}
isStretcherNode() {
return this._pitchCorrection;
}
asStretcher() {
return this.node;
}
asBufferSource() {
return this.node;
}
setDetune(value, when = 0) {
if (!this.isStretcherNode() || !this.hasBeenStarted) {
return;
}
this.asStretcher().schedule({
semitones: Math.floor(clamp(value / 100, -12, 12)),
output: when
});
}
setPlaybackRate(value, when = 0) {
if (!this.isStretcherNode() || !this.hasBeenStarted) {
return;
}
this.asStretcher().schedule({
rate: value,
output: when
});
}
get buffer() {
if (this.isStretcherNode()) {
return this._buffer;
}
const buffer = this.asBufferSource().buffer;
if (!buffer) {
return null;
}
return new AudioBuffer(buffer);
}
set buffer(buffer) {
if (this.isStretcherNode()) {
this._buffer = buffer;
const stretcher = this.asStretcher();
stretcher.dropBuffers();
if (!buffer) {
return;
}
const channelArrays = [];
for (let i = 0; i < buffer.numberOfChannels; i++) {
channelArrays.push(buffer.getChannelData(i));
}
stretcher.addBuffers(channelArrays);
return;
}
if (!buffer) {
this.asBufferSource().buffer = null;
return;
}
this.asBufferSource().buffer = buffer.buffer;
}
get loop() {
if (this.isStretcherNode()) {
return this._loop;
}
return this.asBufferSource().loop;
}
set loop(value) {
if (this.isStretcherNode()) {
this._loop = value;
return;
}
this.asBufferSource().loop = value;
}
get loopStart() {
if (this.isStretcherNode()) {
return this._loopStart;
}
return this.asBufferSource().loopStart;
}
set loopStart(value) {
if (this.isStretcherNode()) {
this._loopStart = value;
return;
}
this.asBufferSource().loopStart = value;
}
get loopEnd() {
if (this.isStretcherNode()) {
return this._loopEnd;
}
return this.asBufferSource().loopEnd;
}
set loopEnd(value) {
if (this.isStretcherNode()) {
this._loopEnd = value;
return;
}
this.asBufferSource().loopEnd = value;
}
start(when, offset, duration) {
if (when && when < 0) {
throw new RangeError(`when must be a finite non-negative number: ${when}`);
}
if (offset && offset < 0) {
throw new RangeError(`offset must be a finite non-negative number: ${offset}`);
}
if (duration && duration < 0) {
throw new RangeError(`duration must be a finite non-negative number: ${duration}`);
}
if (this.hasBeenStarted) {
throw new InvalidStateError('Cannot call start more than once');
}
this.hasBeenStarted = true;
if (!this.isStretcherNode()) {
this.asBufferSource().start(when, offset, duration);
return;
}
const startAt = !when || when < this.context.currentTime ? this.context.currentTime : when;
if (this.loop && this._loopStart !== -1 && this._loopEnd !== -1) {
this.asStretcher().schedule({
loopStart: this._loopStart,
loopEnd: this._loopEnd
});
}
this.asStretcher().start(startAt, offset, duration, this.playbackRate.value, Math.floor(clamp(this.detune.value / 100, -12, 12)));
}
stop(when = 0) {
if (when < 0) {
throw new RangeError(`when must be a finite non-negative number: ${when}`);
}
if (!this.hasBeenStarted) {
throw new InvalidStateError('Cannot call stop without calling start first');
}
if (!this.isStretcherNode()) {
this.asBufferSource().stop(when);
return;
}
this.asStretcher().stop(when);
}
}
//# sourceMappingURL=AudioBufferSourceNode.js.map