Updates
parent
cf321654dc
commit
02e7f8c355
|
@ -0,0 +1,2 @@
|
|||
js/**/*.js*
|
||||
.idea/
|
File diff suppressed because one or more lines are too long
|
@ -4189,7 +4189,7 @@ class TSClient {
|
|||
}
|
||||
else {
|
||||
host = addr;
|
||||
port = 19974;
|
||||
port = 9987;
|
||||
}
|
||||
console.log("Start connection to " + host + ":" + port);
|
||||
this.channelTree.initialiseHead(addr);
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -20,7 +20,8 @@ abstract class BasicCodec implements Codec {
|
|||
}
|
||||
|
||||
abstract name() : string;
|
||||
abstract initialise();
|
||||
abstract initialise() : Promise<Boolean>;
|
||||
abstract initialized() : boolean;
|
||||
abstract deinitialise();
|
||||
abstract reset() : boolean;
|
||||
|
||||
|
|
|
@ -11,17 +11,43 @@ class CodecWrapper extends BasicCodec {
|
|||
private _workerTokeIndex: number = 0;
|
||||
type: CodecWorkerType;
|
||||
|
||||
private _initialized: boolean = false;
|
||||
private _workerCallbackResolve: () => any;
|
||||
private _workerCallbackReject: ($: any) => any;
|
||||
|
||||
private _initializePromise: Promise<Boolean>;
|
||||
name(): string {
|
||||
return "Worker for " + CodecWorkerType[this.type] + " Channels " + this.channelCount;
|
||||
}
|
||||
|
||||
initialise() {
|
||||
this.spawnWorker();
|
||||
initialise() : Promise<Boolean> {
|
||||
if(this._initializePromise) return this._initializePromise;
|
||||
console.log("INIT!");
|
||||
return this._initializePromise = this.spawnWorker().then(() => new Promise<Boolean>((resolve, reject) => {
|
||||
const token = this.generateToken();
|
||||
this.sendWorkerMessage({
|
||||
command: "initialise",
|
||||
type: this.type,
|
||||
channelCount: this.channelCount
|
||||
channelCount: this.channelCount,
|
||||
token: token
|
||||
});
|
||||
|
||||
this._workerListener.push({
|
||||
token: token,
|
||||
resolve: data => {
|
||||
console.log("Init result: %o", data);
|
||||
this._initialized = data["success"] == true;
|
||||
if(data["success"] == true)
|
||||
resolve();
|
||||
else
|
||||
reject(data.message);
|
||||
}
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
initialized() : boolean {
|
||||
return this._initialized;
|
||||
}
|
||||
|
||||
deinitialise() {
|
||||
|
@ -31,7 +57,7 @@ class CodecWrapper extends BasicCodec {
|
|||
}
|
||||
|
||||
decode(data: Uint8Array): Promise<AudioBuffer> {
|
||||
let token = this._workerTokeIndex++ + "_token";
|
||||
let token = this.generateToken();
|
||||
let result = new Promise<AudioBuffer>((resolve, reject) => {
|
||||
this._workerListener.push(
|
||||
{
|
||||
|
@ -64,7 +90,8 @@ class CodecWrapper extends BasicCodec {
|
|||
}
|
||||
|
||||
encode(data: AudioBuffer) : Promise<Uint8Array> {
|
||||
let token = this._workerTokeIndex++ + "_token";
|
||||
console.log(data);
|
||||
let token = this.generateToken();
|
||||
let result = new Promise<Uint8Array>((resolve, reject) => {
|
||||
this._workerListener.push(
|
||||
{
|
||||
|
@ -112,6 +139,10 @@ class CodecWrapper extends BasicCodec {
|
|||
this.channelCount = channelCount;
|
||||
}
|
||||
|
||||
private generateToken() {
|
||||
return this._workerTokeIndex++ + "_token";
|
||||
}
|
||||
|
||||
private sendWorkerMessage(message: any, transfare?: any[]) {
|
||||
this._worker.postMessage(JSON.stringify(message), transfare);
|
||||
}
|
||||
|
@ -123,6 +154,17 @@ class CodecWrapper extends BasicCodec {
|
|||
}
|
||||
|
||||
if(message["token"] == this._workerCallbackToken) {
|
||||
if(message["type"] == "loaded") {
|
||||
console.log("Got loaded result: %o", message);
|
||||
if(message["success"]) {
|
||||
if(this._workerCallbackResolve)
|
||||
this._workerCallbackResolve();
|
||||
} else {
|
||||
if(this._workerCallbackReject)
|
||||
this._workerCallbackReject(message["message"]);
|
||||
}
|
||||
console.log("Worker initialized!");
|
||||
}
|
||||
console.log("Callback data!");
|
||||
return;
|
||||
}
|
||||
|
@ -138,8 +180,12 @@ class CodecWrapper extends BasicCodec {
|
|||
console.error("Could not find worker token entry! (" + message["token"] + ")");
|
||||
}
|
||||
|
||||
private spawnWorker() {
|
||||
this._worker = new Worker("js/codec/workers/CompiledCodecWorker.js");
|
||||
private spawnWorker() : Promise<Boolean> {
|
||||
return new Promise<Boolean>((resolve, reject) => {
|
||||
this._workerCallbackReject = reject;
|
||||
this._workerCallbackResolve = resolve;
|
||||
this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerCodec.js");
|
||||
this._worker.onmessage = event => this.onWorkerMessage(JSON.parse(event.data));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -13,9 +13,14 @@ class RawCodec extends BasicCodec {
|
|||
return "raw";
|
||||
}
|
||||
|
||||
initialise() {
|
||||
initialise() : Promise<Boolean> {
|
||||
this.converterRaw = Module._malloc(this.bufferSize);
|
||||
this.converter = new Uint8Array(Module.HEAPU8.buffer, this.converterRaw, this.bufferSize);
|
||||
return new Promise<Boolean>(resolve => resolve());
|
||||
}
|
||||
|
||||
initialized(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
deinitialise() { }
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
/usr/local/bin/tsc
|
|
@ -1,5 +1,8 @@
|
|||
//Source: https://www.movable-type.co.uk/scripts/sha1.html
|
||||
|
||||
declare class TextEncoder {
|
||||
encode(msg) : ArrayBuffer;
|
||||
}
|
||||
namespace sha {
|
||||
export function sha1(message: string | ArrayBuffer) : PromiseLike<ArrayBuffer> {
|
||||
let buffer = message instanceof ArrayBuffer ? message : new TextEncoder().encode(message);
|
||||
|
|
|
@ -181,13 +181,14 @@ function loadTemplates() {
|
|||
});
|
||||
}
|
||||
|
||||
//TODO release config!
|
||||
function loadSide() {
|
||||
//Load the general scripts and required scripts
|
||||
awaitLoad(loadScripts([
|
||||
["vendor/jquery/jquery.min.js", /*"https://code.jquery.com/jquery-latest.min.js"*/],
|
||||
["https://webrtc.github.io/adapter/adapter-latest.js"]
|
||||
])).then(() => awaitLoad(loadScripts([
|
||||
["asm/generated/TeaWeb-Native.js", "js/TeaWeb-Native.js"],
|
||||
["asm/generated/TeaWeb-Identity.js", "js/TeaWeb-Native.js"],
|
||||
["https://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"]
|
||||
]))).then(() => {
|
||||
//Load the teaweb scripts
|
||||
|
|
|
@ -14,8 +14,14 @@ let chat: ChatBox;
|
|||
|
||||
let forumIdentity: TeaForumIdentity;
|
||||
|
||||
|
||||
function invokeMain() {
|
||||
Module['onRuntimeInitialized'] = function() {
|
||||
console.log("Runtime ready!");
|
||||
main();
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
//localhost:63343/Web-Client/index.php?disableUnloadDialog=1&default_connect_type=forum&default_connect_url=localhost
|
||||
AudioController.initializeAudioController();
|
||||
if(!TSIdentityHelper.setup()) { console.error("Could not setup the TeamSpeak identity parser!"); return; }
|
||||
|
|
|
@ -44,11 +44,12 @@ class Settings {
|
|||
});
|
||||
}
|
||||
|
||||
private static transformStO?<T>(input: string, _default?: T) : T {
|
||||
private static transformStO?<T>(input?: string, _default?: T) : T {
|
||||
if (typeof input === "undefined") return _default;
|
||||
if (typeof _default === "string") return input as any;
|
||||
else if (typeof _default === "number") return parseInt(input) as any;
|
||||
else if (typeof _default === "boolean") return (input == "1" || input == "true") as any;
|
||||
else if (typeof _default == "undefined") return input as any;
|
||||
else if (typeof _default === "undefined") return input as any;
|
||||
return JSON.parse(input) as any;
|
||||
}
|
||||
|
||||
|
@ -72,6 +73,7 @@ class Settings {
|
|||
|
||||
static?<T>(key: string, _default?: T) : T {
|
||||
let result = this._staticPropsTag.find("[key='" + key + "']");
|
||||
console.log("%d | %o", result.length, result);
|
||||
return Settings.transformStO(result.length > 0 ? decodeURIComponent(result.last().attr("value")) : undefined, _default);
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ class AudioController {
|
|||
if (buffer.sampleRate != this.speakerContext.sampleRate)
|
||||
console.warn("[AudioController] Source sample rate isn't equal to playback sample rate! (" + buffer.sampleRate + " | " + this.speakerContext.sampleRate + ")");
|
||||
|
||||
console.log("%o", buffer);
|
||||
this.applayVolume(buffer);
|
||||
this.audioCache.push(buffer);
|
||||
if(this.playerState == PlayerState.STOPPED || this.playerState == PlayerState.STOPPING) {
|
||||
|
|
|
@ -12,7 +12,7 @@ class AudioResampler {
|
|||
return new Promise<AudioBuffer>(resolve => resolve(buffer));
|
||||
|
||||
let context;
|
||||
context = new OfflineAudioContext(buffer.numberOfChannels, Math.floor(buffer.length * this.targetSampleRate / buffer.sampleRate), this.targetSampleRate);
|
||||
context = new OfflineAudioContext(buffer.numberOfChannels, Math.ceil(buffer.length * this.targetSampleRate / buffer.sampleRate), this.targetSampleRate);
|
||||
|
||||
let source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
|
|
@ -7,6 +7,8 @@ class CodecPoolEntry {
|
|||
owner: number;
|
||||
|
||||
last_access: number;
|
||||
|
||||
private _initializePromise: Promise<Boolean>;
|
||||
}
|
||||
|
||||
class CodecPool {
|
||||
|
@ -19,39 +21,63 @@ class CodecPool {
|
|||
|
||||
initialize(cached: number) {
|
||||
for(let i = 0; i < cached; i++)
|
||||
this.ownCodec(i);
|
||||
for(let i = 0; i < cached; i++)
|
||||
this.releaseCodec(i);
|
||||
this.ownCodec(i + 1).then(codec => {
|
||||
console.log("Release again! (%o)", codec);
|
||||
this.releaseCodec(i + 1);
|
||||
}).catch(errror => {
|
||||
console.error(errror);
|
||||
});
|
||||
}
|
||||
|
||||
supported() { return this.creator != undefined; }
|
||||
|
||||
ownCodec?(clientId: number, create: boolean = true) : BasicCodec {
|
||||
if(!this.creator) return null;
|
||||
ownCodec?(clientId: number, create: boolean = true) : Promise<BasicCodec | undefined> {
|
||||
return new Promise<BasicCodec>((resolve, reject) => {
|
||||
if(!this.creator) {
|
||||
reject("unsupported codec!");
|
||||
return;
|
||||
}
|
||||
|
||||
let free = 0;
|
||||
let freeSlot = 0;
|
||||
for(let index = 0; index < this.entries.length; index++) {
|
||||
if(this.entries[index].owner == clientId) {
|
||||
this.entries[index].last_access = new Date().getTime();
|
||||
return this.entries[index].instance;
|
||||
} else if(free == 0 && this.entries[index].owner == 0) {
|
||||
free = index;
|
||||
if(this.entries[index].instance.initialized()) resolve(this.entries[index].instance);
|
||||
else resolve(this.entries[index].instance.initialise().catch(error => {
|
||||
console.error("Could not initialize codec!\nError: %o", error);
|
||||
reject("Could not initialize codec!");
|
||||
}).then((flag) => {
|
||||
//TODO test success flag
|
||||
return this.ownCodec(clientId, false);
|
||||
}));
|
||||
return;
|
||||
} else if(freeSlot == 0 && this.entries[index].owner == 0) {
|
||||
freeSlot = index;
|
||||
}
|
||||
}
|
||||
|
||||
if(!create) return null;
|
||||
if(free == 0){
|
||||
free = this.entries.length;
|
||||
if(!create) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
if(freeSlot == 0){
|
||||
freeSlot = this.entries.length;
|
||||
let entry = new CodecPoolEntry();
|
||||
entry.instance = this.creator();
|
||||
entry.instance.initialise();
|
||||
entry.instance.on_encoded_data = buffer => this.handle.sendVoicePacket(buffer, this.codecIndex);
|
||||
this.entries.push(entry);
|
||||
}
|
||||
this.entries[free].owner = clientId;
|
||||
this.entries[free].last_access = new Date().getTime();
|
||||
this.entries[free].instance.reset();
|
||||
return this.entries[free].instance;
|
||||
this.entries[freeSlot].owner = clientId;
|
||||
this.entries[freeSlot].last_access = new Date().getTime();
|
||||
if(this.entries[freeSlot].instance.initialized())
|
||||
this.entries[freeSlot].instance.reset();
|
||||
else {
|
||||
resolve(this.ownCodec(clientId, false));
|
||||
return;
|
||||
}
|
||||
resolve(this.entries[freeSlot].instance);
|
||||
});
|
||||
}
|
||||
|
||||
releaseCodec(clientId: number) {
|
||||
|
@ -210,12 +236,18 @@ class VoiceConnection {
|
|||
client.getAudioController().stopAudio();
|
||||
codecPool.releaseCodec(clientId);
|
||||
} else {
|
||||
codecPool.ownCodec(clientId)
|
||||
.then(decoder => decoder.decodeSamples(client.getAudioController().codecCache(codec), encodedData))
|
||||
.then(buffer => client.getAudioController().playBuffer(buffer)).catch(error => {
|
||||
console.error("Could not playback client's (" + clientId + ") audio (" + error + ")");
|
||||
});
|
||||
/*
|
||||
let decoder = codecPool.ownCodec(clientId);
|
||||
decoder.decodeSamples(client.getAudioController().codecCache(codec), encodedData).then(buffer => {
|
||||
client.getAudioController().playBuffer(buffer);
|
||||
}).catch(error => {
|
||||
console.error("Could not playback client's (" + clientId + ") audio (" + error + ")");
|
||||
});
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,12 +260,10 @@ class VoiceConnection {
|
|||
this.chunkVPacketId = 0;
|
||||
this.client.getClient().speaking = true;
|
||||
}
|
||||
let encoder = this.codecPool[4].ownCodec(this.client.getClientId());
|
||||
if(!encoder) {
|
||||
console.error("Could not reserve encoder!");
|
||||
return;
|
||||
}
|
||||
encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4),data); //TODO Use channel codec!
|
||||
|
||||
//TODO Use channel codec!
|
||||
this.codecPool[4].ownCodec(this.client.getClientId())
|
||||
.then(encoder => encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4),data));
|
||||
//this.client.getClient().getAudioController().play(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
const prefix = "[CodecWorker] ";
|
||||
const workerCallbackToken = "callback_token";
|
||||
|
||||
interface CodecWorker {
|
||||
name();
|
||||
initialise();
|
||||
|
@ -12,14 +14,15 @@ interface CodecWorker {
|
|||
enum CodecWorkerType {
|
||||
WORKER_OPUS
|
||||
}
|
||||
|
||||
let codecInstance: CodecWorker;
|
||||
|
||||
onmessage = function(e) {
|
||||
let data = JSON.parse(e.data);
|
||||
//console.log(data);
|
||||
|
||||
let res: any = {};
|
||||
res.token = data.token;
|
||||
res.success = false;
|
||||
|
||||
switch (data.command) {
|
||||
case "initialise":
|
||||
|
@ -30,13 +33,14 @@ onmessage = function(e) {
|
|||
break;
|
||||
|
||||
default:
|
||||
res.message = "Could not find worker type!";
|
||||
console.error("Could not resolve opus type!");
|
||||
return;
|
||||
}
|
||||
|
||||
codecInstance.initialise();
|
||||
res["success"] = true;
|
||||
break;
|
||||
|
||||
case "encodeSamples":
|
||||
let encodeArray = new Float32Array(data.dataLength);
|
||||
for(let index = 0; index < encodeArray.length; index++)
|
||||
|
@ -45,16 +49,13 @@ onmessage = function(e) {
|
|||
let encodeResult = codecInstance.encode(encodeArray);
|
||||
|
||||
if(typeof encodeResult === "string") {
|
||||
res.success = false;
|
||||
res.message = encodeResult;
|
||||
} else {
|
||||
res.success = true;
|
||||
res.data = encodeResult;
|
||||
res.dataLength = encodeResult.length;
|
||||
}
|
||||
sendMessage(res, e.origin);
|
||||
break;
|
||||
|
||||
case "decodeSamples":
|
||||
let decodeArray = new Uint8Array(data.dataLength);
|
||||
for(let index = 0; index < decodeArray.length; index++)
|
||||
|
@ -63,26 +64,24 @@ onmessage = function(e) {
|
|||
let decodeResult = codecInstance.decode(decodeArray);
|
||||
|
||||
if(typeof decodeResult === "string") {
|
||||
res.success = false;
|
||||
res.message = decodeResult;
|
||||
} else {
|
||||
res.success = true;
|
||||
res.data = decodeResult;
|
||||
res.dataLength = decodeResult.length;
|
||||
}
|
||||
sendMessage(res, e.origin);
|
||||
break;
|
||||
|
||||
case "reset":
|
||||
codecInstance.reset();
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(prefix + "Unknown type " + data.command);
|
||||
}
|
||||
|
||||
if(res.token && res.token.length > 0) sendMessage(res, e.origin);
|
||||
};
|
||||
|
||||
declare function postMessage(message: any): void;
|
||||
function sendMessage(message: any, origin: string){
|
||||
function sendMessage(message: any, origin?: string){
|
||||
postMessage(JSON.stringify(message));
|
||||
}
|
|
@ -1,19 +1,25 @@
|
|||
/// <reference path="CodecWorker.ts" />
|
||||
|
||||
//"js/TeaWeb-Native.js", "asm/generated/TeaWeb-Native.js"
|
||||
this["Module"] = typeof this["Module"] !== "undefined" ? this["Module"] : {};
|
||||
let initialized = false;
|
||||
Module['onRuntimeInitialized'] = function() {
|
||||
initialized = true;
|
||||
console.log(prefix + "Initialized!");
|
||||
|
||||
sendMessage({
|
||||
token: workerCallbackToken,
|
||||
type: "loaded",
|
||||
success: true
|
||||
})
|
||||
};
|
||||
|
||||
//let Module = typeof Module !== 'undefined' ? Module : {};
|
||||
try {
|
||||
importScripts("../TeaWeb-Native.js");
|
||||
Module['locateFile'] = file => "../../asm/generated/" + file;
|
||||
importScripts("../../asm/generated/TeaWeb-Worker-Codec-Opus.js");
|
||||
} catch (e) {
|
||||
try {
|
||||
importScripts("../../asm/generated/TeaWeb-Native.js");
|
||||
} catch (e) {
|
||||
try {
|
||||
importScripts("../../../asm/generated/TeaWeb-Native.js");
|
||||
} catch (e) {
|
||||
console.error("Could not load native script!");
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum OpusType {
|
||||
|
@ -89,6 +95,7 @@ class OpusWorker implements CodecWorker {
|
|||
}
|
||||
|
||||
reset() {
|
||||
console.log(prefix + " Reseting opus codec!");
|
||||
this.fn_reset(this.nativeHandle);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
#!/usr/bin/env bash
|
||||
/usr/local/bin/tsc -p tsconfig_worker_codec.json
|
|
@ -6,6 +6,6 @@
|
|||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"js/codec/workers"
|
||||
"js/workers"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue