Implemented speex for the native client

canary
WolverinDEV 2018-11-14 19:29:29 +01:00
parent 5ac06d4819
commit 69537aee79
6 changed files with 47 additions and 21 deletions

View File

@ -4,7 +4,12 @@ interface CodecCostructor {
enum CodecType { enum CodecType {
OPUS_VOICE, OPUS_VOICE,
OPUS_MUSIC OPUS_MUSIC,
SPEEX_NARROWBAND,
SPEEX_WIDEBAND,
SPEEX_ULTRA_WIDEBAND,
CELT_MONO
} }
class BufferChunk { class BufferChunk {

View File

@ -121,6 +121,7 @@ function loadDebug() {
if(!window.require) { if(!window.require) {
console.log("Adding browser audio player"); console.log("Adding browser audio player");
custom_scripts.push(["js/audio/AudioPlayer.js"]); custom_scripts.push(["js/audio/AudioPlayer.js"]);
custom_scripts.push(["js/audio/WebCodec.js"]);
} }
load_wait_scripts([ load_wait_scripts([

View File

@ -13,7 +13,7 @@ class CodecPool {
handle: VoiceConnection; handle: VoiceConnection;
codecIndex: number; codecIndex: number;
name: string; name: string;
creator: () => BasicCodec; type: CodecType;
entries: CodecPoolEntry[] = []; entries: CodecPoolEntry[] = [];
maxInstances: number = 2; maxInstances: number = 2;
@ -26,6 +26,7 @@ class CodecPool {
console.log("Release again! (%o)", codec); console.log("Release again! (%o)", codec);
this.releaseCodec(i + 1); this.releaseCodec(i + 1);
}).catch(error => { }).catch(error => {
console.warn("Disabling codec support for " + this.name);
if(this._supported) { if(this._supported) {
createErrorModal("Could not load codec driver", "Could not load or initialize codec " + this.name + "<br>" + createErrorModal("Could not load codec driver", "Could not load or initialize codec " + this.name + "<br>" +
"Error: <code>" + JSON.stringify(error) + "</code>").open(); "Error: <code>" + JSON.stringify(error) + "</code>").open();
@ -35,11 +36,11 @@ class CodecPool {
}); });
} }
supported() { return this.creator != undefined && this._supported; } supported() { return this._supported; }
ownCodec?(clientId: number, create: boolean = true) : Promise<BasicCodec | undefined> { ownCodec?(clientId: number, create: boolean = true) : Promise<BasicCodec | undefined> {
return new Promise<BasicCodec>((resolve, reject) => { return new Promise<BasicCodec>((resolve, reject) => {
if(!this.creator || !this._supported) { if(!this._supported) {
reject("unsupported codec!"); reject("unsupported codec!");
return; return;
} }
@ -72,7 +73,7 @@ class CodecPool {
if(freeSlot == 0){ if(freeSlot == 0){
freeSlot = this.entries.length; freeSlot = this.entries.length;
let entry = new CodecPoolEntry(); let entry = new CodecPoolEntry();
entry.instance = this.creator(); entry.instance = audio.codec.new_instance(this.type);
entry.instance.on_encoded_data = buffer => this.handle.handleEncodedVoicePacket(buffer, this.codecIndex); entry.instance.on_encoded_data = buffer => this.handle.handleEncodedVoicePacket(buffer, this.codecIndex);
this.entries.push(entry); this.entries.push(entry);
} }
@ -93,11 +94,13 @@ class CodecPool {
if(this.entries[index].owner == clientId) this.entries[index].owner = 0; if(this.entries[index].owner == clientId) this.entries[index].owner = 0;
} }
constructor(handle: VoiceConnection, index: number, name: string, creator: () => BasicCodec){ constructor(handle: VoiceConnection, index: number, name: string, type: CodecType){
this.creator = creator;
this.handle = handle; this.handle = handle;
this.codecIndex = index; this.codecIndex = index;
this.name = name; this.name = name;
this.type = type;
this._supported = this.type !== undefined && audio.codec.supported(this.type);
} }
} }
@ -121,17 +124,17 @@ class VoiceConnection {
dataChannel: RTCDataChannel; dataChannel: RTCDataChannel;
voiceRecorder: VoiceRecorder; voiceRecorder: VoiceRecorder;
private _type: VoiceConnectionType = VoiceConnectionType.NATIVE_ENCODE; private _type: VoiceConnectionType = VoiceConnectionType.JS_ENCODE;
local_audio_stream: any; local_audio_stream: any;
private codec_pool: CodecPool[] = [ private codec_pool: CodecPool[] = [
new CodecPool(this,0,"Spex A", undefined), //Spex new CodecPool(this,0,"Speex Narrowband", CodecType.SPEEX_NARROWBAND),
new CodecPool(this,1,"Spex B", undefined), //Spex new CodecPool(this,1,"Speex Wideband", CodecType.SPEEX_WIDEBAND),
new CodecPool(this,2,"Spex C", undefined), //Spex new CodecPool(this,2,"Speex Ultra Wideband", CodecType.SPEEX_ULTRA_WIDEBAND),
new CodecPool(this,3,"CELT Mono", undefined), //CELT Mono new CodecPool(this,3,"CELT Mono", CodecType.CELT_MONO),
new CodecPool(this,4,"Opus Voice", () => { return audio.codec.new_instance(CodecType.OPUS_VOICE) }), //opus voice new CodecPool(this,4,"Opus Voice", CodecType.OPUS_VOICE),
new CodecPool(this,5,"Opus Music", () => { return audio.codec.new_instance(CodecType.OPUS_MUSIC) }) //opus music new CodecPool(this,5,"Opus Music", CodecType.OPUS_MUSIC)
]; ];
private vpacketId: number = 0; private vpacketId: number = 0;
@ -148,6 +151,12 @@ class VoiceConnection {
audio.player.on_ready(() => { audio.player.on_ready(() => {
log.info(LogCategory.VOICE, "Initializing voice handler after AudioController has been initialized!"); log.info(LogCategory.VOICE, "Initializing voice handler after AudioController has been initialized!");
if(native_client) {
this.codec_pool[0].initialize(2);
this.codec_pool[1].initialize(2);
this.codec_pool[2].initialize(2);
this.codec_pool[3].initialize(2);
}
this.codec_pool[4].initialize(2); this.codec_pool[4].initialize(2);
this.codec_pool[5].initialize(2); this.codec_pool[5].initialize(2);
@ -415,6 +424,10 @@ class VoiceConnection {
} }
} }
private current_channel_codec() : number {
return (this.client.getClient().currentChannel() || {properties: { channel_codec: 4}}).properties.channel_codec;
}
private handleVoiceData(data: AudioBuffer, head: boolean) { private handleVoiceData(data: AudioBuffer, head: boolean) {
if(!this.voiceRecorder) return; if(!this.voiceRecorder) return;
if(!this.client.connected) return false; if(!this.client.connected) return false;
@ -426,8 +439,9 @@ class VoiceConnection {
} }
//TODO Use channel codec! //TODO Use channel codec!
this.codec_pool[4].ownCodec(this.client.getClientId()) const codec = this.current_channel_codec();
.then(encoder => encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4), data)); this.codec_pool[codec].ownCodec(this.client.getClientId())
.then(encoder => encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(codec), data));
} }
private handleVoiceEnded() { private handleVoiceEnded() {
@ -439,7 +453,7 @@ class VoiceConnection {
console.log("Local voice ended"); console.log("Local voice ended");
if(this.dataChannel) if(this.dataChannel)
this.sendVoicePacket(new Uint8Array(0), 5); //TODO Use channel codec! this.sendVoicePacket(new Uint8Array(0), this.current_channel_codec()); //TODO Use channel codec!
} }
private handleVoiceStarted() { private handleVoiceStarted() {

View File

@ -23,6 +23,7 @@ onmessage = function(e: MessageEvent) {
//console.log(prefix + " Got from main: %o", data); //console.log(prefix + " Got from main: %o", data);
switch (data.command) { switch (data.command) {
case "initialise": case "initialise":
let error;
console.log(prefix + "Got initialize for type " + CodecType[data.type as CodecType]); console.log(prefix + "Got initialize for type " + CodecType[data.type as CodecType]);
switch (data.type as CodecType) { switch (data.type as CodecType) {
case CodecType.OPUS_MUSIC: case CodecType.OPUS_MUSIC:
@ -32,12 +33,12 @@ onmessage = function(e: MessageEvent) {
codecInstance = new OpusWorker(1, OpusType.VOIP); codecInstance = new OpusWorker(1, OpusType.VOIP);
break; break;
default: default:
res.message = "Could not find worker type!"; error = "Could not find worker type!";
console.error("Could not resolve opus type!"); console.error("Could not resolve opus type!");
return; break;
} }
let error = codecInstance.initialise(); error = error || codecInstance.initialise();
if(error) if(error)
res["message"] = error; res["message"] = error;
else else

View File

@ -7,6 +7,7 @@
}, },
"files": [ "files": [
"codec/CodecWorker.ts", "codec/CodecWorker.ts",
"codec/OpusCodec.ts" "codec/OpusCodec.ts",
"../codec/Codec.ts"
] ]
} }

View File

@ -4,4 +4,8 @@ namespace audio.codec {
export function new_instance(type: CodecType) : BasicCodec { export function new_instance(type: CodecType) : BasicCodec {
return new CodecWrapperWorker(type); return new CodecWrapperWorker(type);
} }
export function supported(type: CodecType) : boolean {
return type == CodecType.OPUS_MUSIC || type == CodecType.OPUS_VOICE;
}
} }