import {AbstractInput, LevelMeter} from "../voice/RecorderBase"; import {Registry} from "../events"; export interface AudioRecorderBacked { createInput() : AbstractInput; createLevelMeter(device: InputDevice) : Promise; getDeviceList() : DeviceList; } export interface DeviceListEvents { notify_list_updated: { removedDeviceCount: number, addedDeviceCount: number }, notify_state_changed: { oldState: DeviceListState; newState: DeviceListState; }, notify_permissions_changed: { oldState: PermissionState, newState: PermissionState } } export type DeviceListState = "healthy" | "uninitialized" | "no-permissions" | "error"; export interface InputDevice { deviceId: string; driver: string; name: string; } export namespace InputDevice { export const NoDeviceId = "none"; export const DefaultDeviceId = "default"; } export type PermissionState = "granted" | "denied" | "unknown"; export interface DeviceList { getEvents() : Registry; isRefreshAvailable() : boolean; refresh() : Promise; /* implicitly update our own permission state */ requestPermissions() : Promise; getPermissionState() : PermissionState; getStatus() : DeviceListState; getDevices() : InputDevice[]; getDefaultDeviceId() : string; awaitHealthy(): Promise; awaitInitialized() : Promise; } export abstract class AbstractDeviceList implements DeviceList { protected readonly events: Registry; protected listState: DeviceListState; protected permissionState: PermissionState; protected constructor() { this.events = new Registry(); this.permissionState = "unknown"; this.listState = "uninitialized"; } getStatus(): DeviceListState { return this.listState; } getPermissionState(): PermissionState { return this.permissionState; } protected setState(state: DeviceListState) { if(this.listState === state) { return; } const oldState = this.listState; this.listState = state; this.events.fire("notify_state_changed", { oldState: oldState, newState: state }); } protected setPermissionState(state: PermissionState) { if(this.permissionState === state) { return; } const oldState = this.permissionState; this.permissionState = state; this.events.fire("notify_permissions_changed", { oldState: oldState, newState: state }); } awaitInitialized(): Promise { if(this.listState !== "uninitialized") { return Promise.resolve(); } return new Promise(resolve => { const callback = (event: DeviceListEvents["notify_state_changed"]) => { if(event.newState === "uninitialized") { return; } this.events.off("notify_state_changed", callback); resolve(); }; this.events.on("notify_state_changed", callback); }); } awaitHealthy(): Promise { if(this.listState === "healthy") { return Promise.resolve(); } return new Promise(resolve => { const callback = (event: DeviceListEvents["notify_state_changed"]) => { if(event.newState !== "healthy") return; this.events.off("notify_state_changed", callback); resolve(); }; this.events.on("notify_state_changed", callback); }); } abstract getDefaultDeviceId(): string; abstract getDevices(): InputDevice[]; abstract getEvents(): Registry; abstract isRefreshAvailable(): boolean; abstract refresh(): Promise; abstract requestPermissions(): Promise; } let recorderBackend: AudioRecorderBacked; export function getRecorderBackend() : AudioRecorderBacked { if(typeof recorderBackend === "undefined") { throw tr("the recorder backend hasn't been set yet"); } return recorderBackend; } export function setRecorderBackend(instance: AudioRecorderBacked) { if(typeof recorderBackend !== "undefined") { throw tr("a recorder backend has already been initialized"); } recorderBackend = instance; }