118 lines
No EOL
3.6 KiB
TypeScript
118 lines
No EOL
3.6 KiB
TypeScript
enum PlayerState {
|
|
PREBUFFERING,
|
|
PLAYING,
|
|
BUFFERING,
|
|
STOPPED
|
|
}
|
|
|
|
class AudioController {
|
|
private static _globalContext;
|
|
static get globalContext() : AudioContext {
|
|
if(this._globalContext) return this._globalContext;
|
|
this._globalContext = new AudioContext();
|
|
return this._globalContext;
|
|
}
|
|
|
|
speakerContext: AudioContext;
|
|
private timeIndex: number = 0;
|
|
private playerState: PlayerState = PlayerState.STOPPED;
|
|
private audioCache: AudioBuffer[];
|
|
private _playingSources: AudioBufferSourceNode[] = [];
|
|
allowBuffering: boolean = false;
|
|
|
|
//Events
|
|
onSpeaking: () => void;
|
|
onSilence: () => void;
|
|
|
|
constructor() {
|
|
this.speakerContext = AudioController.globalContext;
|
|
this.audioCache = [];
|
|
|
|
this.onSpeaking = function () { };
|
|
this.onSilence = function () { };
|
|
}
|
|
|
|
playBuffer(buffer: AudioBuffer) {
|
|
if (buffer.sampleRate != this.speakerContext.sampleRate)
|
|
console.warn("[AudioController] Source sample rate isn't equal to playback sample rate!");
|
|
this.audioCache.push(buffer);
|
|
|
|
if(this.playerState == PlayerState.STOPPED) {
|
|
console.log("[Audio] Starting new playback");
|
|
this.playerState = PlayerState.PREBUFFERING;
|
|
//New audio
|
|
}
|
|
|
|
|
|
switch (this.playerState) {
|
|
case PlayerState.PREBUFFERING:
|
|
case PlayerState.BUFFERING:
|
|
if(this.audioCache.length < 5) {
|
|
if(this.playerState == PlayerState.BUFFERING) {
|
|
if(this.allowBuffering) break;
|
|
} else break;
|
|
}
|
|
if(this.playerState == PlayerState.PREBUFFERING) {
|
|
console.log("[Audio] Prebuffering succeeded (Replaying now)");
|
|
this.onSpeaking();
|
|
} else {
|
|
if(this.allowBuffering)
|
|
console.log("[Audio] Buffering succeeded (Replaying now)");
|
|
}
|
|
this.timeIndex = 0; //Instant replay
|
|
this.playerState = PlayerState.PLAYING;
|
|
case PlayerState.PLAYING:
|
|
this.playCache(this.audioCache);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
private playCache(cache) {
|
|
while (cache.length) {
|
|
let buffer = cache.shift();
|
|
let source = this.speakerContext.createBufferSource();
|
|
|
|
source.buffer = buffer;
|
|
source.connect(this.speakerContext.destination);
|
|
source.onended = () => {
|
|
this._playingSources.remove(source);
|
|
this.testBufferQueue();
|
|
};
|
|
if (this.timeIndex == 0) {
|
|
this.timeIndex = this.speakerContext.currentTime;
|
|
console.log("New next time!");
|
|
}
|
|
source.start(this.timeIndex);
|
|
this.timeIndex += source.buffer.duration;
|
|
this._playingSources.push(source);
|
|
}
|
|
};
|
|
|
|
stopAudio(now: boolean = false) {
|
|
this.playerState = PlayerState.STOPPED;
|
|
if(now) {
|
|
for(let e of this._playingSources)
|
|
e.stop();
|
|
this._playingSources = [];
|
|
}
|
|
}
|
|
|
|
private testBufferQueue() {
|
|
if(this._playingSources.length == 0) {
|
|
this.onSilence();
|
|
|
|
|
|
if(this.playerState != PlayerState.STOPPED) {
|
|
this.playerState = PlayerState.BUFFERING;
|
|
if(!this.allowBuffering)
|
|
console.warn("[Audi] Detected a buffer underflow!");
|
|
}
|
|
}
|
|
}
|
|
|
|
close(){
|
|
|
|
}
|
|
} |