TeaSpeak-Client/modules/renderer/audio/AudioPlayer.ts

123 lines
3.6 KiB
TypeScript

import * as native from "tc-native/connection";
import {AudioBackend, OutputDevice} from "tc-shared/audio/Player";
export class NativeAudioPlayer implements AudioBackend {
private readonly audioContext: AudioContext;
private initializedPromises: (() => void)[];
private currentDevice: native.audio.AudioDevice;
constructor() {
this.audioContext = new AudioContext();
this.initializedPromises = [];
native.audio.initialize(() => {
const promises = this.initializedPromises;
this.initializedPromises = undefined;
promises.forEach(callback => callback());
});
}
executeWhenInitialized(callback: () => void): any {
if(this.initializedPromises) {
this.initializedPromises.push(callback);
} else {
callback();
}
}
getMasterVolume(): number {
return native.audio.playback.get_master_volume();
}
setMasterVolume(volume: number): any {
native.audio.playback.set_master_volume(volume);
}
getAudioContext(): AudioContext | undefined {
return this.audioContext;
}
async getAvailableDevices(): Promise<OutputDevice[]> {
const devices = native.audio.available_devices().filter(e => e.output_supported || e.output_default);
/* Fixup MMEs character limit of 31 by trying to resolve a name somewhere else */
devices.forEach(device => {
if(device.driver !== "MME") {
return;
}
const fallback = devices.find(fallbackDevice => fallbackDevice.name.substring(0, 31) === device.name && fallbackDevice.name.length > 31);
if(fallback) {
device.name = fallback.name;
}
});
return devices.map(entry => ({
deviceId: entry.device_id,
driver: entry.driver,
name: entry.name
}));
}
getCurrentDevice(): OutputDevice {
if(this.currentDevice) {
return {
name: this.currentDevice.name,
driver: this.currentDevice.driver,
deviceId: this.currentDevice.device_id
};
}
const defaultDevice = native.audio.available_devices().find(entry => entry.output_default);
if(defaultDevice) {
return {
name: defaultDevice.name,
driver: defaultDevice.driver,
deviceId: defaultDevice.device_id
};
}
return {
name: "Default device",
deviceId: "default",
driver: "default driver"
};
}
async setCurrentDevice(targetId: string | undefined): Promise<void> {
const device = native.audio.available_devices().find(e => e.device_id == targetId);
if(!device) {
console.warn("Missing audio device with is %s", targetId);
throw "invalid device id";
}
try {
native.audio.playback.set_device(device.device_id);
} catch(error) {
if(error instanceof Error) {
throw error.message;
}
throw error;
}
this.currentDevice = device;
}
getDefaultDeviceId(): string {
return native.audio.available_devices().find(entry => entry.output_default)?.device_id || "default";
}
isDeviceRefreshAvailable(): boolean {
return false;
}
isInitialized(): boolean {
return !this.initializedPromises;
}
refreshDevices(): Promise<void> {
return Promise.resolve(undefined);
}
}