60 lines
2.1 KiB
JavaScript
60 lines
2.1 KiB
JavaScript
import SharedAudioContext from './shared-audio-context.js';
|
|
|
|
class AudioBufferPlayer {
|
|
constructor (samples, sampleRate) {
|
|
this.audioContext = new SharedAudioContext();
|
|
this.buffer = this.audioContext.createBuffer(1, samples.length, sampleRate);
|
|
this.buffer.getChannelData(0).set(samples);
|
|
this.source = null;
|
|
|
|
this.startTime = null;
|
|
this.updateCallback = null;
|
|
this.trimStart = null;
|
|
this.trimEnd = null;
|
|
}
|
|
|
|
play (trimStart, trimEnd, onUpdate, onEnded) {
|
|
this.updateCallback = onUpdate;
|
|
this.trimStart = trimStart;
|
|
this.trimEnd = trimEnd;
|
|
this.startTime = Date.now();
|
|
|
|
const trimStartTime = this.buffer.duration * trimStart;
|
|
const trimmedDuration = (this.buffer.duration * trimEnd) - trimStartTime;
|
|
|
|
this.source = this.audioContext.createBufferSource();
|
|
this.source.onended = onEnded;
|
|
this.source.buffer = this.buffer;
|
|
this.source.connect(this.audioContext.destination);
|
|
this.source.start(0, trimStartTime, trimmedDuration);
|
|
|
|
this.update();
|
|
}
|
|
|
|
update () {
|
|
const timeSinceStart = (Date.now() - this.startTime) / 1000;
|
|
const percentage = timeSinceStart / this.buffer.duration;
|
|
if (percentage + this.trimStart < this.trimEnd && this.source.onended) {
|
|
requestAnimationFrame(this.update.bind(this));
|
|
this.updateCallback(percentage + this.trimStart);
|
|
} else {
|
|
this.updateCallback = null;
|
|
}
|
|
}
|
|
|
|
stop () {
|
|
if (this.source) {
|
|
this.source.onended = null; // Do not call onEnded callback if manually stopped
|
|
try {
|
|
this.source.stop();
|
|
} catch (e) {
|
|
// This is probably Safari, which dies when you call stop more than once
|
|
// which the spec says is allowed: https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode
|
|
console.log('Caught error while stopping buffer source node.'); // eslint-disable-line no-console
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export default AudioBufferPlayer;
|