2020-04-01 15:56:23 -04:00
import { ServerConnection } from "./ServerConnection" ;
import { NativeVoiceConnection } from "tc-native/connection" ;
import { RecorderProfile } from "tc-shared/voice/RecorderProfile" ;
import { tr } from "tc-shared/i18n/localize" ;
import { LogCategory } from "tc-shared/log" ;
import * as log from "tc-shared/log" ;
2020-08-21 07:37:10 -04:00
import {
AbstractVoiceConnection ,
LatencySettings ,
VoiceClient ,
VoiceConnectionStatus
} from "tc-shared/connection/VoiceConnection" ;
2020-04-01 15:56:23 -04:00
import { NativeInput } from "../audio/AudioRecorder" ;
export class VoiceConnection extends AbstractVoiceConnection {
readonly connection : ServerConnection ;
readonly handle : NativeVoiceConnection ;
private _audio_source : RecorderProfile ;
constructor ( connection : ServerConnection , voice : NativeVoiceConnection ) {
super ( connection ) ;
this . connection = connection ;
this . handle = voice ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
setup() { }
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
async acquire_voice_recorder ( recorder : RecorderProfile | undefined , enforce? : boolean ) {
if ( this . _audio_source === recorder && ! enforce )
return ;
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
if ( this . _audio_source )
await this . _audio_source . unmount ( ) ;
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
if ( recorder ) {
if ( ! ( recorder . input instanceof NativeInput ) )
throw "Recorder input must be an instance of NativeInput!" ;
await recorder . unmount ( ) ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
this . handleVoiceEnded ( ) ;
this . _audio_source = recorder ;
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
if ( recorder ) {
recorder . current_handler = this . connection . client ;
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
recorder . callback_unmount = ( ) = > {
this . _audio_source = undefined ;
this . handle . set_audio_source ( undefined ) ;
this . connection . client . update_voice_status ( undefined ) ;
} ;
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
recorder . callback_start = this . on_voice_started . bind ( this ) ;
recorder . callback_stop = this . handleVoiceEnded . bind ( this ) ;
2019-10-25 19:51:40 -04:00
2020-06-13 09:05:49 -04:00
( recorder as any ) . callback_support_change = ( ) = > {
2020-04-01 15:56:23 -04:00
this . connection . client . update_voice_status ( undefined ) ;
} ;
2020-06-13 09:05:49 -04:00
2020-08-21 07:37:10 -04:00
this . handle . set_audio_source ( ( recorder . input as NativeInput ) . getNativeConsumer ( ) ) ;
2020-04-01 15:56:23 -04:00
}
this . connection . client . update_voice_status ( undefined ) ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
voice_playback_support ( ) : boolean {
return this . connection . connected ( ) ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
voice_send_support ( ) : boolean {
return this . connection . connected ( ) ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
private current_channel_codec ( ) : number {
const chandler = this . connection . client ;
return ( chandler . getClient ( ) . currentChannel ( ) || { properties : { channel_codec : 4 } } ) . properties . channel_codec ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
private handleVoiceEnded() {
const chandler = this . connection . client ;
chandler . getClient ( ) . speaking = false ;
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
if ( ! chandler . connected )
return false ;
2019-10-25 19:51:40 -04:00
2020-06-13 09:05:49 -04:00
if ( chandler . isMicrophoneMuted ( ) )
2020-04-01 15:56:23 -04:00
return false ;
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
console . log ( tr ( "Local voice ended" ) ) ;
2020-04-25 06:48:44 -04:00
//TODO Send end? (Or is this already an automated thing?)
2020-04-01 15:56:23 -04:00
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
private on_voice_started() {
const chandler = this . connection . client ;
2020-06-13 09:05:49 -04:00
if ( chandler . isMicrophoneMuted ( ) ) {
2020-04-01 15:56:23 -04:00
/* evil hack due to the settings :D */
log . warn ( LogCategory . VOICE , tr ( "Received local voice started event, even thou we're muted! Do not send any voice." ) ) ;
if ( this . handle ) {
this . handle . enable_voice_send ( false ) ;
2019-10-25 19:51:40 -04:00
}
2020-04-01 15:56:23 -04:00
return ;
}
2019-10-25 19:51:40 -04:00
2020-08-21 07:37:10 -04:00
log . info ( LogCategory . VOICE , tr ( "Local voice started (Native)" ) ) ;
2020-04-01 15:56:23 -04:00
this . handle . enable_voice_send ( true ) ;
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
const ch = chandler . getClient ( ) ;
if ( ch ) ch . speaking = true ;
}
2019-10-25 19:51:40 -04:00
2020-08-21 07:37:10 -04:00
getConnectionState ( ) : VoiceConnectionStatus {
return VoiceConnectionStatus . Connected ;
2020-04-01 15:56:23 -04:00
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
voice_recorder ( ) : RecorderProfile {
return this . _audio_source ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
available_clients ( ) : VoiceClient [ ] {
return this . handle . available_clients ( ) . map ( e = > Object . assign ( e , {
support_latency_settings() { return true ; } ,
2020-06-13 09:05:49 -04:00
reset_latency_settings() {
2020-04-01 15:56:23 -04:00
const stream = this . get_stream ( ) ;
2020-06-11 07:50:37 -04:00
stream . set_buffer_latency ( 0.080 ) ;
stream . set_buffer_max_latency ( 0.5 ) ;
2020-04-01 15:56:23 -04:00
return this . latency_settings ( ) ;
} ,
2020-06-13 09:05:49 -04:00
latency_settings ( settings? : LatencySettings ) : LatencySettings {
2020-04-01 15:56:23 -04:00
const stream = this . get_stream ( ) ;
if ( typeof settings !== "undefined" ) {
stream . set_buffer_latency ( settings . min_buffer / 1000 ) ;
stream . set_buffer_max_latency ( settings . max_buffer / 100 ) ;
2019-10-25 19:51:40 -04:00
}
2020-04-01 15:56:23 -04:00
return {
max_buffer : Math.floor ( stream . get_buffer_max_latency ( ) * 1000 ) ,
min_buffer : Math.floor ( stream . get_buffer_latency ( ) * 1000 )
} ;
} ,
support_flush() { return true ; } ,
2020-06-13 09:05:49 -04:00
flush() {
2020-04-01 15:56:23 -04:00
const stream = this . get_stream ( ) ;
stream . flush_buffer ( ) ;
}
2020-06-13 09:05:49 -04:00
} ) as any ) ; /* cast to any because of: Type 'import("/mnt/d/TeaSpeak/client_linux/client/imports/shared-app/connection/ConnectionBase").voice.PlayerState' is not assignable to type 'import("tc-native/connection").PlayerState' */
2020-04-01 15:56:23 -04:00
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
find_client ( client_id : number ) : VoiceClient | undefined {
for ( const client of this . available_clients ( ) )
if ( client . client_id === client_id )
return client ;
return undefined ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
unregister_client ( client : VoiceClient ) : Promise < void > {
this . handle . unregister_client ( client . client_id ) ;
return Promise . resolve ( ) ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
register_client ( client_id : number ) : VoiceClient {
const client = this . handle . register_client ( client_id ) ;
const c = this . find_client ( client_id ) ;
c . reset_latency_settings ( ) ;
return c ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
decoding_supported ( codec : number ) : boolean {
return this . handle . decoding_supported ( codec ) ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
encoding_supported ( codec : number ) : boolean {
return this . handle . encoding_supported ( codec ) ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
get_encoder_codec ( ) : number {
return this . handle . get_encoder_codec ( ) ;
}
2019-10-25 19:51:40 -04:00
2020-04-01 15:56:23 -04:00
set_encoder_codec ( codec : number ) {
return this . handle . set_encoder_codec ( codec ) ;
2019-10-25 19:51:40 -04:00
}
}