|
|
|
|
|
|
|
|
|
export class AudioPipe { |
|
constructor() { |
|
this.initialized = false; |
|
this.nextAvailableTime = 0; |
|
this.gain = 1; |
|
} |
|
|
|
|
|
|
|
|
|
initialize() { |
|
this.audioContext = new AudioContext(); |
|
this.gainNode = this.audioContext.createGain(); |
|
this.gainNode.connect(this.audioContext.destination); |
|
this.gainNode.gain.value = this.gain; |
|
this.initialized = true; |
|
} |
|
|
|
|
|
|
|
|
|
get volume() { |
|
return this.gain; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
set volume(value) { |
|
if (this.initialized) { |
|
|
|
this.gainNode.gain.value = 2 * value - 1; |
|
} |
|
this.gain = value; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
push(data, sampleRate = 48000) { |
|
if (!this.initialized) { |
|
this.initialize(); |
|
} |
|
const audioBuffer = new AudioBuffer({ |
|
length: data.length, |
|
numberOfChannels: 1, |
|
sampleRate: sampleRate |
|
}); |
|
audioBuffer.copyToChannel(data, 0); |
|
const audioBufferNode = new AudioBufferSourceNode( |
|
this.audioContext, |
|
{ buffer: audioBuffer } |
|
); |
|
audioBufferNode.connect(this.gainNode); |
|
audioBufferNode.start(this.nextAvailableTime); |
|
if (this.nextAvailableTime > this.audioContext.currentTime) { |
|
|
|
this.nextAvailableTime += audioBuffer.duration; |
|
} else { |
|
this.nextAvailableTime = this.audioContext.currentTime + audioBuffer.duration; |
|
} |
|
return audioBufferNode; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pushSilence(duration, sampleRate = 48000) { |
|
if (!this.initialized) { |
|
return; |
|
} |
|
const data = new Float32Array(Math.floor(duration * sampleRate)); |
|
this.push(data, sampleRate); |
|
} |
|
|
|
|
|
|
|
|
|
get playing() { |
|
if (!this.initialized) { |
|
return false; |
|
} |
|
return this.audioContext.currentTime < this.nextAvailableTime; |
|
} |
|
} |
|
|