UNPKG

react-native-audio-api

Version:

react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification

162 lines (133 loc) • 4.26 kB
import { InvalidStateError } from '../../errors'; import AudioBuffer from '../AudioBuffer.web'; import AudioNode from '../AudioNode.web'; import AudioParam from '../AudioParam.web'; import BaseAudioContext from '../BaseAudioContext.web'; import { AudioBufferSourceNodeBackend } from '../types.web'; export default class AudioBufferSourceNodeStandard implements AudioBufferSourceNodeBackend { private node: globalThis.AudioBufferSourceNode; private hasBeenStarted: boolean = false; private _loopSkip: boolean = false; private _onLoopEnded: ((event: object) => void) | undefined = undefined; readonly playbackRate: AudioParam; readonly detune: AudioParam; public constructor( context: BaseAudioContext, options?: AudioBufferSourceOptions ) { const { buffer, ...rest } = options ?? {}; this.node = new globalThis.AudioBufferSourceNode(context.context, { ...rest, ...(buffer ? { buffer: (buffer as AudioBuffer).buffer } : {}), }); this.detune = new AudioParam(this.node.detune, context); this.playbackRate = new AudioParam(this.node.playbackRate, context); } public start(when: number = 0, offset?: number, duration?: number): void { 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; this.node.start(when, offset, duration); } public stop(when?: number): void { if (when !== undefined && 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' ); } this.node.stop(when); } public connect(destination: AudioNode | AudioParam): AudioNode | AudioParam { if (destination instanceof AudioParam) { this.node.connect(destination.param); } else { this.node.connect(destination.node); } return destination; } public disconnect(destination?: AudioNode | AudioParam): void { if (destination === undefined) { this.node.disconnect(); return; } if (destination instanceof AudioParam) { this.node.disconnect(destination.param); return; } this.node.disconnect(destination.node); } public get buffer(): AudioBuffer | null { const buffer = this.node.buffer; if (!buffer) { return null; } return new AudioBuffer(buffer); } public set buffer(buffer: AudioBuffer | null) { if (!buffer) { this.node.buffer = null; return; } this.node.buffer = buffer.buffer; } public get loop(): boolean { return this.node.loop; } public set loop(value: boolean) { this.node.loop = value; } public get loopStart(): number { return this.node.loopStart; } public set loopStart(value: number) { this.node.loopStart = value; } public get loopEnd(): number { return this.node.loopEnd; } public set loopEnd(value: number) { this.node.loopEnd = value; } public get loopSkip(): boolean { return this._loopSkip; } public set loopSkip(value: boolean) { this._loopSkip = value; } public get onEnded(): ((event: Event) => void) | null { return this.node.onended as ((event: Event) => void) | null; } public set onEnded(callback: ((event: Event) => void) | null) { this.node.onended = callback as | ((this: AudioScheduledSourceNode, ev: Event) => unknown) | null; } public get onLoopEnded(): ((event: object) => void) | undefined { return this._onLoopEnded; } // The browser Web Audio API has no per-loop event; callback is stored but never fired. public set onLoopEnded(callback: ((event: object) => void) | null) { this._onLoopEnded = callback ?? undefined; } }