Updates
This commit is contained in:
parent
cf321654dc
commit
02e7f8c355
21 changed files with 185 additions and 279789 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -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
2
generated/js/client.min.js
vendored
2
generated/js/client.min.js
vendored
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();
|
||||
this.sendWorkerMessage({
|
||||
command: "initialise",
|
||||
type: this.type,
|
||||
channelCount: this.channelCount
|
||||
});
|
||||
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,
|
||||
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");
|
||||
this._worker.onmessage = event => this.onWorkerMessage(JSON.parse(event.data));
|
||||
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;
|
||||
|
||||
let free = 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;
|
||||
ownCodec?(clientId: number, create: boolean = true) : Promise<BasicCodec | undefined> {
|
||||
return new Promise<BasicCodec>((resolve, reject) => {
|
||||
if(!this.creator) {
|
||||
reject("unsupported codec!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!create) return null;
|
||||
if(free == 0){
|
||||
free = 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;
|
||||
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();
|
||||
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) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
if(freeSlot == 0){
|
||||
freeSlot = this.entries.length;
|
||||
let entry = new CodecPoolEntry();
|
||||
entry.instance = this.creator();
|
||||
entry.instance.on_encoded_data = buffer => this.handle.sendVoicePacket(buffer, this.codecIndex);
|
||||
this.entries.push(entry);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
2
js/workers/compile.sh
Executable file
2
js/workers/compile.sh
Executable file
|
@ -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…
Add table
Reference in a new issue