The web audio player now only gets constructed on a users gesture

This commit is contained in:
WolverinDEV 2020-08-05 19:10:24 +02:00
parent 226f3d50d0
commit 83cfe0bfd5

View file

@ -21,15 +21,16 @@ export function initializeFromGesture();
*/ */
import {Device} from "tc-shared/audio/player"; import {Device} from "tc-shared/audio/player";
import {LogCategory} from "tc-shared/log";
import * as log from "tc-shared/log"; import * as log from "tc-shared/log";
import * as loader from "tc-loader"; import {LogCategory} from "tc-shared/log";
let _globalContext: AudioContext; const kAvoidAudioContextWarning = true;
let _global_destination: GainNode;
let _globalContextPromise: Promise<void>; let audioContextRequiredGesture = false;
let _initialized_listener: (() => any)[] = []; let audioContextInstance: AudioContext;
let globalAudioGainInstance: GainNode;
let audioContextInitializeCallbacks: (() => any)[] = [];
let _master_volume: number = 1; let _master_volume: number = 1;
let _no_device = false; let _no_device = false;
@ -39,44 +40,44 @@ export function initialize() : boolean {
} }
export function initialized() : boolean { export function initialized() : boolean {
return !!_globalContext && _globalContext.state === 'running'; return !!audioContextInstance && audioContextInstance.state === 'running';
} }
function fire_initialized() { function fire_initialized() {
log.info(LogCategory.AUDIO, tr("Fire audio player initialized for %d listeners"), _initialized_listener.length); log.info(LogCategory.AUDIO, tr("Fire audio player initialized for %d listeners"), audioContextInitializeCallbacks.length);
while(_initialized_listener.length > 0) while(audioContextInitializeCallbacks.length > 0)
_initialized_listener.pop_front()(); audioContextInitializeCallbacks.pop_front()();
} }
function createNewContext() {
audioContextInstance = new (window.webkitAudioContext || window.AudioContext)();
audioContextInitializeCallbacks.unshift(() => {
globalAudioGainInstance = audioContextInstance.createGain();
globalAudioGainInstance.gain.value = _no_device ? 0 : _master_volume;
globalAudioGainInstance.connect(audioContextInstance.destination);
});
if(audioContextInstance.state === "suspended") {
audioContextRequiredGesture = true;
return audioContextInstance;
} else if(audioContextInstance.state === "running") {
fire_initialized();
} else if(audioContextInstance.state === "closed") {
throw tr("Audio context has been closed");
} else {
throw tr("invalid audio context state");
}
}
export function context() : AudioContext { export function context() : AudioContext {
if(_globalContext && _globalContext.state != "suspended") return _globalContext; if(audioContextInstance || kAvoidAudioContextWarning)
return audioContextInstance;
if(!_globalContext) if(!audioContextInstance)
_globalContext = new (window.webkitAudioContext || window.AudioContext)(); createNewContext();
_initialized_listener.unshift(() => { return audioContextInstance;
_global_destination = _globalContext.createGain();
_global_destination.gain.value = _no_device ? 0 : _master_volume;
_global_destination.connect(_globalContext.destination);
});
if(_globalContext.state == "suspended") {
if(!_globalContextPromise) {
(_globalContextPromise = _globalContext.resume()).then(() => {
fire_initialized();
}).catch(error => {
loader.critical_error("Failed to initialize global audio context! (" + error + ")");
});
}
_globalContext.resume(); //We already have our listener
return _globalContext;
}
if(_globalContext.state == "running") {
fire_initialized();
return _globalContext;
}
return _globalContext;
} }
export function get_master_volume() : number { export function get_master_volume() : number {
@ -84,22 +85,22 @@ export function get_master_volume() : number {
} }
export function set_master_volume(volume: number) { export function set_master_volume(volume: number) {
_master_volume = volume; _master_volume = volume;
if(_global_destination) if(globalAudioGainInstance)
_global_destination.gain.value = _no_device ? 0 : _master_volume; globalAudioGainInstance.gain.value = _no_device ? 0 : _master_volume;
} }
export function destination() : AudioNode { export function destination() : AudioNode {
const ctx = context(); const ctx = context();
if(!ctx) throw tr("Audio player isn't initialized yet!"); if(!ctx) throw tr("Audio player isn't initialized yet!");
return _global_destination; return globalAudioGainInstance;
} }
export function on_ready(cb: () => any) { export function on_ready(cb: () => any) {
if(initialized()) if(initialized())
cb(); cb();
else else
_initialized_listener.push(cb); audioContextInitializeCallbacks.push(cb);
} }
export const WEB_DEVICE: Device = { export const WEB_DEVICE: Device = {
@ -114,7 +115,7 @@ export function available_devices() : Promise<Device[]> {
export function set_device(device_id: string) : Promise<void> { export function set_device(device_id: string) : Promise<void> {
_no_device = !device_id; _no_device = !device_id;
_global_destination.gain.value = _no_device ? 0 : _master_volume; globalAudioGainInstance.gain.value = _no_device ? 0 : _master_volume;
return Promise.resolve(); return Promise.resolve();
} }
@ -124,5 +125,15 @@ export function current_device() : Device {
} }
export function initializeFromGesture() { export function initializeFromGesture() {
context(); if(audioContextInstance) {
if(audioContextInstance.state !== "running") {
audioContextInstance.resume().then(() => {
fire_initialized();
}).catch(error => {
log.error(LogCategory.AUDIO, tr("Failed to initialize audio context instance from gesture: %o"), error);
});
}
} else {
createNewContext();
}
} }