909 lines
29 KiB
C++
909 lines
29 KiB
C++
/*
|
|
* Original work: Copyright (c) 2014, Oculus VR, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
|
|
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
|
|
*
|
|
*
|
|
* Modified work: Copyright (c) 2017-2020, SLikeSoft UG (haftungsbeschränkt)
|
|
*
|
|
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
|
|
* license found in the license.txt file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "RakVoice.h"
|
|
#include "speex/speex.h"
|
|
#include "speex/speex_preprocess.h"
|
|
#include "slikenet/BitStream.h"
|
|
#include "slikenet/PacketPriority.h"
|
|
#include "slikenet/MessageIdentifiers.h"
|
|
#include "slikenet/BitStream.h"
|
|
#include "slikenet/peerinterface.h"
|
|
#include <stdlib.h>
|
|
#include "slikenet/GetTime.h"
|
|
|
|
#ifdef _DEBUG
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
using namespace SLNet;
|
|
|
|
//#define PRINT_DEBUG_INFO
|
|
|
|
#define SAMPLESIZE 2
|
|
|
|
#ifdef PRINT_DEBUG_INFO
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
int SLNet::VoiceChannelComp( const RakNetGUID &key, VoiceChannel * const &data )
|
|
{
|
|
if (key < data->guid)
|
|
return -1;
|
|
if (key == data->guid)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
RakVoice::RakVoice()
|
|
{
|
|
bufferedOutput=0;
|
|
defaultEncoderComplexity=2;
|
|
defaultVADState=true;
|
|
defaultDENOISEState=false;
|
|
defaultVBRState=false;
|
|
loopbackMode=false;
|
|
}
|
|
RakVoice::~RakVoice()
|
|
{
|
|
Deinit();
|
|
}
|
|
// #med - change member variable - then revert argument to sampleRate/bufferSizeBytes
|
|
void RakVoice::Init(unsigned short speexSampleRate, unsigned newBufferSizeBytes)
|
|
{
|
|
// Record the parameters
|
|
RakAssert(speexSampleRate==8000 || speexSampleRate==16000 || speexSampleRate==32000);
|
|
sampleRate=speexSampleRate;
|
|
bufferSizeBytes=newBufferSizeBytes;
|
|
bufferedOutputCount=newBufferSizeBytes/SAMPLESIZE;
|
|
bufferedOutput = (float*) rakMalloc_Ex(sizeof(float)*bufferedOutputCount, _FILE_AND_LINE_);
|
|
unsigned i;
|
|
for (i=0; i < bufferedOutputCount; i++)
|
|
bufferedOutput[i]=0.0f;
|
|
zeroBufferedOutput=false;
|
|
|
|
}
|
|
void RakVoice::Deinit(void)
|
|
{
|
|
// check pointer before free
|
|
if (bufferedOutput)
|
|
{
|
|
rakFree_Ex(bufferedOutput, _FILE_AND_LINE_ );
|
|
bufferedOutput = 0;
|
|
CloseAllChannels();
|
|
}
|
|
}
|
|
void RakVoice::SetLoopbackMode(bool enabled)
|
|
{
|
|
if (enabled)
|
|
{
|
|
Packet p;
|
|
SLNet::BitStream out;
|
|
out.Write((unsigned char)ID_RAKVOICE_OPEN_CHANNEL_REQUEST);
|
|
out.Write((int32_t)sampleRate);
|
|
p.data=out.GetData();
|
|
p.systemAddress= SLNet::UNASSIGNED_SYSTEM_ADDRESS;
|
|
p.guid=UNASSIGNED_RAKNET_GUID;
|
|
p.length=out.GetNumberOfBytesUsed();
|
|
OpenChannel(&p);
|
|
}
|
|
else
|
|
{
|
|
FreeChannelMemory(UNASSIGNED_RAKNET_GUID);
|
|
}
|
|
loopbackMode=enabled;
|
|
}
|
|
bool RakVoice::IsLoopbackMode(void) const
|
|
{
|
|
return loopbackMode;
|
|
}
|
|
void RakVoice::RequestVoiceChannel(RakNetGUID recipient)
|
|
{
|
|
// Send a reliable ordered message to the other system to open a voice channel
|
|
SLNet::BitStream out;
|
|
out.Write((unsigned char)ID_RAKVOICE_OPEN_CHANNEL_REQUEST);
|
|
out.Write((int32_t)sampleRate);
|
|
SendUnified(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,recipient,false);
|
|
}
|
|
void RakVoice::CloseVoiceChannel(RakNetGUID recipient)
|
|
{
|
|
FreeChannelMemory(recipient);
|
|
|
|
// Send a message to the remote system telling them to close the channel
|
|
SLNet::BitStream out;
|
|
out.Write((unsigned char)ID_RAKVOICE_CLOSE_CHANNEL);
|
|
SendUnified(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,recipient,false);
|
|
}
|
|
void RakVoice::CloseAllChannels(void)
|
|
{
|
|
SLNet::BitStream out;
|
|
out.Write((unsigned char)ID_RAKVOICE_CLOSE_CHANNEL);
|
|
|
|
// Free the memory for all channels
|
|
unsigned index;
|
|
for (index=0; index < voiceChannels.Size(); index++)
|
|
{
|
|
SendUnified(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,voiceChannels[index]->guid,false);
|
|
FreeChannelMemory(index,false);
|
|
}
|
|
|
|
voiceChannels.Clear(false, _FILE_AND_LINE_);
|
|
}
|
|
bool RakVoice::SendFrame(RakNetGUID recipient, void *inputBuffer)
|
|
{
|
|
bool objectExists;
|
|
unsigned index;
|
|
VoiceChannel *channel;
|
|
|
|
index = voiceChannels.GetIndexFromKey(recipient, &objectExists);
|
|
if (objectExists)
|
|
{
|
|
unsigned totalBufferSize;
|
|
unsigned remainingBufferSize;
|
|
|
|
channel=voiceChannels[index];
|
|
|
|
totalBufferSize=bufferSizeBytes * FRAME_OUTGOING_BUFFER_COUNT;
|
|
if (channel->outgoingWriteIndex >= channel->outgoingReadIndex)
|
|
remainingBufferSize=totalBufferSize-(channel->outgoingWriteIndex-channel->outgoingReadIndex);
|
|
else
|
|
remainingBufferSize=channel->outgoingReadIndex-channel->outgoingWriteIndex;
|
|
|
|
#ifdef _DEBUG
|
|
RakAssert(remainingBufferSize>0 && remainingBufferSize <= totalBufferSize);
|
|
// printf("SendFrame: buff=%i writeIndex=%i readIndex=%i\n",remainingBufferSize, channel->outgoingWriteIndex, channel->outgoingReadIndex);
|
|
|
|
//printf("Writing %i bytes to write offset %i. %i %i.\n", bufferSizeBytes, channel->outgoingWriteIndex, *((char*)inputBuffer+channel->outgoingWriteIndex), *((char*)inputBuffer+channel->outgoingWriteIndex+bufferSizeBytes-1));
|
|
#endif
|
|
|
|
// Copy encoded sound to the outgoing buffer for that channel. This has to be fast, since this function is likely to be called from a locked buffer
|
|
// I allocated the buffer to be a size multiple of bufferSizeBytes so don't have to watch for overflow on this line
|
|
memcpy(channel->outgoingBuffer + channel->outgoingWriteIndex, inputBuffer, bufferSizeBytes );
|
|
|
|
#ifdef _DEBUG
|
|
RakAssert(channel->outgoingWriteIndex+bufferSizeBytes <= totalBufferSize);
|
|
#endif
|
|
|
|
|
|
// Increment the write index, wrapping if needed.
|
|
channel->outgoingWriteIndex+=bufferSizeBytes;
|
|
#ifdef _DEBUG
|
|
// Verify that the write is aligned to the size of outgoingBuffer
|
|
RakAssert(channel->outgoingWriteIndex <= totalBufferSize);
|
|
#endif
|
|
if (channel->outgoingWriteIndex==totalBufferSize)
|
|
channel->outgoingWriteIndex=0;
|
|
|
|
if (bufferSizeBytes >= remainingBufferSize) // Would go past the current read position
|
|
{
|
|
#ifdef _DEBUG
|
|
// This is actually a warning - it means that FRAME_OUTGOING_BUFFER_COUNT wasn't big enough and old data is being overwritten
|
|
RakAssert(0);
|
|
#endif
|
|
// Force the read index up one block
|
|
channel->outgoingReadIndex=(channel->outgoingReadIndex+channel->speexOutgoingFrameSampleCount * SAMPLESIZE)%totalBufferSize;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool RakVoice::IsSendingVoiceDataTo(RakNetGUID recipient)
|
|
{
|
|
bool objectExists;
|
|
unsigned index;
|
|
index = voiceChannels.GetIndexFromKey(recipient, &objectExists);
|
|
|
|
// Free the memory for this channel
|
|
if (objectExists)
|
|
return voiceChannels[index]->isSendingVoiceData;
|
|
return false;
|
|
}
|
|
void RakVoice::ReceiveFrame(void *outputBuffer)
|
|
{
|
|
short *out = (short*)outputBuffer;
|
|
unsigned i;
|
|
// Convert the floats to final 16-bits output
|
|
for (i=0; i < bufferSizeBytes / SAMPLESIZE; i++)
|
|
{
|
|
if (bufferedOutput[i]>32767.0f)
|
|
out[i]=32767;
|
|
else if (bufferedOutput[i]<-32768.0f)
|
|
out[i]=-32768;
|
|
else
|
|
out[i]=(short)bufferedOutput[i];
|
|
}
|
|
|
|
// Done with this block. Zero all the values in Update
|
|
zeroBufferedOutput=true;
|
|
}
|
|
|
|
|
|
int RakVoice::GetSampleRate(void) const
|
|
{
|
|
return sampleRate;
|
|
}
|
|
|
|
int RakVoice::GetBufferSizeBytes(void) const
|
|
{
|
|
return bufferSizeBytes;
|
|
}
|
|
|
|
bool RakVoice::IsInitialized(void) const
|
|
{
|
|
// Use bufferedOutput to tell if the object was not initialized
|
|
return (bufferedOutput!=0);
|
|
}
|
|
|
|
RakPeerInterface* RakVoice::GetRakPeerInterface(void) const
|
|
{
|
|
return rakPeerInterface;
|
|
}
|
|
unsigned RakVoice::GetBufferedBytesToSend(RakNetGUID guid) const
|
|
{
|
|
bool objectExists;
|
|
VoiceChannel *channel;
|
|
unsigned totalBufferSize=bufferSizeBytes * FRAME_OUTGOING_BUFFER_COUNT;
|
|
if (guid!=UNASSIGNED_RAKNET_GUID)
|
|
{
|
|
unsigned index = voiceChannels.GetIndexFromKey(guid, &objectExists);
|
|
channel = voiceChannels[index];
|
|
if (objectExists)
|
|
{
|
|
if (channel->outgoingWriteIndex>=channel->outgoingReadIndex)
|
|
return channel->outgoingWriteIndex-channel->outgoingReadIndex;
|
|
else
|
|
return channel->outgoingWriteIndex + (totalBufferSize-channel->outgoingReadIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned total=0;
|
|
for (unsigned i=0; i < voiceChannels.Size(); i++)
|
|
{
|
|
channel=voiceChannels[i];
|
|
if (channel->outgoingWriteIndex>=channel->outgoingReadIndex)
|
|
total+=channel->outgoingWriteIndex-channel->outgoingReadIndex;
|
|
else
|
|
total+=channel->outgoingWriteIndex + (totalBufferSize-channel->outgoingReadIndex);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
unsigned RakVoice::GetBufferedBytesToReturn(RakNetGUID guid) const
|
|
{
|
|
bool objectExists;
|
|
VoiceChannel *channel;
|
|
unsigned totalBufferSize=bufferSizeBytes * FRAME_OUTGOING_BUFFER_COUNT;
|
|
if (guid!=UNASSIGNED_RAKNET_GUID)
|
|
{
|
|
unsigned index = voiceChannels.GetIndexFromKey(guid, &objectExists);
|
|
channel = voiceChannels[index];
|
|
if (objectExists)
|
|
{
|
|
if (channel->incomingReadIndex <= channel->incomingWriteIndex)
|
|
return channel->incomingWriteIndex-channel->incomingReadIndex;
|
|
else
|
|
return totalBufferSize-channel->incomingReadIndex+channel->incomingWriteIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned total=0;
|
|
for (unsigned i=0; i < voiceChannels.Size(); i++)
|
|
{
|
|
channel=voiceChannels[i];
|
|
if (channel->incomingReadIndex <= channel->incomingWriteIndex)
|
|
total+=channel->incomingWriteIndex-channel->incomingReadIndex;
|
|
else
|
|
total+=totalBufferSize-channel->incomingReadIndex+channel->incomingWriteIndex;
|
|
}
|
|
return total;
|
|
}
|
|
return 0;
|
|
}
|
|
void RakVoice::OnShutdown(void)
|
|
{
|
|
CloseAllChannels();
|
|
}
|
|
|
|
void RakVoice::Update(void)
|
|
{
|
|
unsigned i,j, bytesAvailable, speexFramesAvailable, speexBlockSize;
|
|
unsigned bytesWaitingToReturn;
|
|
int bytesWritten;
|
|
VoiceChannel *channel;
|
|
char *inputBuffer;
|
|
char tempOutput[2048];
|
|
// 1 byte for ID, and 2 bytes(short) for Message number
|
|
static const int headerSize=sizeof(unsigned char) + sizeof(unsigned short);
|
|
// First byte is ID for RakNet
|
|
tempOutput[0]=ID_RAKVOICE_DATA;
|
|
|
|
SLNet::TimeMS currentTime = SLNet::GetTimeMS();
|
|
|
|
// Size of VoiceChannel::incomingBuffer and VoiceChannel::outgoingBuffer arrays
|
|
unsigned totalBufferSize=bufferSizeBytes * FRAME_OUTGOING_BUFFER_COUNT;
|
|
|
|
// Allow all channels to write, and set the output to zero in preparation
|
|
if (zeroBufferedOutput)
|
|
{
|
|
for (i=0; i < bufferedOutputCount; i++)
|
|
bufferedOutput[i]=0.0f;
|
|
for (i=0; i < voiceChannels.Size(); i++)
|
|
voiceChannels[i]->copiedOutgoingBufferToBufferedOutput=false;
|
|
zeroBufferedOutput=false;
|
|
}
|
|
|
|
// For each channel
|
|
for (i=0; i < voiceChannels.Size(); i++)
|
|
{
|
|
channel=voiceChannels[i];
|
|
|
|
if (currentTime - channel->lastSend > 50) // Throttle to 20 sends a second
|
|
{
|
|
channel->isSendingVoiceData=false;
|
|
|
|
// Circular buffer so I have to do this to count how many bytes are available
|
|
if (channel->outgoingWriteIndex>=channel->outgoingReadIndex)
|
|
bytesAvailable=channel->outgoingWriteIndex-channel->outgoingReadIndex;
|
|
else
|
|
bytesAvailable=channel->outgoingWriteIndex + (totalBufferSize-channel->outgoingReadIndex);
|
|
|
|
// Speex returns how many frames it encodes per block. Each frame is of byte length sampleSize.
|
|
speexBlockSize = channel->speexOutgoingFrameSampleCount * SAMPLESIZE;
|
|
|
|
#ifdef PRINT_DEBUG_INFO
|
|
static int lastPrint=0;
|
|
if (i==0 && currentTime-lastPrint > 2000)
|
|
{
|
|
lastPrint=currentTime;
|
|
unsigned bytesWaitingToReturn;
|
|
if (channel->incomingReadIndex <= channel->incomingWriteIndex)
|
|
bytesWaitingToReturn=channel->incomingWriteIndex-channel->incomingReadIndex;
|
|
else
|
|
bytesWaitingToReturn=totalBufferSize-channel->incomingReadIndex+channel->incomingWriteIndex;
|
|
|
|
printf("%i bytes to send. incomingMessageNumber=%i. bytesWaitingToReturn=%i.\n", bytesAvailable, channel->incomingMessageNumber, bytesWaitingToReturn );
|
|
}
|
|
#endif
|
|
|
|
#ifdef _TEST_LOOPBACK
|
|
/*
|
|
if (bufferSizeBytes<bytesAvailable)
|
|
{
|
|
printf("Update: bytesAvailable=%i writeIndex=%i readIndex=%i\n",bytesAvailable, channel->outgoingWriteIndex, channel->outgoingReadIndex);
|
|
memcpy(channel->incomingBuffer + channel->incomingWriteIndex, channel->outgoingBuffer+channel->outgoingReadIndex, bufferSizeBytes);
|
|
channel->incomingWriteIndex=(channel->incomingWriteIndex+bufferSizeBytes) % totalBufferSize;
|
|
channel->outgoingReadIndex=(channel->outgoingReadIndex+bufferSizeBytes) % totalBufferSize;
|
|
}
|
|
return;
|
|
*/
|
|
#endif
|
|
|
|
// Find out how many frames we can read out of the buffer for speex to encode and send these out.
|
|
speexFramesAvailable = bytesAvailable / speexBlockSize;
|
|
|
|
// Encode all available frames and send them unreliable sequenced
|
|
if (speexFramesAvailable > 0)
|
|
{
|
|
SpeexBits speexBits;
|
|
speex_bits_init(&speexBits);
|
|
while (speexFramesAvailable-- > 0)
|
|
{
|
|
speex_bits_reset(&speexBits);
|
|
|
|
// If the input data would wrap around the buffer, copy it to another buffer first
|
|
if (channel->outgoingReadIndex + speexBlockSize >= totalBufferSize)
|
|
{
|
|
#ifdef _DEBUG
|
|
RakAssert(speexBlockSize < 2048-1);
|
|
#endif
|
|
unsigned t;
|
|
for (t=0; t < speexBlockSize; t++)
|
|
tempOutput[t+headerSize]=channel->outgoingBuffer[t%totalBufferSize];
|
|
inputBuffer=tempOutput+headerSize;
|
|
}
|
|
else
|
|
inputBuffer=channel->outgoingBuffer+channel->outgoingReadIndex;
|
|
|
|
#ifdef _DEBUG
|
|
/*
|
|
printf("In: ");
|
|
if (shortSampleType)
|
|
{
|
|
short *blah = (short*) inputBuffer;
|
|
for (int p=0; p < 5; p++)
|
|
{
|
|
printf("%.i ", blah[p]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float *blah = (float*) inputBuffer;
|
|
for (int p=0; p < 5; p++)
|
|
{
|
|
printf("%.3f ", blah[p]);
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
*/
|
|
#endif
|
|
int is_speech=1;
|
|
|
|
// Run preprocessor if required
|
|
if (defaultDENOISEState||defaultVADState){
|
|
is_speech=speex_preprocess((SpeexPreprocessState*)channel->pre_state,(spx_int16_t*) inputBuffer, nullptr);
|
|
}
|
|
|
|
if ((is_speech)||(!defaultVADState)){
|
|
is_speech = speex_encode_int(channel->enc_state, (spx_int16_t*) inputBuffer, &speexBits);
|
|
}
|
|
|
|
channel->outgoingReadIndex=(channel->outgoingReadIndex+speexBlockSize)%totalBufferSize;
|
|
|
|
// If no speech detected, don't send this frame
|
|
if ((!is_speech)&&(defaultVADState)){
|
|
continue;
|
|
}
|
|
|
|
channel->isSendingVoiceData=true;
|
|
|
|
#ifdef _DEBUG
|
|
// printf("Update: bytesAvailable=%i writeIndex=%i readIndex=%i\n",bytesAvailable, channel->outgoingWriteIndex, channel->outgoingReadIndex);
|
|
#endif
|
|
|
|
bytesWritten = speex_bits_write(&speexBits, tempOutput+headerSize, 2048-headerSize);
|
|
#ifdef _DEBUG
|
|
// If this assert hits then you need to increase the size of the temp buffer, but this is really a bug because
|
|
// voice packets should never be bigger than a few hundred bytes.
|
|
RakAssert(bytesWritten!=2048-headerSize);
|
|
#endif
|
|
|
|
// static int bytesSent=0;
|
|
// bytesSent+= bytesWritten+headerSize;
|
|
// printf("bytesSent=%i\n", bytesSent);
|
|
|
|
#ifdef PRINT_DEBUG_INFO
|
|
static int voicePacketsSent=0;
|
|
printf("%i ", voicePacketsSent++);
|
|
#endif
|
|
|
|
// at +1, because the first byte in the buffer has the ID for RakNet.
|
|
memcpy(tempOutput+1, &channel->outgoingMessageNumber, sizeof(unsigned short));
|
|
channel->outgoingMessageNumber++;
|
|
SLNet::BitStream tempOutputBs((unsigned char*) tempOutput,bytesWritten+headerSize,false);
|
|
SendUnified(&tempOutputBs, HIGH_PRIORITY, UNRELIABLE,0,channel->guid,false);
|
|
|
|
if (loopbackMode)
|
|
{
|
|
Packet p;
|
|
p.length=bytesWritten+1;
|
|
p.data=(unsigned char*)tempOutput;
|
|
p.guid=channel->guid;
|
|
p.systemAddress=rakPeerInterface->GetSystemAddressFromGuid(p.guid);
|
|
OnVoiceData(&p);
|
|
}
|
|
}
|
|
|
|
speex_bits_destroy(&speexBits);
|
|
channel->lastSend=currentTime;
|
|
}
|
|
}
|
|
|
|
// As sound buffer blocks fill up, I add their values to RakVoice::bufferedOutput . Then when the user calls ReceiveFrame they get that value, already
|
|
// processed. This is necessary because that function needs to run as fast as possible so I remove all processing there that I can. Otherwise the sound
|
|
// plays back distorted and popping
|
|
if (channel->copiedOutgoingBufferToBufferedOutput==false)
|
|
{
|
|
if (channel->incomingReadIndex <= channel->incomingWriteIndex)
|
|
bytesWaitingToReturn=channel->incomingWriteIndex-channel->incomingReadIndex;
|
|
else
|
|
bytesWaitingToReturn=totalBufferSize-channel->incomingReadIndex+channel->incomingWriteIndex;
|
|
|
|
if (bytesWaitingToReturn==0)
|
|
{
|
|
channel->bufferOutput=true;
|
|
}
|
|
else if (channel->bufferOutput==false || bytesWaitingToReturn > bufferSizeBytes*2)
|
|
{
|
|
// Block running this again until the user calls ReceiveFrame since every call to ReceiveFrame only gets zero or one output blocks from
|
|
// each channel
|
|
channel->copiedOutgoingBufferToBufferedOutput=true;
|
|
|
|
// Stop buffering output. We won't start buffering again until there isn't enough data to read.
|
|
channel->bufferOutput=false;
|
|
|
|
// Cap to the size of the output buffer. But we do write less if less is available, with the rest silence
|
|
if (bytesWaitingToReturn > bufferSizeBytes)
|
|
{
|
|
bytesWaitingToReturn=bufferSizeBytes;
|
|
}
|
|
else
|
|
{
|
|
// Align the write index so when we increment the partial block read (which is always aligned) it computes out to 0 bytes waiting
|
|
channel->incomingWriteIndex=channel->incomingReadIndex+bufferSizeBytes;
|
|
if (channel->incomingWriteIndex==totalBufferSize)
|
|
channel->incomingWriteIndex=0;
|
|
}
|
|
|
|
short *in = (short *) (channel->incomingBuffer+channel->incomingReadIndex);
|
|
for (j=0; j < bytesWaitingToReturn / SAMPLESIZE; j++)
|
|
{
|
|
// Write short to float so if the range goes over the range of a float we can still add and subtract the correct final value.
|
|
// It will be clamped at the end
|
|
bufferedOutput[j]+=in[j%(totalBufferSize/SAMPLESIZE)];
|
|
}
|
|
|
|
// Update the read index. Always update by bufferSizeBytes, not bytesWaitingToReturn.
|
|
// if bytesWaitingToReturn < bufferSizeBytes then the rest is silence since this means the buffer ran out or we stopped sending.
|
|
channel->incomingReadIndex+=bufferSizeBytes;
|
|
if (channel->incomingReadIndex==totalBufferSize)
|
|
channel->incomingReadIndex=0;
|
|
|
|
// printf("%f %f\n", channel->incomingReadIndex/(float)bufferSizeBytes, channel->incomingWriteIndex/(float)bufferSizeBytes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PluginReceiveResult RakVoice::OnReceive(Packet *packet)
|
|
{
|
|
RakAssert(packet);
|
|
|
|
switch (packet->data[0])
|
|
{
|
|
case ID_RAKVOICE_OPEN_CHANNEL_REQUEST:
|
|
OnOpenChannelRequest(packet);
|
|
break;
|
|
case ID_RAKVOICE_OPEN_CHANNEL_REPLY:
|
|
OnOpenChannelReply(packet);
|
|
break;
|
|
case ID_RAKVOICE_CLOSE_CHANNEL:
|
|
FreeChannelMemory(packet->guid);
|
|
break;
|
|
case ID_RAKVOICE_DATA:
|
|
OnVoiceData(packet);
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
}
|
|
|
|
return RR_CONTINUE_PROCESSING;
|
|
}
|
|
void RakVoice::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
|
|
{
|
|
(void)systemAddress;
|
|
|
|
if (lostConnectionReason==LCR_CLOSED_BY_USER)
|
|
CloseVoiceChannel(rakNetGUID);
|
|
else
|
|
FreeChannelMemory(rakNetGUID);
|
|
}
|
|
|
|
void RakVoice::OnOpenChannelRequest(Packet *packet)
|
|
{
|
|
if (voiceChannels.HasData(packet->guid))
|
|
return;
|
|
|
|
// If the system is not initialized, just return
|
|
if (bufferedOutput==0)
|
|
return;
|
|
|
|
OpenChannel(packet);
|
|
|
|
SLNet::BitStream out;
|
|
out.Write((unsigned char)ID_RAKVOICE_OPEN_CHANNEL_REPLY);
|
|
out.Write((int32_t)sampleRate);
|
|
SendUnified(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,packet->systemAddress,false);
|
|
}
|
|
void RakVoice::OnOpenChannelReply(Packet *packet)
|
|
{
|
|
if (voiceChannels.HasData(packet->guid))
|
|
return;
|
|
OpenChannel(packet);
|
|
}
|
|
void RakVoice::OpenChannel(Packet *packet)
|
|
{
|
|
SLNet::BitStream in(packet->data, packet->length, false);
|
|
in.IgnoreBits(8);
|
|
|
|
FreeChannelMemory(packet->guid);
|
|
|
|
VoiceChannel *channel= SLNet::OP_NEW<VoiceChannel>( _FILE_AND_LINE_ );
|
|
channel->guid=packet->guid;
|
|
channel->isSendingVoiceData=false;
|
|
int newSampleRate;
|
|
in.Read(newSampleRate);
|
|
channel->remoteSampleRate=newSampleRate;
|
|
|
|
if (channel->remoteSampleRate!=8000 && channel->remoteSampleRate!=16000 && channel->remoteSampleRate!=32000)
|
|
{
|
|
#ifdef _DEBUG
|
|
RakAssert(0);
|
|
#endif
|
|
SLNet::OP_DELETE(channel, _FILE_AND_LINE_);
|
|
return;
|
|
}
|
|
|
|
if (newSampleRate==8000)
|
|
channel->enc_state=speex_encoder_init(&speex_nb_mode);
|
|
else if (newSampleRate==16000)
|
|
channel->enc_state=speex_encoder_init(&speex_wb_mode);
|
|
else // 32000
|
|
channel->enc_state=speex_encoder_init(&speex_uwb_mode);
|
|
|
|
if (channel->remoteSampleRate==8000)
|
|
channel->dec_state=speex_decoder_init(&speex_nb_mode);
|
|
else if (channel->remoteSampleRate==16000)
|
|
channel->dec_state=speex_decoder_init(&speex_wb_mode);
|
|
else // 32000
|
|
channel->dec_state=speex_decoder_init(&speex_uwb_mode);
|
|
|
|
// make sure encoder and decoder are created
|
|
RakAssert((channel->enc_state)&&(channel->dec_state));
|
|
|
|
int ret;
|
|
ret=speex_encoder_ctl(channel->enc_state, SPEEX_GET_FRAME_SIZE, &channel->speexOutgoingFrameSampleCount);
|
|
RakAssert(ret==0);
|
|
channel->outgoingBuffer = (char*) rakMalloc_Ex(bufferSizeBytes * FRAME_OUTGOING_BUFFER_COUNT, _FILE_AND_LINE_);
|
|
channel->outgoingReadIndex=0;
|
|
channel->outgoingWriteIndex=0;
|
|
channel->bufferOutput=true;
|
|
channel->outgoingMessageNumber=0;
|
|
channel->copiedOutgoingBufferToBufferedOutput=false;
|
|
|
|
ret=speex_decoder_ctl(channel->dec_state, SPEEX_GET_FRAME_SIZE, &channel->speexIncomingFrameSampleCount);
|
|
RakAssert(ret==0);
|
|
channel->incomingBuffer = (char*) rakMalloc_Ex(bufferSizeBytes * FRAME_INCOMING_BUFFER_COUNT, _FILE_AND_LINE_);
|
|
channel->incomingReadIndex=0;
|
|
channel->incomingWriteIndex=0;
|
|
channel->lastSend=0;
|
|
channel->incomingMessageNumber=0;
|
|
|
|
// Initialize preprocessor
|
|
channel->pre_state = speex_preprocess_state_init(channel->speexOutgoingFrameSampleCount, newSampleRate);
|
|
RakAssert(channel->pre_state);
|
|
|
|
// Set encoder default parameters
|
|
SetEncoderParameter(channel->enc_state, SPEEX_SET_VBR, (defaultVBRState) ? 1 : 0 );
|
|
SetEncoderParameter(channel->enc_state, SPEEX_SET_COMPLEXITY, defaultEncoderComplexity);
|
|
// Set preprocessor default parameters
|
|
SetPreprocessorParameter(channel->pre_state, SPEEX_PREPROCESS_SET_DENOISE, (defaultDENOISEState) ? 1 : 2);
|
|
SetPreprocessorParameter(channel->pre_state, SPEEX_PREPROCESS_SET_VAD, (defaultVADState) ? 1 : 2);
|
|
|
|
voiceChannels.Insert(packet->guid, channel, true, _FILE_AND_LINE_);
|
|
}
|
|
|
|
|
|
void RakVoice::SetEncoderParameter(void* enc_state, int vartype, int val)
|
|
{
|
|
if (enc_state){
|
|
// Set parameter for just one encoder
|
|
SLNET_VERIFY(speex_encoder_ctl(enc_state, vartype, &val) == 0);
|
|
} else {
|
|
// Set parameter for all encoders
|
|
for (unsigned int index=0; index < voiceChannels.Size(); index++)
|
|
{
|
|
SLNET_VERIFY(speex_encoder_ctl(voiceChannels[index]->enc_state, vartype, &val) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RakVoice::SetPreprocessorParameter(void* pre_state, int vartype, int val)
|
|
{
|
|
if (pre_state){
|
|
// Set parameter for just one preprocessor
|
|
SLNET_VERIFY(speex_preprocess_ctl((SpeexPreprocessState*)pre_state, vartype, &val) == 0);
|
|
} else {
|
|
// Set parameter for all decoders
|
|
for (unsigned int index=0; index < voiceChannels.Size(); index++)
|
|
{
|
|
SLNET_VERIFY(speex_preprocess_ctl((SpeexPreprocessState*)voiceChannels[index]->pre_state, vartype, &val) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RakVoice::SetEncoderComplexity(int complexity)
|
|
{
|
|
RakAssert((complexity>=0)&&(complexity<=10));
|
|
SetEncoderParameter(nullptr, SPEEX_SET_COMPLEXITY, complexity);
|
|
defaultEncoderComplexity = complexity;
|
|
}
|
|
void RakVoice::SetVAD(bool enable)
|
|
{
|
|
SetPreprocessorParameter(nullptr, SPEEX_PREPROCESS_SET_VAD, (enable)? 1 : 2);
|
|
defaultVADState = enable;
|
|
}
|
|
void RakVoice::SetNoiseFilter(bool enable)
|
|
{
|
|
SetPreprocessorParameter(nullptr, SPEEX_PREPROCESS_SET_DENOISE, (enable) ? 1 : 2);
|
|
defaultDENOISEState = enable;
|
|
}
|
|
void RakVoice::SetVBR(bool enable)
|
|
{
|
|
SetEncoderParameter(nullptr, SPEEX_SET_VBR, (enable) ? 1 : 0);
|
|
defaultVBRState = enable;
|
|
}
|
|
|
|
int RakVoice::GetEncoderComplexity(void)
|
|
{
|
|
return defaultEncoderComplexity;
|
|
}
|
|
bool RakVoice::IsVADActive(void)
|
|
{
|
|
return defaultVADState;
|
|
}
|
|
bool RakVoice::IsNoiseFilterActive()
|
|
{
|
|
return defaultDENOISEState;
|
|
}
|
|
bool RakVoice::IsVBRActive()
|
|
{
|
|
return defaultVBRState;
|
|
}
|
|
|
|
|
|
void RakVoice::FreeChannelMemory(RakNetGUID recipient)
|
|
{
|
|
bool objectExists;
|
|
unsigned index;
|
|
index = voiceChannels.GetIndexFromKey(recipient, &objectExists);
|
|
|
|
// Free the memory for this channel
|
|
if (objectExists)
|
|
{
|
|
FreeChannelMemory(index, true);
|
|
}
|
|
}
|
|
void RakVoice::FreeChannelMemory(unsigned index, bool removeIndex)
|
|
{
|
|
VoiceChannel *channel;
|
|
channel=voiceChannels[index];
|
|
speex_encoder_destroy(channel->enc_state);
|
|
speex_decoder_destroy(channel->dec_state);
|
|
speex_preprocess_state_destroy((SpeexPreprocessState*)channel->pre_state);
|
|
rakFree_Ex(channel->incomingBuffer, _FILE_AND_LINE_ );
|
|
rakFree_Ex(channel->outgoingBuffer, _FILE_AND_LINE_ );
|
|
SLNet::OP_DELETE(channel, _FILE_AND_LINE_);
|
|
if (removeIndex)
|
|
voiceChannels.RemoveAtIndex(index);
|
|
}
|
|
void RakVoice::OnVoiceData(Packet *packet)
|
|
{
|
|
bool objectExists;
|
|
unsigned index;
|
|
unsigned short packetMessageNumber, messagesSkipped;
|
|
VoiceChannel *channel;
|
|
char tempOutput[2048];
|
|
unsigned int i;
|
|
// 1 byte for ID, 2 bytes(short) for message number
|
|
static const int headerSize=sizeof(unsigned char) + sizeof(unsigned short);
|
|
|
|
index = voiceChannels.GetIndexFromKey(packet->guid, &objectExists);
|
|
if (objectExists)
|
|
{
|
|
SpeexBits speexBits;
|
|
speex_bits_init(&speexBits);
|
|
channel=voiceChannels[index];
|
|
memcpy(&packetMessageNumber, packet->data+1, sizeof(unsigned short));
|
|
|
|
// Intentional overflow
|
|
messagesSkipped=packetMessageNumber-channel->incomingMessageNumber;
|
|
if (messagesSkipped > ((unsigned short)-1)/2)
|
|
{
|
|
#ifdef PRINT_DEBUG_INFO
|
|
printf("--- UNDERFLOW ---\n");
|
|
#endif
|
|
// Underflow, just ignore it
|
|
return;
|
|
}
|
|
#ifdef PRINT_DEBUG_INFO
|
|
if (messagesSkipped>0)
|
|
printf("%i messages skipped\n", messagesSkipped);
|
|
#endif
|
|
// Don't do more than 100 ms of messages skipped. Discard the rest.
|
|
int maxSkip = (int)(100.0f / (float) sampleRate);
|
|
for (i=0; i < (unsigned) messagesSkipped && i < (unsigned) maxSkip; i++)
|
|
{
|
|
speex_decode_int(channel->dec_state, 0, (spx_int16_t*)tempOutput);
|
|
|
|
// Write to buffer a 'message skipped' interpolation
|
|
WriteOutputToChannel(channel, tempOutput);
|
|
}
|
|
|
|
channel->incomingMessageNumber=packetMessageNumber+1;
|
|
|
|
// Write to incomingBuffer the decoded data
|
|
speex_bits_read_from(&speexBits, (char*)(packet->data+headerSize), packet->length-headerSize);
|
|
speex_decode_int(channel->dec_state, &speexBits, (spx_int16_t*)tempOutput);
|
|
|
|
#ifdef _DEBUG
|
|
{
|
|
/*
|
|
printf("Out: ");
|
|
if (channel->remoteIsShortSampleType)
|
|
{
|
|
short *blah = (short*) tempOutput;
|
|
for (int p=0; p < 5; p++)
|
|
{
|
|
printf("%.i ", blah[p]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float *blah = (float*) tempOutput;
|
|
for (int p=0; p < 5; p++)
|
|
{
|
|
printf("%.3f ", blah[p]);
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
// Write to buffer
|
|
WriteOutputToChannel(channel, tempOutput);
|
|
|
|
speex_bits_destroy(&speexBits);
|
|
}
|
|
}
|
|
void RakVoice::WriteOutputToChannel(VoiceChannel *channel, char *dataToWrite)
|
|
{
|
|
unsigned totalBufferSize;
|
|
unsigned remainingBufferSize;
|
|
unsigned speexBlockSize;
|
|
|
|
totalBufferSize=bufferSizeBytes * FRAME_INCOMING_BUFFER_COUNT;
|
|
if (channel->incomingWriteIndex >= channel->incomingReadIndex)
|
|
remainingBufferSize=totalBufferSize-(channel->incomingWriteIndex-channel->incomingReadIndex);
|
|
else
|
|
remainingBufferSize=channel->incomingReadIndex-channel->incomingWriteIndex;
|
|
|
|
// Speex returns how many frames it encodes per block. Each frame is of byte length sampleSize.
|
|
speexBlockSize = channel->speexIncomingFrameSampleCount * SAMPLESIZE;
|
|
|
|
if (channel->incomingWriteIndex+speexBlockSize <= totalBufferSize)
|
|
{
|
|
memcpy(channel->incomingBuffer + channel->incomingWriteIndex, dataToWrite, speexBlockSize);
|
|
}
|
|
else
|
|
{
|
|
memcpy(channel->incomingBuffer + channel->incomingWriteIndex, dataToWrite, totalBufferSize-channel->incomingWriteIndex);
|
|
memcpy(channel->incomingBuffer, dataToWrite, speexBlockSize-(totalBufferSize-channel->incomingWriteIndex));
|
|
}
|
|
channel->incomingWriteIndex=(channel->incomingWriteIndex+speexBlockSize) % totalBufferSize;
|
|
|
|
|
|
#ifdef _DEBUG
|
|
//printf("WriteOutputToChannel: buff=%i writeIndex=%i readIndex=%i\n",remainingBufferSize, channel->incomingWriteIndex, channel->incomingReadIndex);
|
|
#endif
|
|
|
|
if (bufferSizeBytes >= remainingBufferSize) // Would go past the current read position
|
|
{
|
|
#ifdef _DEBUG
|
|
// This is actually a warning - it means that FRAME_INCOMING_BUFFER_COUNT wasn't big enough and old data is being overwritten
|
|
RakAssert(0);
|
|
#endif
|
|
// Force the read index up one block
|
|
channel->incomingReadIndex+=bufferSizeBytes;
|
|
if (channel->incomingReadIndex==totalBufferSize)
|
|
channel->incomingReadIndex=0;
|
|
}
|
|
}
|