|
|
@ -14,12 +14,15 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
GNU General Public License for more details. |
|
|
|
GNU General Public License for more details. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <opus.h> |
|
|
|
|
|
|
|
#include "common.h" |
|
|
|
|
|
|
|
#include "client.h" |
|
|
|
#include "voice.h" |
|
|
|
#include "voice.h" |
|
|
|
|
|
|
|
|
|
|
|
wavdata_t *input_file; |
|
|
|
static wavdata_t *input_file; |
|
|
|
fs_offset_t input_pos; |
|
|
|
static fs_offset_t input_pos; |
|
|
|
|
|
|
|
|
|
|
|
voice_state_t voice; |
|
|
|
voice_state_t voice = { 0 }; |
|
|
|
|
|
|
|
|
|
|
|
CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "enable voice chat" ); |
|
|
|
CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "enable voice chat" ); |
|
|
|
CVAR_DEFINE_AUTO( voice_loopback, "0", FCVAR_PRIVILEGED, "loopback voice back to the speaker" ); |
|
|
|
CVAR_DEFINE_AUTO( voice_loopback, "0", FCVAR_PRIVILEGED, "loopback voice back to the speaker" ); |
|
|
@ -47,6 +50,12 @@ static void Voice_CodecInfo_f( void ) |
|
|
|
opus_int32 encoderBitrate; |
|
|
|
opus_int32 encoderBitrate; |
|
|
|
opus_int32 encoderBandwidthType; |
|
|
|
opus_int32 encoderBandwidthType; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( !voice.initialized ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Con_Printf( "Voice codec is not initialized!\n" ); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
opus_encoder_ctl( voice.encoder, OPUS_GET_BITRATE( &encoderBitrate )); |
|
|
|
opus_encoder_ctl( voice.encoder, OPUS_GET_BITRATE( &encoderBitrate )); |
|
|
|
opus_encoder_ctl( voice.encoder, OPUS_GET_COMPLEXITY( &encoderComplexity )); |
|
|
|
opus_encoder_ctl( voice.encoder, OPUS_GET_COMPLEXITY( &encoderComplexity )); |
|
|
|
opus_encoder_ctl( voice.encoder, OPUS_GET_BANDWIDTH( &encoderBandwidthType )); |
|
|
|
opus_encoder_ctl( voice.encoder, OPUS_GET_BANDWIDTH( &encoderBandwidthType )); |
|
|
@ -54,8 +63,7 @@ static void Voice_CodecInfo_f( void ) |
|
|
|
Con_Printf( "Encoder:\n" ); |
|
|
|
Con_Printf( "Encoder:\n" ); |
|
|
|
Con_Printf( " Bitrate: %.3f kbps\n", encoderBitrate / 1000.0f ); |
|
|
|
Con_Printf( " Bitrate: %.3f kbps\n", encoderBitrate / 1000.0f ); |
|
|
|
Con_Printf( " Complexity: %d\n", encoderComplexity ); |
|
|
|
Con_Printf( " Complexity: %d\n", encoderComplexity ); |
|
|
|
Con_Printf( " Bandwidth: " ); |
|
|
|
Con_Printf( " Bandwidth: %s", Voice_GetBandwidthTypeName( encoderBandwidthType )); |
|
|
|
Con_Printf( Voice_GetBandwidthTypeName( encoderBandwidthType )); |
|
|
|
|
|
|
|
Con_Printf( "\n" ); |
|
|
|
Con_Printf( "\n" ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -191,7 +199,7 @@ void Voice_DeInit( void ) |
|
|
|
voice.initialized = false; |
|
|
|
voice.initialized = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) |
|
|
|
static uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
uint ofs, size = 0; |
|
|
|
uint ofs, size = 0; |
|
|
|
|
|
|
|
|
|
|
@ -234,7 +242,29 @@ uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) |
|
|
|
return size; |
|
|
|
return size; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Voice_Idle( float frametime ) |
|
|
|
static void Voice_StatusTimeout( voice_status_t *status, int entindex, double frametime ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if( status->talking_ack ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
status->talking_timeout += frametime; |
|
|
|
|
|
|
|
if( status->talking_timeout > 0.2 ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
status->talking_ack = false; |
|
|
|
|
|
|
|
Voice_Status( entindex, false ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Voice_StatusAck( voice_status_t *status, int playerIndex ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if( !status->talking_ack ) |
|
|
|
|
|
|
|
Voice_Status( playerIndex, true ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
status->talking_ack = true; |
|
|
|
|
|
|
|
status->talking_timeout = 0.0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Voice_Idle( double frametime ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int i; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
@ -244,29 +274,11 @@ void Voice_Idle( float frametime ) |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ( voice.talking_ack ) |
|
|
|
// update local player status first
|
|
|
|
{ |
|
|
|
Voice_StatusTimeout( &voice.local, VOICE_LOCALPLAYER_INDEX, frametime ); |
|
|
|
voice.talking_timeout += frametime; |
|
|
|
|
|
|
|
if( voice.talking_timeout > 0.2f ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
voice.talking_ack = false; |
|
|
|
|
|
|
|
Voice_Status( -2, false ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < 32; i++ ) |
|
|
|
for( i = 0; i < 32; i++ ) |
|
|
|
{ |
|
|
|
Voice_StatusTimeout( &voice.players_status[i], i, frametime ); |
|
|
|
if ( voice.players_status[i].talking_ack ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
voice.players_status[i].talking_timeout += frametime; |
|
|
|
|
|
|
|
if ( voice.players_status[i].talking_timeout > 0.2f ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
voice.players_status[i].talking_ack = false; |
|
|
|
|
|
|
|
if ( i < cl.maxclients ) |
|
|
|
|
|
|
|
Voice_Status( i, false ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
qboolean Voice_IsRecording( void ) |
|
|
|
qboolean Voice_IsRecording( void ) |
|
|
@ -333,6 +345,12 @@ void Voice_Disconnect( void ) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void Voice_StartChannel( uint samples, byte *data, int entnum ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
SND_ForceInitMouth( entnum ); |
|
|
|
|
|
|
|
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 255 ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) |
|
|
|
void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); |
|
|
|
int samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); |
|
|
@ -353,42 +371,9 @@ void CL_AddVoiceToDatagram( void ) |
|
|
|
if( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 ) |
|
|
|
if( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
MSG_BeginClientCmd( &cls.datagram, clc_voicedata ); |
|
|
|
MSG_BeginClientCmd( &cls.datagram, clc_voicedata ); |
|
|
|
MSG_WriteByte( &cls.datagram, Voice_GetLoopback() ); |
|
|
|
MSG_WriteByte( &cls.datagram, voice_loopback.value != 0 ); |
|
|
|
MSG_WriteByte( &cls.datagram, frames ); |
|
|
|
MSG_WriteByte( &cls.datagram, frames ); |
|
|
|
MSG_WriteShort( &cls.datagram, size ); |
|
|
|
MSG_WriteShort( &cls.datagram, size ); |
|
|
|
MSG_WriteBytes( &cls.datagram, voice.output_buffer, size ); |
|
|
|
MSG_WriteBytes( &cls.datagram, voice.output_buffer, size ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
qboolean Voice_GetLoopback( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return voice_loopback.value; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Voice_LocalPlayerTalkingAck( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if ( !voice.talking_ack ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Voice_Status( -2, true ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
voice.talking_ack = true; |
|
|
|
|
|
|
|
voice.talking_timeout = 0.0f; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Voice_PlayerTalkingAck(int playerIndex) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if( !voice.players_status[playerIndex].talking_ack ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Voice_Status( playerIndex, true ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
voice.players_status[playerIndex].talking_ack = true; |
|
|
|
|
|
|
|
voice.players_status[playerIndex].talking_timeout = 0.0f; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Voice_StartChannel( uint samples, byte *data, int entnum ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
SND_ForceInitMouth( entnum ); |
|
|
|
|
|
|
|
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 255 ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|