-
Notifications
You must be signed in to change notification settings - Fork 1
/
audio-player.js
53 lines (42 loc) · 1.38 KB
/
audio-player.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import Speaker from "speaker";
export default class AudioPlayer {
constructor(sampleRate) {
this.speaker = new SpeakerWrapper(sampleRate);
this.bufferedAudio = [];
this.targetSpeakerAudioMs = 400;
setInterval(() => this.#refillSpeaker(), 200);
}
play(audio) {
this.bufferedAudio.push(audio);
this.#refillSpeaker();
}
stop() {
this.bufferedAudio = [];
}
#refillSpeaker() {
while (this.bufferedAudio.length && this.speaker.getBufferedMs() < this.targetSpeakerAudioMs) {
this.speaker.write(this.bufferedAudio.shift());
}
}
}
class SpeakerWrapper {
constructor(sampleRate) {
this.speaker = new Speaker({ channels: 1, bitDepth: 16, sampleRate });
this.msPerSample = 1000 / sampleRate;
this.lastWriteTime = Date.now();
this.bufferedMsAtLastWrite = 0;
}
write(audio) {
this.bufferedMsAtLastWrite = this.getBufferedMs() + this.#getAudioDurationMs(audio);
this.lastWriteTime = Date.now();
this.speaker.write(audio);
}
getBufferedMs() {
const msSinceLastWrite = Date.now() - this.lastWriteTime;
return Math.max(this.bufferedMsAtLastWrite - msSinceLastWrite, 0);
}
#getAudioDurationMs(audio) {
const num_samples = audio.length / 2;
return this.msPerSample * num_samples;
}
}