Finally got fully rid of the initial backend glue and changes all systems to provider
parent
38ea11725f
commit
e2be859266
|
@ -1,4 +1,7 @@
|
|||
# Changelog:
|
||||
* **18.03.21**
|
||||
- Finally got fully rid of the initial backend glue and changes all systems to provider
|
||||
|
||||
* **17.03.21**
|
||||
- Updated from webpack 4 to webpack 5
|
||||
- Reworked the client build process
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
# File structure
|
||||
The TeaSpeak web client is separated into 2 different parts.
|
||||
|
||||
## I) Application files
|
||||
Application files are all files which directly belong to the app itself.
|
||||
Like the javascript files who handle the UI stuff or even translation templates.
|
||||
Theses files are separated into two type of files.
|
||||
1. [Shared application files](#1-shared-application-files)
|
||||
2. [Web application files](#2-web-application-files)
|
||||
|
||||
### 1. Shared application files
|
||||
Containing all files used by the TeaSpeak client and the Web client.
|
||||
All of these files will be found within the folder `shared`.
|
||||
This folder follows the general application file structure.
|
||||
More information could be found [here](#application-file-structure)
|
||||
|
||||
### 2. Web application files
|
||||
All files which only belong to a browser only instance.
|
||||
All of these files will be found within the folder `web`.
|
||||
This folder follows the general application file structure.
|
||||
More information could be found [here](#application-file-structure)
|
||||
|
||||
### application file structure
|
||||
Every application root contains several subfolders.
|
||||
In the following list will be listed which files belong to which folder
|
||||
|
||||
| Folder | Description |
|
||||
| --- | --- |
|
||||
| `audio` | This folder contains all audio files used by the application. More information could be found [here](). |
|
||||
| `css` | This folder contains all style sheets used by the application. More information could be found [here](). |
|
||||
| `js` | This folder contains all javascript files used by the application. More information could be found [here](). |
|
||||
| `html` | This folder contains all HTML and PHP files used by the application. More information could be found [here](). |
|
||||
| `i18n` | This folder contains all default translations. Information about the translation system could be found [here](). |
|
||||
| `img` | This folder contains all image files. |
|
||||
|
||||
## I) Additional tools
|
||||
|
||||
## Environment builder
|
||||
The environment builder is one of the most important tools of the entire project.
|
||||
This tool, basically implemented in the file `files.php`, will be your helper while live developing.
|
||||
What this tool does is, it creates a final environment where you could navigate to with your browser.
|
||||
It merges all the type separated files, which had been listed above ([here](#application-file-structure)).
|
|
@ -1,17 +0,0 @@
|
|||
import {Device} from "tc-shared/audio/player";
|
||||
|
||||
export function initialize() : boolean;
|
||||
export function initialized() : boolean;
|
||||
|
||||
export function get_master_volume() : number;
|
||||
export function set_master_volume(volume: number);
|
||||
|
||||
export function on_ready(cb: () => any);
|
||||
|
||||
export function available_devices() : Promise<Device[]>;
|
||||
export function set_device(device_id: string) : Promise<void>;
|
||||
export function current_device() : Device;
|
||||
|
||||
export function initializeFromGesture();
|
||||
|
||||
export function globalAudioContext() : AudioContext;
|
|
@ -1,3 +0,0 @@
|
|||
import {SoundFile} from "tc-shared/sound/Sounds";
|
||||
|
||||
export function play_sound(file: SoundFile) : Promise<void>;
|
|
@ -1,12 +0,0 @@
|
|||
import {KeyEvent, KeyHook, SpecialKey} from "tc-shared/PPTListener";
|
||||
|
||||
export function initialize() : Promise<void>;
|
||||
export function finalize(); // most the times not really required
|
||||
|
||||
export function register_key_listener(listener: (_: KeyEvent) => any);
|
||||
export function unregister_key_listener(listener: (_: KeyEvent) => any);
|
||||
|
||||
export function register_key_hook(hook: KeyHook);
|
||||
export function unregister_key_hook(hook: KeyHook);
|
||||
|
||||
export function key_pressed(code: string | SpecialKey) : boolean;
|
|
@ -1,2 +0,0 @@
|
|||
This folder contains declarations files which are required to be implemented
|
||||
Else the UI shared pack wound work
|
|
@ -2,7 +2,7 @@ import {AbstractServerConnection} from "./connection/ConnectionBase";
|
|||
import {PermissionManager} from "./permission/PermissionManager";
|
||||
import {GroupManager} from "./permission/GroupManager";
|
||||
import {ServerSettings, Settings, settings, StaticSettings} from "./settings";
|
||||
import {Sound, SoundManager} from "./sound/Sounds";
|
||||
import {Sound, SoundManager} from "./audio/Sounds";
|
||||
import {ConnectionProfile} from "./profiles/ConnectionProfile";
|
||||
import {LogCategory, logError, logInfo, logTrace, logWarn} from "./log";
|
||||
import {createErrorModal, createInfoModal, createInputModal, Modal} from "./ui/elements/Modal";
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import * as ppt from "tc-backend/ppt";
|
||||
import * as log from "./log";
|
||||
import {LogCategory, logError, logWarn} from "./log";
|
||||
import {KeyDescriptor, KeyHook} from "./PPTListener";
|
||||
import {getKeyBoard, KeyDescriptor, KeyHook} from "./PPTListener";
|
||||
import {Settings, settings} from "./settings";
|
||||
import {server_connections} from "tc-shared/ConnectionManager";
|
||||
import {tr} from "./i18n/localize";
|
||||
|
@ -181,18 +179,18 @@ function bindKey(action: string, key: KeyDescriptor) {
|
|||
|
||||
keyBindings[action] = {
|
||||
hook: Object.assign({
|
||||
callback_press: () => control.handler(),
|
||||
callback_release: () => {},
|
||||
callbackPress: () => control.handler(),
|
||||
callbackRelease: () => {},
|
||||
cancel: false
|
||||
}, key),
|
||||
binding: key
|
||||
};
|
||||
ppt.register_key_hook(keyBindings[action].hook);
|
||||
getKeyBoard().registerHook(keyBindings[action].hook);
|
||||
}
|
||||
|
||||
export function setKey(action: string, key?: KeyDescriptor) {
|
||||
if(typeof keyBindings[action] !== "undefined") {
|
||||
ppt.unregister_key_hook(keyBindings[action].hook);
|
||||
getKeyBoard().unregisterHook(keyBindings[action].hook);
|
||||
delete keyBindings[action];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { tr } from "./i18n/localize";
|
||||
import {LogCategory, logTrace} from "tc-shared/log";
|
||||
|
||||
export enum KeyCode {
|
||||
KEY_CANCEL = 3,
|
||||
|
@ -134,57 +135,199 @@ export enum SpecialKey {
|
|||
}
|
||||
|
||||
export interface KeyDescriptor {
|
||||
key_code: string;
|
||||
keyCode: string;
|
||||
|
||||
key_ctrl: boolean;
|
||||
key_windows: boolean;
|
||||
key_shift: boolean;
|
||||
key_alt: boolean;
|
||||
keyCtrl: boolean;
|
||||
keyWindows: boolean;
|
||||
keyShift: boolean;
|
||||
keyAlt: boolean;
|
||||
}
|
||||
|
||||
export interface KeyEvent extends KeyDescriptor {
|
||||
readonly type: EventType;
|
||||
|
||||
readonly key: string;
|
||||
}
|
||||
|
||||
export interface KeyHook extends KeyDescriptor {
|
||||
cancel: boolean;
|
||||
|
||||
callback_press: () => any;
|
||||
callback_release: () => any;
|
||||
callbackPress: () => any;
|
||||
callbackRelease: () => any;
|
||||
}
|
||||
|
||||
export function key_description(key: KeyDescriptor) {
|
||||
export interface KeyBoardBackend {
|
||||
registerListener(listener: (event: KeyEvent) => void);
|
||||
unregisterListener(listener: (event: KeyEvent) => void);
|
||||
|
||||
registerHook(hook: KeyHook);
|
||||
unregisterHook(hook: KeyHook);
|
||||
|
||||
isKeyPressed(key: string | SpecialKey) : boolean;
|
||||
}
|
||||
|
||||
export class AbstractKeyBoard implements KeyBoardBackend {
|
||||
protected readonly registeredListener: ((event: KeyEvent) => void)[];
|
||||
protected readonly activeSpecialKeys: { [key: number] : boolean };
|
||||
protected readonly activeKeys;
|
||||
|
||||
protected registeredKeyHooks: KeyHook[] = [];
|
||||
protected activeKeyHooks: KeyHook[] = [];
|
||||
|
||||
constructor() {
|
||||
this.activeSpecialKeys = {};
|
||||
this.activeKeys = {};
|
||||
this.registeredListener = [];
|
||||
}
|
||||
|
||||
protected destroy() {}
|
||||
|
||||
isKeyPressed(key: string | SpecialKey): boolean {
|
||||
if(typeof(key) === 'string') {
|
||||
return typeof this.activeKeys[key] !== "undefined";
|
||||
}
|
||||
|
||||
return this.activeSpecialKeys[key];
|
||||
}
|
||||
|
||||
registerHook(hook: KeyHook) {
|
||||
this.registeredKeyHooks.push(hook);
|
||||
}
|
||||
|
||||
unregisterHook(hook: KeyHook) {
|
||||
this.registeredKeyHooks.remove(hook);
|
||||
this.activeKeyHooks.remove(hook);
|
||||
}
|
||||
|
||||
registerListener(listener: (event: KeyEvent) => void) {
|
||||
this.registeredListener.push(listener);
|
||||
}
|
||||
|
||||
unregisterListener(listener: (event: KeyEvent) => void) {
|
||||
this.registeredListener.remove(listener);
|
||||
}
|
||||
|
||||
protected fireKeyEvent(event: KeyEvent) {
|
||||
//console.debug("Trigger key event %o", key_event);
|
||||
for(const listener of this.registeredListener) {
|
||||
listener(event);
|
||||
}
|
||||
|
||||
if(event.type == EventType.KEY_TYPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
let oldHooks = [...this.activeKeyHooks];
|
||||
let newHooks = [];
|
||||
|
||||
this.activeSpecialKeys[SpecialKey.ALT] = event.keyAlt;
|
||||
this.activeSpecialKeys[SpecialKey.CTRL] = event.keyCtrl;
|
||||
this.activeSpecialKeys[SpecialKey.SHIFT] = event.keyShift;
|
||||
this.activeSpecialKeys[SpecialKey.WINDOWS] = event.keyWindows;
|
||||
|
||||
delete this.activeKeys[event.keyCode];
|
||||
if(event.type == EventType.KEY_PRESS) {
|
||||
this.activeKeys[event.keyCode] = event;
|
||||
|
||||
for(const hook of this.registeredKeyHooks) {
|
||||
if(hook.keyCode !== event.keyCode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(hook.keyAlt != event.keyAlt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(hook.keyCtrl != event.keyCtrl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(hook.keyShift != event.keyShift) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(hook.keyWindows != event.keyWindows) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newHooks.push(hook);
|
||||
if(!oldHooks.remove(hook) && hook.callbackPress) {
|
||||
hook.callbackPress();
|
||||
logTrace(LogCategory.GENERAL, tr("Trigger key press for %o!"), hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//We have a new situation
|
||||
for(const hook of oldHooks) {
|
||||
//Do not test for meta key states because they could differ in a key release event
|
||||
if(hook.keyCode === event.keyCode) {
|
||||
if(hook.callbackRelease) {
|
||||
hook.callbackRelease();
|
||||
logTrace(LogCategory.GENERAL, tr("Trigger key release for %o!"), hook);
|
||||
}
|
||||
} else {
|
||||
newHooks.push(hook);
|
||||
}
|
||||
}
|
||||
|
||||
this.activeKeyHooks = newHooks;
|
||||
}
|
||||
|
||||
protected resetKeyboardState() {
|
||||
this.activeSpecialKeys[SpecialKey.ALT] = false;
|
||||
this.activeSpecialKeys[SpecialKey.CTRL] = false;
|
||||
this.activeSpecialKeys[SpecialKey.SHIFT] = false;
|
||||
this.activeSpecialKeys[SpecialKey.WINDOWS] = false;
|
||||
|
||||
for(const code of Object.keys(this.activeKeys)) {
|
||||
delete this.activeKeys[code];
|
||||
}
|
||||
|
||||
for(const hook of this.activeKeyHooks) {
|
||||
hook.callbackRelease();
|
||||
}
|
||||
|
||||
this.activeKeyHooks = [];
|
||||
}
|
||||
}
|
||||
|
||||
let keyBoardBackend: KeyBoardBackend;
|
||||
export function getKeyBoard() : KeyBoardBackend {
|
||||
return keyBoardBackend;
|
||||
}
|
||||
|
||||
export function setKeyBoardBackend(newBackend: KeyBoardBackend) {
|
||||
keyBoardBackend = newBackend;
|
||||
}
|
||||
|
||||
export function getKeyDescription(key: KeyDescriptor) {
|
||||
let result = "";
|
||||
if(key.key_shift) {
|
||||
if(key.keyShift) {
|
||||
result += " + " + tr("Shift");
|
||||
}
|
||||
|
||||
if(key.key_alt) {
|
||||
if(key.keyAlt) {
|
||||
result += " + " + tr("Alt");
|
||||
}
|
||||
|
||||
if(key.key_ctrl) {
|
||||
if(key.keyCtrl) {
|
||||
result += " + " + tr("CTRL");
|
||||
}
|
||||
|
||||
if(key.key_windows) {
|
||||
if(key.keyWindows) {
|
||||
result += " + " + tr("Win");
|
||||
}
|
||||
|
||||
if(key.key_code) {
|
||||
let key_name;
|
||||
if(key.key_code.startsWith("Key")) {
|
||||
key_name = key.key_code.substr(3);
|
||||
} else if(key.key_code.startsWith("Digit")) {
|
||||
key_name = key.key_code.substr(5);
|
||||
} else if(key.key_code.startsWith("Numpad")) {
|
||||
key_name = "Numpad " + key.key_code.substr(6);
|
||||
if(key.keyCode) {
|
||||
let keyName;
|
||||
if(key.keyCode.startsWith("Key")) {
|
||||
keyName = key.keyCode.substr(3);
|
||||
} else if(key.keyCode.startsWith("Digit")) {
|
||||
keyName = key.keyCode.substr(5);
|
||||
} else if(key.keyCode.startsWith("Numpad")) {
|
||||
keyName = "Numpad " + key.keyCode.substr(6);
|
||||
} else {
|
||||
key_name = key.key_code;
|
||||
keyName = key.keyCode;
|
||||
}
|
||||
result += " + " + key_name;
|
||||
result += " + " + keyName;
|
||||
}
|
||||
return result ? result.substr(3) : tr("unset");
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
export interface OutputDevice {
|
||||
device_id: string;
|
||||
|
||||
driver: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface AudioBackendEvents {
|
||||
notify_initialized: {},
|
||||
notify_volume_changed: { oldVolume: number, newVolume: number }
|
||||
}
|
||||
|
||||
export interface AudioBackend {
|
||||
isInitialized() : boolean;
|
||||
getAudioContext() : AudioContext | undefined;
|
||||
|
||||
isDeviceRefreshAvailable() : boolean;
|
||||
refreshDevices() : Promise<void>;
|
||||
|
||||
getAvailableDevices() : Promise<OutputDevice[]>;
|
||||
getDefaultDeviceId() : string;
|
||||
|
||||
getCurrentDevice() : OutputDevice;
|
||||
setCurrentDevice(targetId: string | undefined) : Promise<void>;
|
||||
|
||||
getMasterVolume() : number;
|
||||
setMasterVolume(volume: number);
|
||||
|
||||
executeWhenInitialized(callback: () => void);
|
||||
}
|
||||
|
||||
let backend: AudioBackend;
|
||||
export function getAudioBackend(): AudioBackend {
|
||||
return backend;
|
||||
}
|
||||
|
||||
export function setAudioBackend(newBackend: AudioBackend) {
|
||||
backend = newBackend;
|
||||
}
|
|
@ -4,11 +4,9 @@ import {AbstractInput, LevelMeter} from "../voice/RecorderBase";
|
|||
import {Registry} from "../events";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
|
||||
export type DeviceQueryResult = {}
|
||||
|
||||
export interface AudioRecorderBacked {
|
||||
createInput() : AbstractInput;
|
||||
createLevelMeter(device: IDevice) : Promise<LevelMeter>;
|
||||
createLevelMeter(device: InputDevice) : Promise<LevelMeter>;
|
||||
|
||||
getDeviceList() : DeviceList;
|
||||
|
||||
|
@ -35,14 +33,14 @@ export interface DeviceListEvents {
|
|||
|
||||
export type DeviceListState = "healthy" | "uninitialized" | "no-permissions" | "error";
|
||||
|
||||
export interface IDevice {
|
||||
export interface InputDevice {
|
||||
deviceId: string;
|
||||
|
||||
driver: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export namespace IDevice {
|
||||
export namespace InputDevice {
|
||||
export const NoDeviceId = "none";
|
||||
export const DefaultDeviceId = "default";
|
||||
}
|
||||
|
@ -60,7 +58,7 @@ export interface DeviceList {
|
|||
getPermissionState() : PermissionState;
|
||||
|
||||
getStatus() : DeviceListState;
|
||||
getDevices() : IDevice[];
|
||||
getDevices() : InputDevice[];
|
||||
|
||||
getDefaultDeviceId() : string;
|
||||
|
||||
|
@ -144,7 +142,7 @@ export abstract class AbstractDeviceList implements DeviceList {
|
|||
}
|
||||
|
||||
abstract getDefaultDeviceId(): string;
|
||||
abstract getDevices(): IDevice[];
|
||||
abstract getDevices(): InputDevice[];
|
||||
abstract getEvents(): Registry<DeviceListEvents>;
|
||||
abstract isRefreshAvailable(): boolean;
|
||||
abstract refresh(): Promise<void>;
|
|
@ -1,8 +1,6 @@
|
|||
import * as log from "../log";
|
||||
import {LogCategory, logError, logInfo, logWarn} from "../log";
|
||||
import {Settings, settings} from "../settings";
|
||||
import {ConnectionHandler} from "../ConnectionHandler";
|
||||
import * as sbackend from "tc-backend/audio/sounds";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
|
||||
export enum Sound {
|
||||
|
@ -226,7 +224,6 @@ export async function resolve_sound(sound: Sound) : Promise<SoundHandle> {
|
|||
}
|
||||
|
||||
export let manager: SoundManager;
|
||||
|
||||
export class SoundManager {
|
||||
private readonly _handle: ConnectionHandler;
|
||||
private _playing_sounds: {[key: string]:number} = {};
|
||||
|
@ -256,7 +253,7 @@ export class SoundManager {
|
|||
}
|
||||
|
||||
this._playing_sounds[handle.filename] = (this._playing_sounds[handle.filename] || 0) + 1;
|
||||
sbackend.play_sound({
|
||||
getSoundBackend().playSound({
|
||||
path: "audio/" + handle.filename,
|
||||
volume: volume * master_volume
|
||||
}).then(() => {
|
||||
|
@ -275,4 +272,17 @@ export class SoundManager {
|
|||
options.callback(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface SoundBackend {
|
||||
playSound(sound: SoundFile) : Promise<void>;
|
||||
}
|
||||
let soundBackend: SoundBackend;
|
||||
|
||||
export function getSoundBackend() {
|
||||
return soundBackend;
|
||||
}
|
||||
|
||||
export function setSoundBackend(newSoundBackend: SoundBackend) {
|
||||
soundBackend = newSoundBackend;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
export interface Device {
|
||||
device_id: string;
|
||||
|
||||
driver: string;
|
||||
name: string;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import {LogCategory, logError, logInfo, logWarn} from "../log";
|
||||
import {AbstractServerConnection, CommandOptions, ServerCommand} from "../connection/ConnectionBase";
|
||||
import {Sound} from "../sound/Sounds";
|
||||
import {Sound} from "../audio/Sounds";
|
||||
import {CommandResult} from "../connection/ServerConnectionDeclaration";
|
||||
import {createErrorModal, createInfoModal, createInputModal, createModal} from "../ui/elements/Modal";
|
||||
import {
|
||||
|
|
|
@ -9,9 +9,9 @@ import {RemoteRTPAudioTrack, RemoteRTPTrackState, RemoteRTPVideoTrack, TrackClie
|
|||
import {SdpCompressor, SdpProcessor} from "./SdpUtils";
|
||||
import {ErrorCode} from "tc-shared/connection/ErrorCode";
|
||||
import {WhisperTarget} from "tc-shared/voice/VoiceWhisper";
|
||||
import {globalAudioContext} from "tc-backend/audio/player";
|
||||
import {VideoBroadcastConfig, VideoBroadcastType} from "tc-shared/connection/VideoConnection";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
|
||||
const kSdpCompressionMode = 1;
|
||||
|
||||
|
@ -144,7 +144,7 @@ function getIdleTrack(kind: "video" | "audio") : MediaStreamTrack | null {
|
|||
return dummyVideoTrack;
|
||||
} else if(kind === "audio") {
|
||||
if(!dummyAudioTrack) {
|
||||
const dest = globalAudioContext().createMediaStreamDestination();
|
||||
const dest = getAudioBackend().getAudioContext().createMediaStreamDestination();
|
||||
dummyAudioTrack = dest.stream.getAudioTracks()[0];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Registry} from "tc-shared/events";
|
||||
import {LogCategory, logTrace, logWarn} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {globalAudioContext, on_ready} from "tc-backend/audio/player";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
|
||||
export interface TrackClientInfo {
|
||||
media?: number,
|
||||
|
@ -170,13 +170,13 @@ export class RemoteRTPAudioTrack extends RemoteRTPTrack {
|
|||
}
|
||||
*/
|
||||
|
||||
on_ready(() => {
|
||||
getAudioBackend().executeWhenInitialized(() => {
|
||||
if(!this.mediaStream) {
|
||||
/* we've already been destroyed */
|
||||
return;
|
||||
}
|
||||
|
||||
const audioContext = globalAudioContext();
|
||||
const audioContext = getAudioBackend().getAudioContext();
|
||||
this.audioNode = audioContext.createMediaStreamSource(this.mediaStream);
|
||||
this.gainNode = audioContext.createGain();
|
||||
this.updateGainNode();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {Registry} from "../events";
|
||||
import {ClientGlobalControlEvents} from "../events/GlobalEvents";
|
||||
import {Sound} from "../sound/Sounds";
|
||||
import {ConnectionHandler} from "../ConnectionHandler";
|
||||
import {createErrorModal, createInfoModal, createInputModal} from "../ui/elements/Modal";
|
||||
import PermissionType from "../permission/PermissionType";
|
||||
|
@ -20,6 +19,7 @@ import {LogCategory, logError, logWarn} from "tc-shared/log";
|
|||
import {spawnEchoTestModal} from "tc-shared/ui/modal/echo-test/Controller";
|
||||
import {spawnConnectModalNew} from "tc-shared/ui/modal/connect/Controller";
|
||||
import {spawnBookmarkModal} from "tc-shared/ui/modal/bookmarks/Controller";
|
||||
import {Sound} from "tc-shared/audio/Sounds";
|
||||
|
||||
/*
|
||||
function initialize_sounds(event_registry: Registry<ClientGlobalControlEvents>) {
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import * as bipc from "./ipc/BrowserIPC";
|
||||
import * as sound from "./sound/Sounds";
|
||||
import * as sound from "./audio/Sounds";
|
||||
import * as i18n from "./i18n/localize";
|
||||
import {tra} from "./i18n/localize";
|
||||
import * as fidentity from "./profiles/identities/TeaForumIdentity";
|
||||
import * as aplayer from "tc-backend/audio/player";
|
||||
import * as ppt from "tc-backend/ppt";
|
||||
import * as global_ev_handler from "./events/ClientGlobalControlHandler";
|
||||
import {AppParameters, settings, Settings, UrlParameterBuilder, UrlParameterParser} from "tc-shared/settings";
|
||||
import {LogCategory, logDebug, logError, logInfo, logWarn} from "tc-shared/log";
|
||||
|
@ -55,6 +52,7 @@ import "./ui/elements/Tab";
|
|||
import "./clientservice";
|
||||
import "./text/bbcode/InviteController";
|
||||
import "./text/bbcode/YoutubeController";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
|
||||
assertMainApplication();
|
||||
|
||||
|
@ -73,12 +71,7 @@ async function initialize() {
|
|||
|
||||
async function initializeApp() {
|
||||
global_ev_handler.initialize(global_client_actions);
|
||||
|
||||
if(!aplayer.initialize()) {
|
||||
console.warn(tr("Failed to initialize audio controller!"));
|
||||
}
|
||||
|
||||
aplayer.on_ready(() => aplayer.set_master_volume(settings.getValue(Settings.KEY_SOUND_MASTER) / 100));
|
||||
getAudioBackend().setMasterVolume(settings.getValue(Settings.KEY_SOUND_MASTER) / 100);
|
||||
|
||||
const recorder = new RecorderProfile("default");
|
||||
try {
|
||||
|
@ -93,14 +86,6 @@ async function initializeApp() {
|
|||
logInfo(LogCategory.AUDIO, tr("Sounds initialized"));
|
||||
});
|
||||
sound.set_master_volume(settings.getValue(Settings.KEY_SOUND_MASTER_SOUNDS) / 100);
|
||||
|
||||
try {
|
||||
await ppt.initialize();
|
||||
} catch(error) {
|
||||
logError(LogCategory.GENERAL, tr("Failed to initialize ppt!\nError: %o"), error);
|
||||
loader.critical_error(tr("Failed to initialize ppt!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* The native client has received a connect request. */
|
||||
|
@ -200,7 +185,7 @@ async function doHandleConnectRequest(serverAddress: string, serverUniqueId: str
|
|||
return { status: "profile-invalid" };
|
||||
}
|
||||
|
||||
if(!aplayer.initialized()) {
|
||||
if(!getAudioBackend().isInitialized()) {
|
||||
/* Trick the client into clicking somewhere on the site to initialize audio */
|
||||
const resultPromise = new Promise<boolean>(resolve => {
|
||||
spawnYesNo(tra("Connect to {}", serverAddress), tra("Would you like to connect to {}?", serverAddress), resolve).open();
|
||||
|
@ -211,7 +196,7 @@ async function doHandleConnectRequest(serverAddress: string, serverUniqueId: str
|
|||
return { status: "client-aborted" };
|
||||
}
|
||||
|
||||
await new Promise(resolve => aplayer.on_ready(resolve));
|
||||
await new Promise(resolve => getAudioBackend().executeWhenInitialized(resolve));
|
||||
}
|
||||
|
||||
const clientNickname = parameters.getValue(AppParameters.KEY_CONNECT_NICKNAME, undefined);
|
||||
|
@ -403,15 +388,6 @@ const task_teaweb_starter: loader.Task = {
|
|||
try {
|
||||
await initializeApp();
|
||||
main();
|
||||
if(!aplayer.initialized()) {
|
||||
logInfo(LogCategory.VOICE, tr("Initialize audio controller later!"));
|
||||
if(!aplayer.initializeFromGesture) {
|
||||
console.error(tr("Missing aplayer.initializeFromGesture"));
|
||||
} else {
|
||||
$(document).one('click', () => aplayer.initializeFromGesture());
|
||||
}
|
||||
}
|
||||
|
||||
loader.config.abortAnimationOnFinish = settings.getValue(Settings.KEY_LOADER_ANIMATION_ABORT);
|
||||
} catch (ex) {
|
||||
console.error(ex.stack);
|
||||
|
|
|
@ -6,10 +6,9 @@ import {PermissionType} from "../permission/PermissionType";
|
|||
import {settings, Settings} from "../settings";
|
||||
import * as contextmenu from "../ui/elements/ContextMenu";
|
||||
import {MenuEntryType} from "../ui/elements/ContextMenu";
|
||||
import {Sound} from "../sound/Sounds";
|
||||
import {Sound} from "../audio/Sounds";
|
||||
import {createErrorModal, createInfoModal, createInputModal} from "../ui/elements/Modal";
|
||||
import {CommandResult} from "../connection/ServerConnectionDeclaration";
|
||||
import * as htmltags from "../ui/htmltags";
|
||||
import {hashPassword} from "../utils/helpers";
|
||||
import {openChannelInfo} from "../ui/modal/ModalChannelInfo";
|
||||
import {formatMessage} from "../ui/frames/chat";
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
|
|||
import {MenuEntryType} from "tc-shared/ui/elements/ContextMenu";
|
||||
import {LogCategory, logDebug, logError, logWarn} from "tc-shared/log";
|
||||
import {PermissionType} from "tc-shared/permission/PermissionType";
|
||||
import {Sound} from "tc-shared/sound/Sounds";
|
||||
import {Sound} from "tc-shared/audio/Sounds";
|
||||
import {Group} from "tc-shared/permission/GroupManager";
|
||||
import {ServerAddress, ServerEntry} from "./Server";
|
||||
import {ChannelEntry, ChannelProperties, ChannelSubscribeMode} from "./Channel";
|
||||
|
|
|
@ -4,7 +4,7 @@ import {ChannelTree} from "./ChannelTree";
|
|||
import * as log from "../log";
|
||||
import {LogCategory, logDebug, logError, logInfo, LogType} from "../log";
|
||||
import {Settings, settings} from "../settings";
|
||||
import {Sound} from "../sound/Sounds";
|
||||
import {Sound} from "../audio/Sounds";
|
||||
import {Group, GroupManager, GroupTarget, GroupType} from "../permission/GroupManager";
|
||||
import PermissionType from "../permission/PermissionType";
|
||||
import {createErrorModal, createInputModal} from "../ui/elements/Modal";
|
||||
|
|
|
@ -3,7 +3,7 @@ import {Settings, settings} from "../settings";
|
|||
import * as contextmenu from "../ui/elements/ContextMenu";
|
||||
import * as log from "../log";
|
||||
import {LogCategory, logInfo, LogType} from "../log";
|
||||
import {Sound} from "../sound/Sounds";
|
||||
import {Sound} from "../audio/Sounds";
|
||||
import {openServerInfo} from "../ui/modal/ModalServerInfo";
|
||||
import {createServerModal} from "../ui/modal/ModalServerEdit";
|
||||
import {spawnIconSelect} from "../ui/modal/ModalIconSelect";
|
||||
|
|
|
@ -16,7 +16,7 @@ import {VideoBroadcastType, VideoConnectionStatus} from "tc-shared/connection/Vi
|
|||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {getVideoDriver} from "tc-shared/video/VideoSource";
|
||||
import {kLocalBroadcastChannels} from "tc-shared/ui/frames/video/Definitions";
|
||||
import {getRecorderBackend, IDevice} from "tc-shared/audio/recorder";
|
||||
import {getRecorderBackend, InputDevice} from "tc-shared/audio/Recorder";
|
||||
import {defaultRecorder, defaultRecorderEvents} from "tc-shared/voice/RecorderProfile";
|
||||
import {bookmarks} from "tc-shared/Bookmarks";
|
||||
import {connectionHistory} from "tc-shared/connectionlog/History";
|
||||
|
@ -276,7 +276,7 @@ class InfoController {
|
|||
this.events.fire_react("notify_microphone_list", {
|
||||
devices: devices.map(device => {
|
||||
let selected = false;
|
||||
if(selectedDevice === IDevice.DefaultDeviceId && device.deviceId === defaultDevice) {
|
||||
if(selectedDevice === InputDevice.DefaultDeviceId && device.deviceId === defaultDevice) {
|
||||
selected = true;
|
||||
} else if(selectedDevice === device.deviceId) {
|
||||
selected = true;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {createModal} from "../../ui/elements/Modal";
|
||||
import {EventType, key_description, KeyEvent} from "../../PPTListener";
|
||||
import * as ppt from "tc-backend/ppt";
|
||||
import {EventType, getKeyBoard, getKeyDescription, KeyEvent} from "../../PPTListener";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
|
||||
export function spawnKeySelect(callback: (key?: KeyEvent) => void) {
|
||||
|
@ -27,7 +26,7 @@ export function spawnKeySelect(callback: (key?: KeyEvent) => void) {
|
|||
current_key = event;
|
||||
current_key_age = Date.now();
|
||||
|
||||
container_key.text(key_description(event));
|
||||
container_key.text(getKeyDescription(event));
|
||||
button_save.prop("disabled", false);
|
||||
}
|
||||
};
|
||||
|
@ -36,7 +35,7 @@ export function spawnKeySelect(callback: (key?: KeyEvent) => void) {
|
|||
button_save.on('click', () => {
|
||||
if (__build.version !== "web") {
|
||||
/* Because pressing the close button is also a mouse action */
|
||||
if (current_key_age + 1000 > Date.now() && current_key.key_code == "MOUSE1")
|
||||
if (current_key_age + 1000 > Date.now() && current_key.keyCode == "MOUSE1")
|
||||
current_key = last_key;
|
||||
}
|
||||
|
||||
|
@ -45,8 +44,9 @@ export function spawnKeySelect(callback: (key?: KeyEvent) => void) {
|
|||
}).prop("disabled", true);
|
||||
button_cancel.on('click', () => modal.close());
|
||||
|
||||
ppt.register_key_listener(listener);
|
||||
modal.close_listener.push(() => ppt.unregister_key_listener(listener));
|
||||
const keyboard = getKeyBoard();
|
||||
keyboard.registerListener(listener);
|
||||
modal.close_listener.push(() => keyboard.unregisterListener(listener));
|
||||
|
||||
modal.htmlTag.find(".modal-body").addClass("modal-keyselect modal-green");
|
||||
modal.open();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {createErrorModal, createInfoModal, createInputModal, createModal, Modal} from "tc-shared/ui/elements/Modal";
|
||||
import {sliderfy} from "tc-shared/ui/elements/Slider";
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
import * as sound from "tc-shared/sound/Sounds";
|
||||
import {manager, set_master_volume, Sound} from "tc-shared/sound/Sounds";
|
||||
import * as sound from "tc-shared/audio/Sounds";
|
||||
import {manager, set_master_volume, Sound} from "tc-shared/audio/Sounds";
|
||||
import * as profiles from "tc-shared/profiles/ConnectionProfile";
|
||||
import {ConnectionProfile} from "tc-shared/profiles/ConnectionProfile";
|
||||
import {IdentitifyType} from "tc-shared/profiles/Identity";
|
||||
|
@ -18,8 +18,7 @@ import * as i18nc from "tc-shared/i18n/country";
|
|||
import * as forum from "tc-shared/profiles/identities/teaspeak-forum";
|
||||
import {formatMessage, set_icon_size} from "tc-shared/ui/frames/chat";
|
||||
import {spawnTeamSpeakIdentityImport, spawnTeamSpeakIdentityImprove} from "tc-shared/ui/modal/ModalIdentity";
|
||||
import {Device} from "tc-shared/audio/player";
|
||||
import * as aplayer from "tc-backend/audio/player";
|
||||
import {getAudioBackend, OutputDevice} from "tc-shared/audio/Player";
|
||||
import {KeyMapSettings} from "tc-shared/ui/modal/settings/Keymap";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
@ -628,8 +627,8 @@ function settings_audio_speaker(container: JQuery, modal: Modal) {
|
|||
const update_devices = () => {
|
||||
container_devices.children().remove();
|
||||
|
||||
const current_selected = aplayer.current_device();
|
||||
const generate_device = (device: Device | undefined) => {
|
||||
const current_selected = getAudioBackend().getCurrentDevice();
|
||||
const generate_device = (device: OutputDevice | undefined) => {
|
||||
const selected = device === current_selected || (typeof (current_selected) !== "undefined" && typeof (device) !== "undefined" && current_selected.device_id == device.device_id);
|
||||
|
||||
const tag = $.spawn("div").addClass("device").toggleClass("selected", selected).append(
|
||||
|
@ -654,7 +653,7 @@ function settings_audio_speaker(container: JQuery, modal: Modal) {
|
|||
_old.removeClass("selected");
|
||||
tag.addClass("selected");
|
||||
|
||||
aplayer.set_device(device ? device.device_id : null).then(() => {
|
||||
getAudioBackend().setCurrentDevice(device?.device_id).then(() => {
|
||||
logDebug(LogCategory.AUDIO, tr("Changed default speaker device"));
|
||||
}).catch((error) => {
|
||||
_old.addClass("selected");
|
||||
|
@ -669,7 +668,7 @@ function settings_audio_speaker(container: JQuery, modal: Modal) {
|
|||
};
|
||||
|
||||
generate_device(undefined).appendTo(container_devices);
|
||||
aplayer.available_devices().then(result => {
|
||||
getAudioBackend().getAvailableDevices().then(result => {
|
||||
contianer_error.text("").hide();
|
||||
result.forEach(e => generate_device(e).appendTo(container_devices));
|
||||
}).catch(error => {
|
||||
|
@ -710,8 +709,7 @@ function settings_audio_speaker(container: JQuery, modal: Modal) {
|
|||
slider.on('change', event => {
|
||||
const volume = parseInt(slider.attr('value'));
|
||||
|
||||
if (aplayer.set_master_volume)
|
||||
aplayer.set_master_volume(volume / 100);
|
||||
getAudioBackend().setMasterVolume(volume / 100);
|
||||
settings.setValue(Settings.KEY_SOUND_MASTER, volume);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class KeyActionEntry extends ReactComponentBase<KeyActionEntryProperties, KeyAct
|
|||
} else if (this.state.state === "loaded") {
|
||||
rightItem = null;
|
||||
if (this.state.assignedKey)
|
||||
rightItem = <div className={cssStyle.key}>{ppt.key_description(this.state.assignedKey)}</div>;
|
||||
rightItem = <div className={cssStyle.key}>{ppt.getKeyDescription(this.state.assignedKey)}</div>;
|
||||
} else {
|
||||
rightItem =
|
||||
<div key={"status-error"} className={this.classList(cssStyle.status, cssStyle.error)}><Translatable
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import * as aplayer from "tc-backend/audio/player";
|
||||
import * as React from "react";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {LevelMeter} from "tc-shared/voice/RecorderBase";
|
||||
import {LogCategory, logTrace, logWarn} from "tc-shared/log";
|
||||
import {defaultRecorder} from "tc-shared/voice/RecorderProfile";
|
||||
import {DeviceListState, getRecorderBackend, IDevice} from "tc-shared/audio/recorder";
|
||||
import {DeviceListState, getRecorderBackend, InputDevice} from "tc-shared/audio/Recorder";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import {getBackend} from "tc-shared/backend";
|
||||
import * as _ from "lodash";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
|
||||
export type MicrophoneSetting =
|
||||
"volume"
|
||||
|
@ -223,9 +223,9 @@ export function initialize_audio_microphone_controller(events: Registry<Micropho
|
|||
{
|
||||
const currentSelectedDevice = (): SelectedMicrophone => {
|
||||
let deviceId = defaultRecorder.getDeviceId();
|
||||
if(deviceId === IDevice.DefaultDeviceId) {
|
||||
if(deviceId === InputDevice.DefaultDeviceId) {
|
||||
return { type: "default" };
|
||||
} else if(deviceId === IDevice.NoDeviceId) {
|
||||
} else if(deviceId === InputDevice.NoDeviceId) {
|
||||
return { type: "none" };
|
||||
} else {
|
||||
return { type: "device", deviceId: deviceId };
|
||||
|
@ -233,7 +233,7 @@ export function initialize_audio_microphone_controller(events: Registry<Micropho
|
|||
};
|
||||
|
||||
events.on("query_devices", event => {
|
||||
if (!aplayer.initialized()) {
|
||||
if (!getAudioBackend().isInitialized()) {
|
||||
events.fire_react("notify_devices", {
|
||||
status: "audio-not-initialized"
|
||||
});
|
||||
|
@ -452,10 +452,8 @@ export function initialize_audio_microphone_controller(events: Registry<Micropho
|
|||
events.fire("query_devices");
|
||||
}));
|
||||
|
||||
if (!aplayer.initialized()) {
|
||||
aplayer.on_ready(() => {
|
||||
events.fire_react("query_devices");
|
||||
});
|
||||
if(!getAudioBackend().isInitialized()) {
|
||||
getAudioBackend().executeWhenInitialized(() => events.fire_react("query_devices"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,12 +11,12 @@ import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
|||
import {Slider} from "tc-shared/ui/react-elements/Slider";
|
||||
import {RadioButton} from "tc-shared/ui/react-elements/RadioButton";
|
||||
import {VadType} from "tc-shared/voice/RecorderProfile";
|
||||
import {key_description, KeyDescriptor} from "tc-shared/PPTListener";
|
||||
import {getKeyDescription, KeyDescriptor} from "tc-shared/PPTListener";
|
||||
import {spawnKeySelect} from "tc-shared/ui/modal/ModalKeySelect";
|
||||
import {Checkbox} from "tc-shared/ui/react-elements/Checkbox";
|
||||
import {BoxedInputField} from "tc-shared/ui/react-elements/InputField";
|
||||
import {IDevice} from "tc-shared/audio/recorder";
|
||||
import {HighlightContainer, HighlightRegion, HighlightText} from "./Heighlight";
|
||||
import {InputDevice} from "tc-shared/audio/Recorder";
|
||||
|
||||
const cssStyle = require("./Microphone.scss");
|
||||
|
||||
|
@ -128,7 +128,7 @@ const Microphone = (props: { events: Registry<MicrophoneSettingsEvents>, device:
|
|||
<div className={cssStyle.name}>{props.device.name}</div>
|
||||
</div>
|
||||
<div className={cssStyle.containerActivity}>
|
||||
{props.device.id === IDevice.NoDeviceId ? undefined :
|
||||
{props.device.id === InputDevice.NoDeviceId ? undefined :
|
||||
<ActivityBar key={"a"} events={props.events} deviceId={props.device.id}/>
|
||||
}
|
||||
</div>
|
||||
|
@ -401,7 +401,7 @@ const PPTKeyButton = React.memo((props: { events: Registry<MicrophoneSettingsEve
|
|||
props.events.fire("action_set_setting", {setting: "ppt-key", value: key});
|
||||
});
|
||||
}}
|
||||
>{key_description(key)}</Button>;
|
||||
>{getKeyDescription(key)}</Button>;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -5,8 +5,7 @@ import {CommandResult} from "../../../connection/ServerConnectionDeclaration";
|
|||
import PermissionType from "../../../permission/PermissionType";
|
||||
import {LogCategory, logError, logTrace} from "../../../log";
|
||||
import {Entry, MenuEntry, MenuEntryType, spawn_context_menu} from "../../../ui/elements/ContextMenu";
|
||||
import * as ppt from "tc-backend/ppt";
|
||||
import {SpecialKey} from "../../../PPTListener";
|
||||
import {getKeyBoard, SpecialKey} from "../../../PPTListener";
|
||||
import {spawnYesNo} from "../../../ui/modal/ModalYesNo";
|
||||
import {tr, tra, traj} from "../../../i18n/localize";
|
||||
import {
|
||||
|
@ -427,7 +426,7 @@ export function initializeRemoteFileBrowserController(connection: ConnectionHand
|
|||
icon_class: "client-file_refresh"
|
||||
});
|
||||
} else {
|
||||
const forceDelete = ppt.key_pressed(SpecialKey.SHIFT);
|
||||
const forceDelete = getKeyBoard().isKeyPressed(SpecialKey.SHIFT);
|
||||
if (selection.length === 0) {
|
||||
entries.push({
|
||||
type: MenuEntryType.ENTRY,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import {EventHandler, ReactEventHandler, Registry} from "tc-shared/events";
|
||||
import {useContext, useEffect, useRef, useState} from "react";
|
||||
import {FileType} from "tc-shared/file/FileManager";
|
||||
import * as ppt from "tc-backend/ppt";
|
||||
import {SpecialKey} from "tc-shared/PPTListener";
|
||||
import {getKeyBoard, SpecialKey} from "tc-shared/PPTListener";
|
||||
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
||||
import {tra} from "tc-shared/i18n/localize";
|
||||
import {network} from "tc-shared/ui/frames/chat";
|
||||
|
@ -422,7 +421,7 @@ const FileName = (props: { path: string, file: ListedFileInfo }) => {
|
|||
if (props.file.virtual || props.file.mode === "creating" || props.file.mode === "uploading")
|
||||
return;
|
||||
|
||||
if (!ppt.key_pressed(SpecialKey.SHIFT))
|
||||
if (!getKeyBoard().isKeyPressed(SpecialKey.SHIFT))
|
||||
return;
|
||||
|
||||
event.stopPropagation();
|
||||
|
@ -656,7 +655,7 @@ const FileListEntry = (props: { row: TableRow<ListedFileInfo>, columns: TableCol
|
|||
|
||||
onClick={() => props.events.fire("action_select_files", {
|
||||
files: [{name: file.name, type: file.type}],
|
||||
mode: ppt.key_pressed(SpecialKey.SHIFT) ? "toggle" : "exclusive"
|
||||
mode: getKeyBoard().isKeyPressed(SpecialKey.SHIFT) ? "toggle" : "exclusive"
|
||||
})}
|
||||
onContextMenu={e => {
|
||||
if (!selected) {
|
||||
|
@ -664,7 +663,7 @@ const FileListEntry = (props: { row: TableRow<ListedFileInfo>, columns: TableCol
|
|||
/* explicitly clicked on one file */
|
||||
props.events.fire("action_select_files", {
|
||||
files: [{name: file.name, type: file.type}],
|
||||
mode: ppt.key_pressed(SpecialKey.SHIFT) ? "toggle" : "exclusive"
|
||||
mode: getKeyBoard().isKeyPressed(SpecialKey.SHIFT) ? "toggle" : "exclusive"
|
||||
});
|
||||
} else {
|
||||
props.events.fire("action_select_files", {files: [], mode: "exclusive"});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {IDevice} from "../audio/recorder";
|
||||
import {InputDevice} from "../audio/Recorder";
|
||||
import {Registry} from "../events";
|
||||
import {Filter, FilterType, FilterTypeClass} from "../voice/Filter";
|
||||
|
||||
|
@ -119,7 +119,7 @@ export interface AbstractInput {
|
|||
}
|
||||
|
||||
export interface LevelMeter {
|
||||
getDevice() : IDevice;
|
||||
getDevice() : InputDevice;
|
||||
|
||||
setObserver(callback: (value: number) => any);
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import * as log from "../log";
|
||||
import {LogCategory, logDebug, logError, logWarn} from "../log";
|
||||
import {AbstractInput, FilterMode} from "../voice/RecorderBase";
|
||||
import {KeyDescriptor, KeyHook} from "../PPTListener";
|
||||
import {getKeyBoard, KeyDescriptor, KeyHook} from "../PPTListener";
|
||||
import {Settings, settings} from "../settings";
|
||||
import {ConnectionHandler} from "../ConnectionHandler";
|
||||
import * as aplayer from "tc-backend/audio/player";
|
||||
import * as ppt from "tc-backend/ppt";
|
||||
import {getRecorderBackend, IDevice} from "../audio/recorder";
|
||||
import {getRecorderBackend, InputDevice} from "../audio/Recorder";
|
||||
import {FilterType, StateFilter, ThresholdFilter} from "../voice/Filter";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
|
||||
export type VadType = "threshold" | "push_to_talk" | "active";
|
||||
export interface RecorderProfileConfig {
|
||||
|
@ -85,7 +83,7 @@ export class RecorderProfile {
|
|||
this.volatile = typeof(volatile) === "boolean" ? volatile : false;
|
||||
|
||||
this.pptHook = {
|
||||
callback_release: () => {
|
||||
callbackRelease: () => {
|
||||
if(this.pptTimeout)
|
||||
clearTimeout(this.pptTimeout);
|
||||
|
||||
|
@ -94,14 +92,12 @@ export class RecorderProfile {
|
|||
}, Math.max(this.config.vad_push_to_talk.delay, 0));
|
||||
},
|
||||
|
||||
callback_press: () => {
|
||||
callbackPress: () => {
|
||||
if(this.pptTimeout)
|
||||
clearTimeout(this.pptTimeout);
|
||||
|
||||
this.registeredFilter["ppt-gate"]?.setState(false);
|
||||
},
|
||||
|
||||
cancel: false
|
||||
} as KeyHook;
|
||||
this.pptHookRegistered = false;
|
||||
}
|
||||
|
@ -125,7 +121,7 @@ export class RecorderProfile {
|
|||
/* default values */
|
||||
this.config = {
|
||||
version: 1,
|
||||
device_id: IDevice.DefaultDeviceId,
|
||||
device_id: InputDevice.DefaultDeviceId,
|
||||
volume: 100,
|
||||
|
||||
vad_threshold: {
|
||||
|
@ -145,7 +141,7 @@ export class RecorderProfile {
|
|||
Object.assign(this.config, config || {});
|
||||
}
|
||||
|
||||
aplayer.on_ready(async () => {
|
||||
getAudioBackend().executeWhenInitialized(async () => {
|
||||
await getRecorderBackend().getDeviceList().awaitInitialized();
|
||||
|
||||
await this.initializeInput();
|
||||
|
@ -185,7 +181,7 @@ export class RecorderProfile {
|
|||
if(this.config.device_id) {
|
||||
await this.input.setDeviceId(this.config.device_id);
|
||||
} else {
|
||||
await this.input.setDeviceId(IDevice.DefaultDeviceId);
|
||||
await this.input.setDeviceId(InputDevice.DefaultDeviceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,15 +197,12 @@ export class RecorderProfile {
|
|||
}
|
||||
|
||||
if(this.pptHookRegistered) {
|
||||
ppt.unregister_key_hook(this.pptHook);
|
||||
getKeyBoard().unregisterHook(this.pptHook);
|
||||
this.pptHookRegistered = false;
|
||||
}
|
||||
|
||||
for(const key of ["key_alt", "key_ctrl", "key_shift", "key_windows", "key_code"]) {
|
||||
this.pptHook[key] = this.config.vad_push_to_talk[key];
|
||||
}
|
||||
|
||||
ppt.register_key_hook(this.pptHook);
|
||||
Object.assign(this.pptHook, this.getPushToTalkKey());
|
||||
getKeyBoard().registerHook(this.pptHook);
|
||||
this.pptHookRegistered = true;
|
||||
|
||||
this.registeredFilter["ppt-gate"]?.setState(true);
|
||||
|
@ -227,7 +220,7 @@ export class RecorderProfile {
|
|||
this.registeredFilter["ppt-gate"].setEnabled(false);
|
||||
|
||||
if(this.pptHookRegistered) {
|
||||
ppt.unregister_key_hook(this.pptHook);
|
||||
getKeyBoard().unregisterHook(this.pptHook);
|
||||
this.pptHookRegistered = false;
|
||||
}
|
||||
|
||||
|
@ -251,10 +244,8 @@ export class RecorderProfile {
|
|||
filter.setEnabled(true);
|
||||
filter.setState(true); /* by default set filtered */
|
||||
|
||||
for(const key of ["key_alt", "key_ctrl", "key_shift", "key_windows", "key_code"])
|
||||
this.pptHook[key] = this.config.vad_push_to_talk[key];
|
||||
|
||||
ppt.register_key_hook(this.pptHook);
|
||||
Object.assign(this.pptHook, this.getPushToTalkKey());
|
||||
getKeyBoard().registerHook(this.pptHook);
|
||||
this.pptHookRegistered = true;
|
||||
} else if(this.config.vad_type === "active") {
|
||||
/* we don't have to initialize any filters */
|
||||
|
@ -311,10 +302,27 @@ export class RecorderProfile {
|
|||
this.save();
|
||||
}
|
||||
|
||||
getPushToTalkKey() : KeyDescriptor { return this.config.vad_push_to_talk; }
|
||||
getPushToTalkKey() : KeyDescriptor {
|
||||
return {
|
||||
keyCode: this.config.vad_push_to_talk.key_code,
|
||||
|
||||
keyAlt: this.config.vad_push_to_talk.key_alt,
|
||||
keyCtrl: this.config.vad_push_to_talk.key_ctrl,
|
||||
keyShift: this.config.vad_push_to_talk.key_shift,
|
||||
keyWindows: this.config.vad_push_to_talk.key_windows,
|
||||
}
|
||||
}
|
||||
|
||||
setPushToTalkKey(key: KeyDescriptor) {
|
||||
for(const _key of ["key_alt", "key_ctrl", "key_shift", "key_windows", "key_code"])
|
||||
this.config.vad_push_to_talk[_key] = key[_key];
|
||||
this.config.vad_push_to_talk = {
|
||||
delay: this.config.vad_push_to_talk.delay,
|
||||
key_code: key.keyCode,
|
||||
|
||||
key_alt: key.keyAlt,
|
||||
key_ctrl: key.keyCtrl,
|
||||
key_shift: key.keyShift,
|
||||
key_windows: key.keyWindows
|
||||
};
|
||||
|
||||
this.reinitializePPTHook();
|
||||
this.save();
|
||||
|
@ -329,8 +337,8 @@ export class RecorderProfile {
|
|||
this.save();
|
||||
}
|
||||
|
||||
getDeviceId() : string | typeof IDevice.DefaultDeviceId | typeof IDevice.NoDeviceId { return this.config.device_id; }
|
||||
setDevice(device: IDevice | typeof IDevice.DefaultDeviceId | typeof IDevice.NoDeviceId) : Promise<void> {
|
||||
getDeviceId() : string | typeof InputDevice.DefaultDeviceId | typeof InputDevice.NoDeviceId { return this.config.device_id; }
|
||||
setDevice(device: InputDevice | typeof InputDevice.DefaultDeviceId | typeof InputDevice.NoDeviceId) : Promise<void> {
|
||||
let deviceId;
|
||||
if(typeof device === "object") {
|
||||
deviceId = device.deviceId;
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"baseUrl": "../../",
|
||||
"paths": {
|
||||
"tc-shared/*": ["shared/js/*"],
|
||||
"tc-backend/*": ["shared/backend.d/*"],
|
||||
"tc-loader": ["loader/exports/loader.d.ts"],
|
||||
"svg-sprites/*": ["shared/svg-sprites/*"],
|
||||
"vendor/xbbcode/*": ["vendor/xbbcode/src/*"],
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"tc-shared/*": ["shared/js/*"],
|
||||
"tc-backend/audio-lib/*": ["web/audio-lib/pkg/*"], /* specific web part */
|
||||
"tc-backend/web/*": ["web/app/*"], /* specific web part */
|
||||
"tc-backend/*": ["shared/backend.d/*"],
|
||||
"tc-loader": ["loader/exports/loader.d.ts"],
|
||||
"tc-events": ["vendor/TeaEventBus/src/index.ts"],
|
||||
"tc-services": ["vendor/TeaClientServices/src/index.ts"],
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
TransferSourceType,
|
||||
TransferTargetType
|
||||
} from "tc-shared/file/Transfer";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory, logError} from "tc-shared/log";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import {AbstractKeyBoard, EventType, KeyEvent} from "tc-shared/PPTListener";
|
||||
|
||||
export class WebKeyBoard extends AbstractKeyBoard {
|
||||
private readonly listenerBlur;
|
||||
private readonly listenerKeyPress;
|
||||
private readonly listenerKeyDown;
|
||||
private readonly listenerKeyUp;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.listenerBlur = () => this.handleBlurEvent();
|
||||
this.listenerKeyPress = event => this.handleNativeKeyEvent(EventType.KEY_TYPED, event);
|
||||
this.listenerKeyDown = event => this.handleNativeKeyEvent(EventType.KEY_PRESS, event);
|
||||
this.listenerKeyUp = event => this.handleNativeKeyEvent(EventType.KEY_RELEASE, event);
|
||||
|
||||
window.addEventListener("blur", () => this.handleBlurEvent());
|
||||
document.addEventListener('keypress', this.listenerKeyPress);
|
||||
document.addEventListener('keydown', this.listenerKeyDown);
|
||||
document.addEventListener('keyup', this.listenerKeyUp);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
window.removeEventListener("blur", () => this.handleBlurEvent());
|
||||
document.removeEventListener('keypress', this.listenerKeyPress);
|
||||
document.removeEventListener('keydown', this.listenerKeyDown);
|
||||
document.removeEventListener('keyup', this.listenerKeyUp);
|
||||
}
|
||||
|
||||
private handleNativeKeyEvent(type: EventType, nativeEvent: KeyboardEvent) {
|
||||
const event: KeyEvent = {
|
||||
type: type,
|
||||
|
||||
key: nativeEvent.key,
|
||||
keyCode: nativeEvent.code,
|
||||
|
||||
keyCtrl: nativeEvent.ctrlKey,
|
||||
keyShift: nativeEvent.shiftKey,
|
||||
keyAlt: nativeEvent.altKey,
|
||||
keyWindows: nativeEvent.metaKey,
|
||||
};
|
||||
|
||||
this.fireKeyEvent(event);
|
||||
}
|
||||
|
||||
private handleBlurEvent() {
|
||||
this.resetKeyboardState();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
import {AudioBackend, AudioBackendEvents, OutputDevice} from "tc-shared/audio/Player";
|
||||
import {LogCategory, logDebug, logError, logInfo, logWarn} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {Registry} from "tc-events";
|
||||
|
||||
const kWebDevice: OutputDevice = {
|
||||
device_id: "default",
|
||||
name: "default playback",
|
||||
driver: 'Web Audio'
|
||||
};
|
||||
|
||||
export class WebAudioBackend implements AudioBackend {
|
||||
private readonly events: Registry<AudioBackendEvents>;
|
||||
private readonly audioContext: AudioContext;
|
||||
private state: "initializing" | "running" | "closed";
|
||||
private masterVolume: number;
|
||||
|
||||
private gestureListener: () => void;
|
||||
|
||||
constructor() {
|
||||
this.events = new Registry<AudioBackendEvents>();
|
||||
this.state = "initializing";
|
||||
this.masterVolume = 1;
|
||||
|
||||
this.audioContext = new (window.webkitAudioContext || window.AudioContext)();
|
||||
this.audioContext.onstatechange = () => this.handleAudioContextStateChanged(false);
|
||||
this.handleAudioContextStateChanged(true);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.state = "closed";
|
||||
|
||||
document.removeEventListener("click", this.gestureListener);
|
||||
this.gestureListener = undefined;
|
||||
|
||||
this.audioContext.close().catch(error => {
|
||||
logWarn(LogCategory.AUDIO, tr("Failed to close AudioContext: %o"), error);
|
||||
});
|
||||
}
|
||||
|
||||
executeWhenInitialized(callback: () => void) {
|
||||
if(this.state === "running") {
|
||||
callback();
|
||||
} else {
|
||||
this.events.one("notify_initialized", callback);
|
||||
}
|
||||
}
|
||||
|
||||
isInitialized(): boolean {
|
||||
return this.state === "running";
|
||||
}
|
||||
|
||||
getAudioContext(): AudioContext | undefined {
|
||||
return this.audioContext;
|
||||
}
|
||||
|
||||
async getAvailableDevices(): Promise<OutputDevice[]> {
|
||||
return [ kWebDevice ];
|
||||
}
|
||||
|
||||
getDefaultDeviceId(): string {
|
||||
return kWebDevice.device_id;
|
||||
}
|
||||
|
||||
getMasterVolume(): number {
|
||||
return this.masterVolume;
|
||||
}
|
||||
|
||||
setMasterVolume(volume: number) {
|
||||
if(this.masterVolume === volume) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldVolume = this.masterVolume;
|
||||
this.masterVolume = volume;
|
||||
this.events.fire("notify_volume_changed", {
|
||||
oldVolume: oldVolume,
|
||||
newVolume: volume
|
||||
});
|
||||
}
|
||||
|
||||
isDeviceRefreshAvailable(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
refreshDevices(): Promise<void> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
private handleAudioContextStateChanged(initialState: boolean) {
|
||||
switch (this.audioContext.state) {
|
||||
case "suspended":
|
||||
if(initialState) {
|
||||
logDebug(LogCategory.AUDIO, tr("Created new AudioContext but user hasn't yet allowed audio playback. Awaiting his gesture."));
|
||||
this.awaitGesture();
|
||||
return;
|
||||
} else {
|
||||
logWarn(LogCategory.AUDIO, tr("AudioContext state changed to 'suspended'. Trying to resume it."));
|
||||
this.tryResume();
|
||||
}
|
||||
break;
|
||||
|
||||
case "closed":
|
||||
if(this.state === "closed") {
|
||||
return;
|
||||
}
|
||||
|
||||
logError(LogCategory.AUDIO, tr("AudioContext state changed to 'closed'. No audio will be payed."));
|
||||
return;
|
||||
|
||||
case "running":
|
||||
logDebug(LogCategory.AUDIO, tr("Successfully initialized the AudioContext."));
|
||||
this.state = "running";
|
||||
this.events.fire("notify_initialized");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private tryResume() {
|
||||
this.audioContext.resume().then(() => {
|
||||
logInfo(LogCategory.AUDIO, tr("Successfully resumed AudioContext."));
|
||||
}).catch(error => {
|
||||
logError(LogCategory.AUDIO, tr("Failed to resume AudioContext: %o"), error);
|
||||
});
|
||||
}
|
||||
|
||||
private awaitGesture() {
|
||||
if(this.gestureListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.gestureListener = () => {
|
||||
document.removeEventListener("click", this.gestureListener);
|
||||
this.gestureListener = undefined;
|
||||
this.tryResume();
|
||||
};
|
||||
|
||||
document.addEventListener("click", this.gestureListener);
|
||||
}
|
||||
|
||||
async setCurrentDevice(targetId: string | undefined): Promise<void> {
|
||||
/* TODO: Mute on "no device"? */
|
||||
}
|
||||
|
||||
getCurrentDevice(): OutputDevice {
|
||||
return kWebDevice;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import {AudioRecorderBacked, DeviceList, IDevice,} from "tc-shared/audio/recorder";
|
||||
import {AudioRecorderBacked, DeviceList, InputDevice,} from "tc-shared/audio/Recorder";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {
|
||||
AbstractInput,
|
||||
|
@ -12,12 +12,12 @@ import {
|
|||
NodeInputConsumer
|
||||
} from "tc-shared/voice/RecorderBase";
|
||||
import {LogCategory, logDebug, logWarn} from "tc-shared/log";
|
||||
import * as aplayer from "./player";
|
||||
import {JAbstractFilter, JStateFilter, JThresholdFilter} from "./RecorderFilter";
|
||||
import {Filter, FilterType, FilterTypeClass} from "tc-shared/voice/Filter";
|
||||
import {inputDeviceList} from "tc-backend/web/audio/RecorderDeviceList";
|
||||
import {inputDeviceList} from "./RecorderDeviceList";
|
||||
import {requestMediaStream, stopMediaStream} from "tc-shared/media/Stream";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
|
||||
declare global {
|
||||
interface MediaStream {
|
||||
|
@ -25,7 +25,7 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
export interface WebIDevice extends IDevice {
|
||||
export interface WebIDevice extends InputDevice {
|
||||
groupId: string;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ export class WebAudioRecorder implements AudioRecorderBacked {
|
|||
return new JavascriptInput();
|
||||
}
|
||||
|
||||
async createLevelMeter(device: IDevice): Promise<LevelMeter> {
|
||||
async createLevelMeter(device: InputDevice): Promise<LevelMeter> {
|
||||
const meter = new JavascriptLevelMeter(device as any);
|
||||
await meter.initialize();
|
||||
return meter;
|
||||
|
@ -81,14 +81,14 @@ class JavascriptInput implements AbstractInput {
|
|||
constructor() {
|
||||
this.events = new Registry<InputEvents>();
|
||||
|
||||
aplayer.on_ready(() => this.handleAudioInitialized());
|
||||
getAudioBackend().executeWhenInitialized(() => this.handleAudioInitialized());
|
||||
this.audioScriptProcessorCallback = this.handleAudio.bind(this);
|
||||
}
|
||||
|
||||
destroy() { }
|
||||
|
||||
private handleAudioInitialized() {
|
||||
this.audioContext = aplayer.context();
|
||||
this.audioContext = getAudioBackend().getAudioContext();
|
||||
this.audioNodeMute = this.audioContext.createGain();
|
||||
this.audioNodeMute.gain.value = 0;
|
||||
this.audioNodeMute.connect(this.audioContext.destination);
|
||||
|
@ -179,7 +179,7 @@ class JavascriptInput implements AbstractInput {
|
|||
|
||||
private async doStart() : Promise<InputStartError | true> {
|
||||
try {
|
||||
if(!aplayer.initialized() || !this.audioContext) {
|
||||
if(!getAudioBackend().isInitialized() || !this.audioContext) {
|
||||
return InputStartError.ESYSTEMUNINITIALIZED;
|
||||
}
|
||||
|
||||
|
@ -189,9 +189,9 @@ class JavascriptInput implements AbstractInput {
|
|||
this.setState(InputState.INITIALIZING);
|
||||
|
||||
let deviceId;
|
||||
if(this.deviceId === IDevice.NoDeviceId) {
|
||||
if(this.deviceId === InputDevice.NoDeviceId) {
|
||||
throw tr("no device selected");
|
||||
} else if(this.deviceId === IDevice.DefaultDeviceId) {
|
||||
} else if(this.deviceId === InputDevice.DefaultDeviceId) {
|
||||
deviceId = undefined;
|
||||
} else {
|
||||
deviceId = this.deviceId;
|
||||
|
@ -511,7 +511,7 @@ class JavascriptLevelMeter implements LevelMeter {
|
|||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(reject, 5000);
|
||||
aplayer.on_ready(() => {
|
||||
getAudioBackend().executeWhenInitialized(() => {
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
});
|
||||
|
@ -519,7 +519,7 @@ class JavascriptLevelMeter implements LevelMeter {
|
|||
} catch(error) {
|
||||
throw tr("audio context timeout");
|
||||
}
|
||||
this._context = aplayer.context();
|
||||
this._context = getAudioBackend().getAudioContext();
|
||||
if(!this._context) throw tr("invalid context");
|
||||
|
||||
this._gain_node = this._context.createGain();
|
||||
|
@ -591,7 +591,7 @@ class JavascriptLevelMeter implements LevelMeter {
|
|||
}
|
||||
}
|
||||
|
||||
getDevice(): IDevice {
|
||||
getDevice(): InputDevice {
|
||||
return this._device;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,12 @@ import {
|
|||
AbstractDeviceList,
|
||||
DeviceListEvents,
|
||||
DeviceListState,
|
||||
IDevice,
|
||||
InputDevice,
|
||||
PermissionState
|
||||
} from "tc-shared/audio/recorder";
|
||||
import * as log from "tc-shared/log";
|
||||
} from "tc-shared/audio/Recorder";
|
||||
import {LogCategory, logDebug, logError} from "tc-shared/log";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {WebIDevice} from "tc-backend/web/audio/Recorder";
|
||||
import {WebIDevice} from "./Recorder";
|
||||
import * as loader from "tc-loader";
|
||||
import {queryMediaPermissions} from "tc-shared/media/Stream";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
|
@ -49,7 +48,7 @@ class WebInputDeviceList extends AbstractDeviceList {
|
|||
return "default";
|
||||
}
|
||||
|
||||
getDevices(): IDevice[] {
|
||||
getDevices(): InputDevice[] {
|
||||
return this.devices;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {LogCategory, logError, logWarn} from "tc-shared/log";
|
||||
import {SoundFile} from "tc-shared/sound/Sounds";
|
||||
import * as aplayer from "./player";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
import {SoundBackend, SoundFile} from "tc-shared/audio/Sounds";
|
||||
|
||||
interface SoundEntry {
|
||||
cached?: AudioBuffer;
|
||||
|
@ -13,7 +13,7 @@ const error_already_handled = "---- error handled ---";
|
|||
const file_cache: {[key: string]: Promise<SoundEntry> & { timestamp: number }} = {};
|
||||
let warned = false;
|
||||
|
||||
function get_song_entry(file: SoundFile) : Promise<SoundEntry> {
|
||||
function getSongEntry(file: SoundFile) : Promise<SoundEntry> {
|
||||
if(typeof file_cache[file.path] === "object") {
|
||||
return new Promise<SoundEntry>((resolve, reject) => {
|
||||
if(file_cache[file.path].timestamp + 60 * 1000 > Date.now()) {
|
||||
|
@ -26,12 +26,12 @@ function get_song_entry(file: SoundFile) : Promise<SoundEntry> {
|
|||
if(file_cache[file.path].timestamp + 60 * 1000 > original_timestamp)
|
||||
return Promise.reject(error);
|
||||
delete file_cache[file.path];
|
||||
return get_song_entry(file);
|
||||
return getSongEntry(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const context = aplayer.context();
|
||||
const context = getAudioBackend().getAudioContext();
|
||||
if(!context) throw tr("audio context not initialized");
|
||||
|
||||
return (file_cache[file.path] = Object.assign((async () => {
|
||||
|
@ -78,8 +78,8 @@ function get_song_entry(file: SoundFile) : Promise<SoundEntry> {
|
|||
})(), { timestamp: Date.now() }));
|
||||
}
|
||||
|
||||
export async function play_sound(file: SoundFile) : Promise<void> {
|
||||
const entry = get_song_entry(file);
|
||||
async function replaySound(file: SoundFile) : Promise<void> {
|
||||
const entry = getSongEntry(file);
|
||||
if(!entry) {
|
||||
logWarn(LogCategory.AUDIO, tr("Failed to replay sound %s because it could not be resolved."), file.path);
|
||||
return;
|
||||
|
@ -89,7 +89,7 @@ export async function play_sound(file: SoundFile) : Promise<void> {
|
|||
const sound = await entry;
|
||||
|
||||
if(sound.cached) {
|
||||
const context = aplayer.context();
|
||||
const context = getAudioBackend().getAudioContext();
|
||||
if(!context) throw tr("audio context not initialized (this error should never show up!)");
|
||||
|
||||
const player = context.createBufferSource();
|
||||
|
@ -126,4 +126,11 @@ export async function play_sound(file: SoundFile) : Promise<void> {
|
|||
logWarn(LogCategory.AUDIO, tr("Failed to replay sound %s: %o"), file.path, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export class WebSoundBackend implements SoundBackend {
|
||||
playSound(sound: SoundFile): Promise<void> {
|
||||
return replaySound(sound);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
import {Device} from "tc-shared/audio/player";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory, logError, logInfo} from "tc-shared/log";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
|
||||
/* lets try without any gestures, maybe the user already clicked the page */
|
||||
const kAvoidAudioContextWarning = false;
|
||||
|
||||
let audioContextRequiredGesture = false;
|
||||
let audioContextInstance: AudioContext;
|
||||
let globalAudioGainInstance: GainNode;
|
||||
|
||||
let audioContextInitializeCallbacks: (() => any)[] = [];
|
||||
let _master_volume: number = 1;
|
||||
let _no_device = false;
|
||||
|
||||
export function initialize() : boolean {
|
||||
context();
|
||||
return true;
|
||||
}
|
||||
|
||||
export function initialized() : boolean {
|
||||
return !!audioContextInstance && audioContextInstance.state === 'running';
|
||||
}
|
||||
|
||||
function fire_initialized() {
|
||||
logInfo(LogCategory.AUDIO, tr("Fire audio player initialized for %d listeners"), audioContextInitializeCallbacks.length);
|
||||
while(audioContextInitializeCallbacks.length > 0)
|
||||
audioContextInitializeCallbacks.pop_front()();
|
||||
}
|
||||
|
||||
function createNewContext() {
|
||||
audioContextInstance = new (window.webkitAudioContext || window.AudioContext)();
|
||||
audioContextInstance.onstatechange = () => {
|
||||
if(audioContextInstance.state === "running")
|
||||
fire_initialized();
|
||||
};
|
||||
|
||||
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 {
|
||||
if(audioContextInstance || kAvoidAudioContextWarning)
|
||||
return audioContextInstance;
|
||||
|
||||
if(!audioContextInstance)
|
||||
createNewContext();
|
||||
|
||||
return audioContextInstance;
|
||||
}
|
||||
|
||||
export function get_master_volume() : number {
|
||||
return _master_volume;
|
||||
}
|
||||
export function set_master_volume(volume: number) {
|
||||
_master_volume = volume;
|
||||
if(globalAudioGainInstance)
|
||||
globalAudioGainInstance.gain.value = _no_device ? 0 : _master_volume;
|
||||
}
|
||||
|
||||
export function destination() : AudioNode {
|
||||
const ctx = context();
|
||||
if(!ctx) throw tr("Audio player isn't initialized yet!");
|
||||
|
||||
return globalAudioGainInstance;
|
||||
}
|
||||
|
||||
export function on_ready(cb: () => any) {
|
||||
if(initialized())
|
||||
cb();
|
||||
else
|
||||
audioContextInitializeCallbacks.push(cb);
|
||||
}
|
||||
|
||||
export const WEB_DEVICE: Device = {
|
||||
device_id: "default",
|
||||
name: "default playback",
|
||||
driver: 'Web Audio'
|
||||
};
|
||||
|
||||
export function available_devices() : Promise<Device[]> {
|
||||
return Promise.resolve([WEB_DEVICE])
|
||||
}
|
||||
|
||||
export function set_device(device_id: string) : Promise<void> {
|
||||
_no_device = !device_id;
|
||||
globalAudioGainInstance.gain.value = _no_device ? 0 : _master_volume;
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function current_device() : Device {
|
||||
return WEB_DEVICE;
|
||||
}
|
||||
|
||||
export function initializeFromGesture() {
|
||||
if(audioContextInstance) {
|
||||
if(audioContextInstance.state !== "running") {
|
||||
audioContextInstance.resume().catch(error => {
|
||||
logError(LogCategory.AUDIO, tr("Failed to initialize audio context instance from gesture: %o"), error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
createNewContext();
|
||||
}
|
||||
}
|
||||
|
||||
export function globalAudioContext() : AudioContext {
|
||||
return context();
|
||||
}
|
|
@ -10,8 +10,9 @@ function unescapeCommandValue(value: string) : string {
|
|||
|
||||
while (true) {
|
||||
index = value.indexOf('\\', lastIndex);
|
||||
if(index === -1 || index >= value.length + 1)
|
||||
if(index === -1 || index >= value.length + 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
let replace;
|
||||
switch (value.charAt(index + 1)) {
|
||||
|
@ -58,8 +59,9 @@ export function parseCommand(command: string): ParsedCommand {
|
|||
const parts = command.split("|").map(element => element.split(" ").map(e => e.trim()).filter(e => !!e));
|
||||
|
||||
let cmd;
|
||||
if(parts[0][0].indexOf("=") === -1)
|
||||
if(parts[0][0].indexOf("=") === -1) {
|
||||
cmd = parts[0].pop_front();
|
||||
}
|
||||
|
||||
let switches = [];
|
||||
let payloads = [];
|
||||
|
@ -72,10 +74,11 @@ export function parseCommand(command: string): ParsedCommand {
|
|||
}
|
||||
|
||||
const separator = keyValue.indexOf('=');
|
||||
if(separator === -1)
|
||||
if(separator === -1) {
|
||||
payload[keyValue] = "";
|
||||
else
|
||||
} else {
|
||||
payload[keyValue.substring(0, separator)] = unescapeCommandValue(keyValue.substring(separator + 1));
|
||||
}
|
||||
}
|
||||
|
||||
payloads.push(payload)
|
||||
|
@ -95,13 +98,15 @@ export function buildCommand(data: any | any[], switches?: string[], command?: s
|
|||
result += " |";
|
||||
for(const key of Object.keys(payload)) {
|
||||
result += " " + key;
|
||||
if(payload[key] !== undefined && payload[key] !== null)
|
||||
if(payload[key] !== undefined && payload[key] !== null) {
|
||||
result += " " + key + "=" + escapeCommandValue(payload[key].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(switches?.length)
|
||||
if(switches?.length) {
|
||||
result += " " + switches.map(e => "-" + e).join(" ");
|
||||
}
|
||||
|
||||
return command ? command + result.substring(2) : result.substring(3);
|
||||
}
|
|
@ -13,11 +13,11 @@ import * as log from "tc-shared/log";
|
|||
import {LogCategory, logDebug, logError, logInfo, logTrace, logWarn} from "tc-shared/log";
|
||||
import {Regex} from "tc-shared/ui/modal/ModalConnect";
|
||||
import {AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler";
|
||||
import {WrappedWebSocket} from "tc-backend/web/connection/WrappedWebSocket";
|
||||
import {WrappedWebSocket} from "./WrappedWebSocket";
|
||||
import {AbstractVoiceConnection} from "tc-shared/connection/VoiceConnection";
|
||||
import {parseCommand} from "tc-backend/web/connection/CommandParser";
|
||||
import {parseCommand} from "./CommandParser";
|
||||
import {ServerAddress} from "tc-shared/tree/Server";
|
||||
import {RtpVoiceConnection} from "tc-backend/web/voice/Connection";
|
||||
import {RtpVoiceConnection} from "../voice/Connection";
|
||||
import {VideoConnection} from "tc-shared/connection/VideoConnection";
|
||||
import {ServerFeature} from "tc-shared/connection/ServerFeatures";
|
||||
import {RTCConnection} from "tc-shared/connection/rtc/Connection";
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {tr} from "tc-shared/i18n/localize";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory, logError, logTrace} from "tc-shared/log";
|
||||
|
||||
export enum RRType {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {setAudioBackend} from "tc-shared/audio/Player";
|
||||
import {WebAudioBackend} from "../audio/Player";
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "audio backend init",
|
||||
function: async () => {
|
||||
setAudioBackend(new WebAudioBackend());
|
||||
},
|
||||
priority: 100
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import {setRecorderBackend} from "tc-shared/audio/recorder";
|
||||
import {setRecorderBackend} from "tc-shared/audio/Recorder";
|
||||
import {WebAudioRecorder} from "../audio/Recorder";
|
||||
|
||||
setRecorderBackend(new WebAudioRecorder());
|
|
@ -1,7 +1,7 @@
|
|||
import {DNSAddress, DNSProvider, DNSResolveOptions, DNSResolveResult, setDNSProvider} from "tc-shared/dns";
|
||||
import {resolveAddressIpV4, resolveTeaSpeakServerAddress} from "tc-backend/web/dns/resolver";
|
||||
import {LogCategory, logError} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {resolveAddressIpV4, resolveTeaSpeakServerAddress} from "../dns/resolver";
|
||||
|
||||
setDNSProvider(new class implements DNSProvider {
|
||||
resolveAddress(address: DNSAddress, options: DNSResolveOptions): Promise<DNSResolveResult> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {setExternalModalControllerFactory} from "tc-shared/ui/react-elements/external-modal";
|
||||
import {ExternalModalController} from "tc-backend/web/ExternalModalFactory";
|
||||
import {ExternalModalController} from "../ExternalModalFactory";
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
priority: 50,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {setKeyBoardBackend} from "tc-shared/PPTListener";
|
||||
import {WebKeyBoard} from "../KeyBoard";
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "audio backend init",
|
||||
function: async () => setKeyBoardBackend(new WebKeyBoard()),
|
||||
priority: 100
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import {setMenuBarDriver} from "tc-shared/ui/frames/menu-bar";
|
||||
import {WebMenuBarDriver} from "tc-backend/web/ui/menu-bar/Controller";
|
||||
import {WebMenuBarDriver} from "../ui/menu-bar/Controller";
|
||||
|
||||
setMenuBarDriver(new WebMenuBarDriver());
|
|
@ -1,9 +1,9 @@
|
|||
import {ServerConnectionFactory, setServerConnectionFactory} from "tc-shared/connection/ConnectionFactory";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
|
||||
import {ServerConnection} from "tc-backend/web/connection/ServerConnection";
|
||||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {ServerConnection} from "../connection/ServerConnection";
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
priority: 50,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {setSoundBackend} from "tc-shared/audio/Sounds";
|
||||
import {WebSoundBackend} from "../audio/Sounds";
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "audio sound init",
|
||||
function: async () => setSoundBackend(new WebSoundBackend()),
|
||||
priority: 100
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import "./AudioBackend";
|
||||
import "./AudioRecorder";
|
||||
import "./Backend";
|
||||
import "./Dns";
|
||||
import "./MenuBar";
|
||||
import "./ServerConnection";
|
||||
import "./Sounds";
|
||||
import "./Video";
|
||||
import "./KeyBoard";
|
|
@ -4,12 +4,7 @@ import "webcrypto-liner";
|
|||
import "./index.scss";
|
||||
import "./FileTransfer";
|
||||
|
||||
import "./hooks/ServerConnection";
|
||||
import "./hooks/ExternalModal";
|
||||
import "./hooks/AudioRecorder";
|
||||
import "./hooks/MenuBar";
|
||||
import "./hooks/Video";
|
||||
import "./hooks/Dns";
|
||||
import "./hooks"
|
||||
|
||||
import "./UnloadHandler";
|
||||
|
||||
|
|
151
web/app/ppt.ts
151
web/app/ppt.ts
|
@ -1,151 +0,0 @@
|
|||
import {EventType, KeyEvent, KeyHook, SpecialKey} from "tc-shared/PPTListener";
|
||||
import {LogCategory, logTrace} from "tc-shared/log";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
|
||||
interface WebKeyEvent extends KeyEvent {
|
||||
canceled: boolean;
|
||||
}
|
||||
|
||||
let key_listener: ((_: KeyEvent) => any)[] = [];
|
||||
|
||||
function listener_key(type: EventType, event: KeyboardEvent) {
|
||||
const key_event = {
|
||||
type: type,
|
||||
|
||||
key: event.key,
|
||||
key_code: event.code,
|
||||
|
||||
key_ctrl: event.ctrlKey,
|
||||
key_shift: event.shiftKey,
|
||||
key_alt: event.altKey,
|
||||
key_windows: event.metaKey,
|
||||
|
||||
canceled: event.defaultPrevented
|
||||
} as WebKeyEvent;
|
||||
//console.debug("Trigger key event %o", key_event);
|
||||
|
||||
for(const listener of key_listener)
|
||||
listener(key_event);
|
||||
|
||||
if(key_event.canceled)
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
const proxy_key_press = event => listener_key(EventType.KEY_PRESS, event);
|
||||
const proxy_key_release = event => listener_key(EventType.KEY_RELEASE, event);
|
||||
const proxy_key_typed = event => listener_key(EventType.KEY_TYPED, event);
|
||||
|
||||
export function initialize() : Promise<void> {
|
||||
document.addEventListener('keypress', proxy_key_typed);
|
||||
document.addEventListener('keydown', proxy_key_press);
|
||||
document.addEventListener('keyup', proxy_key_release);
|
||||
window.addEventListener('blur', listener_blur);
|
||||
|
||||
register_key_listener(listener_hook);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function finalize() {
|
||||
document.removeEventListener("keypress", proxy_key_typed);
|
||||
document.removeEventListener("keydown", proxy_key_press);
|
||||
document.removeEventListener("keyup", proxy_key_release);
|
||||
window.removeEventListener('blur', listener_blur);
|
||||
|
||||
unregister_key_listener(listener_hook);
|
||||
}
|
||||
|
||||
export function register_key_listener(listener: (_: KeyEvent) => any) {
|
||||
key_listener.push(listener);
|
||||
}
|
||||
|
||||
export function unregister_key_listener(listener: (_: KeyEvent) => any) {
|
||||
key_listener.remove(listener);
|
||||
}
|
||||
|
||||
|
||||
let key_hooks: KeyHook[] = [];
|
||||
|
||||
interface CurrentState {
|
||||
keys: {[code: string]:KeyEvent};
|
||||
special: { [key:number]:boolean };
|
||||
}
|
||||
let current_state: CurrentState = {
|
||||
special: []
|
||||
} as any;
|
||||
|
||||
let key_hooks_active: KeyHook[] = [];
|
||||
|
||||
function listener_blur() {
|
||||
current_state.special[SpecialKey.ALT] = false;
|
||||
current_state.special[SpecialKey.CTRL] = false;
|
||||
current_state.special[SpecialKey.SHIFT] = false;
|
||||
current_state.special[SpecialKey.WINDOWS] = false;
|
||||
|
||||
for(const code of Object.keys(current_state))
|
||||
if(code !== "special")
|
||||
delete current_state[code];
|
||||
|
||||
for(const hook of key_hooks_active)
|
||||
hook.callback_release();
|
||||
key_hooks_active = [];
|
||||
}
|
||||
|
||||
function listener_hook(event: KeyEvent) {
|
||||
if(event.type == EventType.KEY_TYPED)
|
||||
return;
|
||||
|
||||
let old_hooks = [...key_hooks_active];
|
||||
let new_hooks = [];
|
||||
|
||||
current_state.special[SpecialKey.ALT] = event.key_alt;
|
||||
current_state.special[SpecialKey.CTRL] = event.key_ctrl;
|
||||
current_state.special[SpecialKey.SHIFT] = event.key_shift;
|
||||
current_state.special[SpecialKey.WINDOWS] = event.key_windows;
|
||||
|
||||
current_state[event.key_code] = undefined;
|
||||
if(event.type == EventType.KEY_PRESS) {
|
||||
current_state[event.key_code] = event;
|
||||
|
||||
for(const hook of key_hooks) {
|
||||
if(hook.key_code !== event.key_code) continue;
|
||||
if(hook.key_alt != event.key_alt) continue;
|
||||
if(hook.key_ctrl != event.key_ctrl) continue;
|
||||
if(hook.key_shift != event.key_shift) continue;
|
||||
if(hook.key_windows != event.key_windows) continue;
|
||||
|
||||
new_hooks.push(hook);
|
||||
if(!old_hooks.remove(hook) && hook.callback_press) {
|
||||
hook.callback_press();
|
||||
logTrace(LogCategory.GENERAL, tr("Trigger key press for %o!"), hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//We have a new situation
|
||||
for(const hook of old_hooks) {
|
||||
//Do not test for meta key states because they could differ in a key release event
|
||||
if(hook.key_code === event.key_code) {
|
||||
if(hook.callback_release) {
|
||||
hook.callback_release();
|
||||
logTrace(LogCategory.GENERAL, tr("Trigger key release for %o!"), hook);
|
||||
}
|
||||
} else {
|
||||
new_hooks.push(hook);
|
||||
}
|
||||
}
|
||||
key_hooks_active = new_hooks;
|
||||
}
|
||||
|
||||
export function register_key_hook(hook: KeyHook) {
|
||||
key_hooks.push(hook);
|
||||
}
|
||||
|
||||
export function unregister_key_hook(hook: KeyHook) {
|
||||
key_hooks.remove(hook);
|
||||
}
|
||||
|
||||
export function key_pressed(code: string | SpecialKey) : boolean {
|
||||
if(typeof(code) === 'string')
|
||||
return typeof current_state[code] !== "undefined";
|
||||
return current_state.special[code];
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import {MenuBarDriver, MenuBarEntry} from "tc-shared/ui/frames/menu-bar";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {MenuBarRenderer} from "tc-backend/web/ui/menu-bar/Renderer";
|
||||
import {MenuBarRenderer} from "./Renderer";
|
||||
|
||||
const cssStyle = require("./Renderer.scss");
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import * as aplayer from "../audio/player";
|
||||
import {
|
||||
AbstractVoiceConnection,
|
||||
VoiceConnectionStatus,
|
||||
|
@ -17,9 +16,10 @@ import {AbstractServerConnection, ConnectionStatistics} from "tc-shared/connecti
|
|||
import {VoicePlayerState} from "tc-shared/voice/VoicePlayer";
|
||||
import {LogCategory, logDebug, logError, logInfo, logTrace, logWarn} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {RtpVoiceClient} from "tc-backend/web/voice/VoiceClient";
|
||||
import {InputConsumerType} from "tc-shared/voice/RecorderBase";
|
||||
import {RtpWhisperSession} from "tc-backend/web/voice/WhisperClient";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
import {RtpVoiceClient} from "./VoiceClient";
|
||||
import {RtpWhisperSession} from "./WhisperClient";
|
||||
|
||||
type CancelableWhisperTarget = WhisperTarget & { canceled: boolean };
|
||||
export class RtpVoiceConnection extends AbstractVoiceConnection {
|
||||
|
@ -86,8 +86,8 @@ export class RtpVoiceConnection extends AbstractVoiceConnection {
|
|||
this.speakerMuted = connection.client.isSpeakerMuted() || connection.client.isSpeakerDisabled();
|
||||
|
||||
this.setConnectionState(VoiceConnectionStatus.Disconnected);
|
||||
aplayer.on_ready(() => {
|
||||
this.localAudioDestination = aplayer.context().createMediaStreamDestination();
|
||||
getAudioBackend().executeWhenInitialized(() => {
|
||||
this.localAudioDestination = getAudioBackend().getAudioContext().createMediaStreamDestination();
|
||||
if(this.currentAudioSourceNode) {
|
||||
this.currentAudioSourceNode.connect(this.localAudioDestination);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import {VoiceClient} from "tc-shared/voice/VoiceClient";
|
||||
import {VoicePlayer} from "./VoicePlayer";
|
||||
import {LogCategory, logTrace} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {RemoteRTPAudioTrack} from "tc-shared/connection/rtc/RemoteTrack";
|
||||
|
||||
export class RtpVoiceClient extends VoicePlayer implements VoiceClient {
|
||||
private readonly clientId: number;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {VoicePlayerEvents, VoicePlayerLatencySettings, VoicePlayerState} from "tc-shared/voice/VoicePlayer";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {LogCategory, logTrace, logWarn} from "tc-shared/log";
|
||||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
import {RemoteRTPAudioTrack, RemoteRTPTrackState} from "tc-shared/connection/rtc/RemoteTrack";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@ export = env => config_base.config(env, "client").then(config => {
|
|||
|
||||
Object.assign(config.resolve.alias, {
|
||||
"tc-shared": path.resolve(__dirname, "shared/js"),
|
||||
/* backend hasn't declared but its available via "require()" */
|
||||
"tc-backend": path.resolve(__dirname, "shared/backend.d"),
|
||||
});
|
||||
|
||||
if(!Array.isArray(config.externals))
|
||||
|
|
|
@ -9,8 +9,6 @@ export = env => config_base.config(env, "web").then(config => {
|
|||
|
||||
Object.assign(config.resolve.alias, {
|
||||
"tc-shared": path.resolve(__dirname, "shared/js"),
|
||||
"tc-backend/web": path.resolve(__dirname, "web/app"),
|
||||
"tc-backend": path.resolve(__dirname, "web/app"),
|
||||
});
|
||||
|
||||
return Promise.resolve(config);
|
||||
|
|
Loading…
Reference in New Issue