diff --git a/js/ui/client.ts b/js/ui/client.ts index b1e0cb9f..11abb63c 100644 --- a/js/ui/client.ts +++ b/js/ui/client.ts @@ -345,6 +345,7 @@ class ClientEntry { updateVariables(...variables: {key: string, value: string}[]) { let group = log.group(log.LogType.DEBUG, LogCategory.CLIENT, "Update properties (%i) of %s (%i)", variables.length, this.clientNickName(), this.clientId()); + let update_status_icon = false; for(let variable of variables) { JSON.map_field_to(this._properties, variable.value, variable.key); diff --git a/js/ui/frames/ControlBar.ts b/js/ui/frames/ControlBar.ts index 37045f65..cae88fa2 100644 --- a/js/ui/frames/ControlBar.ts +++ b/js/ui/frames/ControlBar.ts @@ -17,7 +17,9 @@ class ControlBar { private _away: boolean; private _awayMessage: string; - private _codecNotSupported: boolean = false; + private codec_supported: boolean = false; + private support_playback: boolean = false; + private support_record: boolean = false; readonly handle: TSClient; htmlTag: JQuery; @@ -141,26 +143,34 @@ class ControlBar { client_output_muted: this._muteOutput, client_away: this._away, client_away_message: this._awayMessage, - client_input_hardware: !this._codecNotSupported, - client_output_hardware: !this._codecNotSupported + client_input_hardware: this.codec_supported && this.support_record, + client_output_hardware: this.codec_supported && this.support_playback }); } updateVoice(targetChannel?: ChannelEntry) { if(!targetChannel) targetChannel = this.handle.getClient().currentChannel(); - let voiceSupport = this.handle.voiceConnection.codecSupported(targetChannel.properties.channel_codec); - this._codecNotSupported = !voiceSupport; + let client = this.handle.getClient(); - let voice_support = this.handle.voiceConnection.voiceSupported(); - this.htmlTag.find(".btn_mute_input").prop("disabled", !this._codecNotSupported && voice_support); - this.htmlTag.find(".btn_mute_output").prop("disabled", !this._codecNotSupported && voice_support); + this.codec_supported = this.handle.voiceConnection.codecSupported(targetChannel.properties.channel_codec); + this.support_record = this.handle.voiceConnection.voice_send_support(); + this.support_playback = this.handle.voiceConnection.voice_playback_support(); + + this.htmlTag.find(".btn_mute_input").prop("disabled", !this.codec_supported|| !this.support_playback || !this.support_record); + this.htmlTag.find(".btn_mute_output").prop("disabled", !this.codec_supported || !this.support_playback); this.handle.serverConnection.sendCommand("clientupdate", { - client_input_hardware: voiceSupport, - client_output_hardware: voiceSupport + client_input_hardware: this.codec_supported && this.support_record, + client_output_hardware: this.codec_supported && this.support_playback }); - if(this._codecNotSupported) + if(!this.codec_supported) createErrorModal("Channel codec unsupported", "This channel has an unsupported codec.
You cant speak or listen to anybody within this channel!").open(); + + /* Update these properties anyways (for case the server fails to handle the command) */ + client.updateVariables( + {key: "client_input_hardware", value: (this.codec_supported && this.support_record) + ""}, + {key: "client_output_hardware", value: (this.codec_supported && this.support_playback) + ""} + ); } private onOpenSettings() { diff --git a/js/voice/VoiceHandler.ts b/js/voice/VoiceHandler.ts index f82f0450..345d6cf4 100644 --- a/js/voice/VoiceHandler.ts +++ b/js/voice/VoiceHandler.ts @@ -136,6 +136,7 @@ class VoiceConnection { this.voiceRecorder.on_data = this.handleVoiceData.bind(this); } this.voiceRecorder.on_end = this.handleVoiceEnded.bind(this); + this.voiceRecorder.on_start = this.handleVoiceStarted.bind(this); this.voiceRecorder.reinitialiseVAD(); AudioController.on_initialized(() => { @@ -158,10 +159,17 @@ class VoiceConnection { return this.codec_pool.length > type && this.codec_pool[type].supported(); } - voiceSupported() : boolean { + voice_playback_support() : boolean { return this.dataChannel && this.dataChannel.readyState == "open"; } + voice_send_support() : boolean { + if(this.type == VoiceConnectionType.NATIVE_ENCODE) + return this.rtcPeerConnection.getLocalStreams().length > 0; + else + return this.voice_playback_support(); + } + private voice_send_queue: {data: Uint8Array, codec: number}[] = []; handleEncodedVoicePacket(data: Uint8Array, codec: number){ this.voice_send_queue.push({data: data, codec: codec}); @@ -340,17 +348,22 @@ class VoiceConnection { .then(encoder => encoder.encodeSamples(this.client.getClient().getAudioController().codecCache(4), data)); } - audio_destination() : AudioNode { - return undefined; - } - private handleVoiceEnded() { + if(this.client && this.client.getClient()) + this.client.getClient().speaking = false; + if(!this.voiceRecorder) return; if(!this.client.connected) return; + console.log("Local voice ended"); - console.log("Voice ended"); - this.client.getClient().speaking = false; if(this.dataChannel) this.sendVoicePacket(new Uint8Array(0), 5); //TODO Use channel codec! } + + private handleVoiceStarted() { + console.log("Local voice started"); + + if(this.client && this.client.getClient()) + this.client.getClient().speaking = true; + } } \ No newline at end of file diff --git a/js/voice/VoiceRecorder.ts b/js/voice/VoiceRecorder.ts index d07e5e96..b956fc0d 100644 --- a/js/voice/VoiceRecorder.ts +++ b/js/voice/VoiceRecorder.ts @@ -30,7 +30,8 @@ class VoiceRecorder { handle: VoiceConnection; on_data: (data: AudioBuffer, head: boolean) => void = undefined; - on_end: () => void = () => {}; + on_end: () => any; + on_start: () => any; private _recording: boolean = false; @@ -57,17 +58,26 @@ class VoiceRecorder { this.audioContext = AudioController.globalContext; this.processor = this.audioContext.createScriptProcessor(VoiceRecorder.BUFFER_SIZE, VoiceRecorder.CHANNELS, VoiceRecorder.CHANNELS); + const empty_buffer = this.audioContext.createBuffer(VoiceRecorder.CHANNELS, VoiceRecorder.BUFFER_SIZE, 48000); this.processor.addEventListener('audioprocess', ev => { if(this.microphoneStream && this.vadHandler.shouldRecord(ev.inputBuffer)) { + if(this._chunkCount == 0 && this.on_start) + this.on_start(); + if(this.on_data) - this.on_data(ev.inputBuffer, this._chunkCount++ == 0); + this.on_data(ev.inputBuffer, this._chunkCount == 0); else { for(let channel = 0; channel < ev.inputBuffer.numberOfChannels; channel++) ev.outputBuffer.copyToChannel(ev.inputBuffer.getChannelData(channel), channel); } + this._chunkCount++; } else { - if(this._chunkCount != 0) this.on_end(); - this._chunkCount = 0 + if(this._chunkCount != 0 && this.on_end) + this.on_end(); + this._chunkCount = 0; + + for(let channel = 0; channel < ev.inputBuffer.numberOfChannels; channel++) + ev.outputBuffer.copyToChannel(empty_buffer.getChannelData(channel), channel); } }); this.processor.connect(this.audioContext.destination);