import { IAudioBufferSourceNode } from '../interfaces'; import AudioBufferBaseSourceNode from './AudioBufferBaseSourceNode'; import AudioBuffer from './AudioBuffer'; import { InvalidStateError, RangeError } from '../errors'; import { EventEmptyType } from '../events/types'; import { AudioEventSubscription } from '../events'; import { AudioBufferSourceOptions } from '../types'; import type BaseAudioContext from './BaseAudioContext'; export default class AudioBufferSourceNode extends AudioBufferBaseSourceNode { private onLoopEndedSubscription?: AudioEventSubscription; private onLoopEndedCallback?: (event: EventEmptyType) => void; private _buffer: AudioBuffer | null = null; private bufferHasBeenSet: boolean = false; constructor(context: BaseAudioContext, options?: AudioBufferSourceOptions) { const node = context.context.createBufferSource(options || {}); super(context, node); if (options?.buffer) { this._buffer = options.buffer; this.bufferHasBeenSet = true; } } public get buffer(): AudioBuffer | null { return this._buffer; } public set buffer(buffer: AudioBuffer | null) { if (buffer === null) { if (this.buffer !== null) { (this.node as IAudioBufferSourceNode).setBuffer(null); this._buffer = null; } return; } if (this.bufferHasBeenSet) { throw new InvalidStateError( 'The buffer can only be set once and cannot be changed afterwards.' ); } (this.node as IAudioBufferSourceNode).setBuffer(buffer.buffer); this._buffer = buffer; this.bufferHasBeenSet = true; } public get loopSkip(): boolean { return (this.node as IAudioBufferSourceNode).loopSkip; } public set loopSkip(value: boolean) { (this.node as IAudioBufferSourceNode).loopSkip = value; } public get loop(): boolean { return (this.node as IAudioBufferSourceNode).loop; } public set loop(value: boolean) { (this.node as IAudioBufferSourceNode).loop = value; } public get loopStart(): number { return (this.node as IAudioBufferSourceNode).loopStart; } public set loopStart(value: number) { (this.node as IAudioBufferSourceNode).loopStart = value; } public get loopEnd(): number { return (this.node as IAudioBufferSourceNode).loopEnd; } public set loopEnd(value: number) { (this.node as IAudioBufferSourceNode).loopEnd = value; } public start(when: number = 0, offset: number = 0, duration?: number): void { if (when < 0) { throw new RangeError( `when must be a finite non-negative number: ${when}` ); } if (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 as IAudioBufferSourceNode).start(when, offset, duration); } public get onLoopEnded(): ((event: EventEmptyType) => void) | undefined { return this.onLoopEndedCallback; } public set onLoopEnded(callback: ((event: EventEmptyType) => void) | null) { if (!callback) { (this.node as IAudioBufferSourceNode).onLoopEnded = '0'; this.onLoopEndedSubscription?.remove(); this.onLoopEndedSubscription = undefined; this.onLoopEndedCallback = undefined; return; } this.onLoopEndedCallback = callback; this.onLoopEndedSubscription = this.audioEventEmitter.addAudioEventListener( 'loopEnded', callback ); (this.node as IAudioBufferSourceNode).onLoopEnded = this.onLoopEndedSubscription.subscriptionId; } }