engine/engine/sndcodec/snd_vc.c

436 lines
10 KiB
C

//voice chat routines.
//needs quite a bit of work.
//it needs a proper protocol.
//the server needs to be able to shutdown again.
//options about who gets the sound data is also needed.
#include "bothdefs.h"
#ifdef VOICECHAT
#include "quakedef.h"
#ifdef _WIN32
#include "winquake.h"
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EMSGSIZE WSAEMSGSIZE
#define ECONNRESET WSAECONNRESET
#define ECONNABORTED WSAECONNABORTED
#define ECONNREFUSED WSAECONNREFUSED
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define qerrno WSAGetLastError()
#else
#define qerrno errno
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#ifdef sun
#include <sys/filio.h>
#endif
#ifdef NeXT
#include <libc.h>
#endif
#define closesocket close
#define ioctlsocket ioctl
#endif
#include "voicechat.h"
static int CLVS_socket;
static int SVVS_socket;
static qboolean SVVS_inited;
static qbyte inputbuffer[44100];
static int readpoint;
static qbyte outputbuffer[44100];
static int sendpoint;
/*
Protocol:
Sound chunk:
(char) data begins with codec id.
(short) followed by number of samples
(short) then size in bytes of chunk.
*/
#ifndef CLIENTONLY
void SVVC_ServerInit(void)
{
netadr_t adr;
struct sockaddr_in address;
unsigned long _true = true;
int i;
int port = PORT_SERVER;
if ((SVVS_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
Sys_Error ("FTP_TCP_OpenSocket: socket:", strerror(qerrno));
}
if (ioctlsocket (SVVS_socket, FIONBIO, &_true) == -1)
{
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO:", strerror(qerrno));
}
address.sin_family = AF_INET;
//ZOID -- check for interface binding option
if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) {
address.sin_addr.s_addr = inet_addr(com_argv[i+1]);
Con_TPrintf(TL_NETBINDINTERFACE,
inet_ntoa(address.sin_addr));
} else
address.sin_addr.s_addr = INADDR_ANY;
if (port == PORT_ANY)
address.sin_port = 0;
else
address.sin_port = htons((short)port);
if( bind (SVVS_socket, (void *)&address, sizeof(address)) == -1)
{
closesocket(SVVS_socket);
return;
}
listen(SVVS_socket, 3);
SVVS_inited = true;
Con_Printf("VC server is running\n");
SockadrToNetadr((struct sockaddr_qstorage*)&address, &adr);
Info_SetValueForKey(svs.info, "voiceaddress", NET_AdrToString(adr), MAX_SERVERINFO_STRING);
return;
}
//currently a dum forwarding/broadcasting mechanism
void SVVC_Frame (qboolean running)
{
struct sockaddr_in frm;
int size = sizeof(frm);
int newcl;
int i, j;
char buffer[1400];
if (!running)
return;
if (!SVVS_socket)
{
SVVC_ServerInit();
return;
}
newcl = accept(SVVS_socket, (struct sockaddr *)&frm, &size);
if (newcl != INVALID_SOCKET)
{
for (i = 0; i < MAX_CLIENTS; i++)
{
if (!svs.clients[i].voicechat.socket) //this really isn't the right way...
{
svs.clients[i].voicechat.socket = newcl;
break;
}
}
}
for (i = 0; i < MAX_CLIENTS; i++)
{
host_client = &svs.clients[i];
if (host_client->voicechat.socket)
{
size = recv(host_client->voicechat.socket, buffer, sizeof(buffer), 0);
if (size > 0)
{
for (j = 0; j < MAX_CLIENTS; j++)
{
if (j != i && svs.clients[j].voicechat.socket) //gotta be capable of receiving, and not the sender (that would be dumb).
send(svs.clients[j].voicechat.socket, buffer, size, 0);
}
}
}
}
}
#endif
#ifndef SERVERONLY
sfxcache_t *voicesoundbuffer[2];
sfx_t recordaudio[2] = {
{"recordaudio1",
{NULL, false},
NULL},
{"recordaudio2",
{NULL, false},
NULL}
};
int audiobuffer;
void SNDVC_Submit(int codec, qbyte *buffer, int samples, int freq, int width)
{
S_RawAudio(0, buffer, freq, samples, 1, width);
/*
soundcardinfo_t *cursndcard;
audiobuffer=0;
if (!recordaudio[audiobuffer].cache.data)
{
voicesoundbuffer[audiobuffer] = BZ_Malloc(44100*2+sizeof(sfxcache_t));
recordaudio[audiobuffer].cache.data = voicesoundbuffer[audiobuffer];
recordaudio[audiobuffer].cache.fake = true;
}
cursndcard = sndcardinfo;
if (!cursndcard)
{
Con_Printf("Accepting voice chat with no sound card\n");
return;
}
voicesoundbuffer[audiobuffer]->length = samples;
voicesoundbuffer[audiobuffer]->stereo = false;
voicesoundbuffer[audiobuffer]->speed = sndcardinfo->sn.speed;
voicesoundbuffer[audiobuffer]->width = width;
voicesoundbuffer[audiobuffer]->loopstart=-1;
// Con_DPrintf("Submit %i\n", (int)samples);
if (codec == 0) //codec 0 is special. (A straight copy)
ResampleSfx(&recordaudio[audiobuffer], freq, width, buffer);
else
{
qbyte *temp = BZ_Malloc(samples*width);
audiocodecs[codec].decode(buffer, (short*)temp, samples);
ResampleSfx(&recordaudio[audiobuffer], freq, width, temp);
BZ_Free(temp);
}
for (cursndcard = sndcardinfo; cursndcard; cursndcard=cursndcard->next)
{
if (0&&cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].sfx == &recordaudio[1-audiobuffer]) //other buffer is playing.
{
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].sfx = &recordaudio[audiobuffer];
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].end - voicesoundbuffer[1-audiobuffer]->length- cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].pos;
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end = cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end + samples;
if (cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos >= voicesoundbuffer[audiobuffer]->length)
{
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].pos = voicesoundbuffer[1-audiobuffer]->length;
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = 0;
Con_Printf("Sound out of sync\n");
}
}
else
{
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].sfx = &recordaudio[audiobuffer];
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = 0;
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end = cursndcard->paintedtime + samples;
}
}
audiobuffer = 1-audiobuffer;*/
}
void CLVC_SetServer (char *addy)
{
unsigned long _true = true;
struct sockaddr_qstorage from;
if ((CLVS_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
Sys_Error ("FTP_UDP_OpenSocket: socket: %s\n", strerror(qerrno));
}
{//quake routines using dns and stuff (Really, I wanna keep quake and ftp fairly seperate)
netadr_t qaddy;
if (!NET_StringToAdr (addy, &qaddy)) //server doesn't exist.
return;
if (qaddy.type != NA_IP) //Only TCP is supported.
return;
if (!qaddy.port)
qaddy.port = htons(PORT_SERVER);
NetadrToSockadr(&qaddy, &from);
}
//not yet non blocking.
if (connect(CLVS_socket, (struct sockaddr*)&from, sizeof(from)) == -1)
{
Con_Printf ("FTP_TCP_OpenSocket: connect: %i %s\n", qerrno, strerror(qerrno));
closesocket(CLVS_socket);
CLVS_socket = 0;
return;
}
if (ioctlsocket (CLVS_socket, FIONBIO, &_true) == -1) //now make it non blocking.
{
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO: %s\n", strerror(qerrno));
}
}
void CLVC_Disconnect (void)
{
closesocket(CLVS_socket);
CLVS_socket = 0;
}
void CLVC_Poll (void)
{
int codec;
int size;
if (!CLVS_socket)
return;
while (1)
{
size = recv(CLVS_socket, &inputbuffer[readpoint], sizeof(inputbuffer) - readpoint, 0);
if (size>0)
{
int samps;
int bytes;
readpoint+=size;
if (readpoint >= 1)
{
codec = *inputbuffer;
if (codec >= 0 && codec <= 127 && readpoint>=5) //just in case.
{
samps = LittleLong(*(signed short *)(inputbuffer+1));
bytes = LittleLong(*(unsigned short *)(inputbuffer+3));
// Con_DPrintf("read %i\n", size);
if (samps < 0) //something special
{
readpoint=0;
}
else
{
if (readpoint >= bytes+5)
{
if (codec == 1)
Con_Printf("Reading\n");
if (codec < audionumcodecs && audiocodecs[codec].decode)
{
SNDVC_Submit(codec, ((qbyte *)inputbuffer)+5, samps, 11025, 2);
readpoint -= bytes+5;
memmove(inputbuffer, &inputbuffer[readpoint+bytes+5], readpoint);
}
else
{
Con_Printf("Bad codec %i\n", (int)codec);
readpoint=0;
closesocket(CLVS_socket);
CLVS_socket = 0;
}
}
}
}
}
}
else if (readpoint >= sizeof(inputbuffer) || readpoint < 0)
{
Con_Printf("Too small buffer or extended client %i\n", (int)readpoint);
readpoint=0;
closesocket(CLVS_socket);
CLVS_socket = 0;
}
else
{
break;
}
}
}
void SNDVC_MicInput(qbyte *buffer, int samples, int freq, int width) //this correctly buffers data ready to be sent.
{
int sent;
qbyte codec;
unsigned short *sampleswritten;
samples/=width;
if (!CLVS_socket)
{
if (cls.state)
{
char *server;
server = Info_ValueForKey(cl.serverinfo, "voiceaddress");
if (*server)
CLVC_SetServer(server);
}
SNDVC_Submit(0, buffer, samples, freq, width);
return;
}
else if (!cls.state)
{
readpoint = 0;
sendpoint= 0;
SNDVC_Submit(0, buffer, samples, freq, width);
return;
}
SNDVC_Submit(0, buffer, samples, freq, width); //remembering at all times that we will not be allowed to hear it ourselves.
//add to send buffer.
if (samples > 0x7ffe)
samples = 0x7ffe;
if (sendpoint + samples*width+sizeof(unsigned char)+sizeof(short)+sizeof(*sampleswritten) < sizeof(outputbuffer))
{
// Con_DPrintf("sending %i\n", (int)samples);
codec = 1;
outputbuffer[sendpoint] = codec; sendpoint += sizeof(unsigned char);
*(unsigned short*)(&outputbuffer[sendpoint]) = samples; sendpoint += sizeof(unsigned short);
sampleswritten = (short *)&outputbuffer[sendpoint]; sendpoint += sizeof(*sampleswritten);
*sampleswritten = audiocodecs[codec].encode((short *)buffer, &outputbuffer[sendpoint], samples);
sendpoint += *sampleswritten;
}
else
{
Con_Printf("Connection overflowing\n");
}
//try and send it
sent = send(CLVS_socket, outputbuffer, sendpoint, 0);
if (sent > 0)
{
// Con_DPrintf("sent %i\n", (int)sent);
sendpoint -= sent;
}
else
{
CLVS_socket=0;
}
// SNDVC_Submit(buffer, samples, freq, width);
}
#endif
#endif