// inputBuffer and outputBuffer is an array of SAMPLE of count framesPerBuffer // A sample is one unit of sound. // The sample rate is the number of sound samples taken per second // I think one frame is a set of samples equal to the number of channels. I'm not sure though or how that sample is arranged. static int PACallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData ) { if (inputBuffer && !mute) { // TODO - if the input data is mostly silence, don't send and save the bandwidth. #ifndef _TEST_LOOPBACK unsigned i; for (i=0; i < rakPeer->GetMaximumNumberOfPeers(); i++) { rakVoice.SendFrame(rakPeer->GetGUIDFromIndex(i), inputBuffer); } #else rakVoice.SendFrame(UNASSIGNED_SYSTEM_ADDRESS, inputBuffer); #endif } rakVoice.ReceiveFrame(outputBuffer); return 0; }
// Prints the current encoder parameters void PrintParameters(void) { printf("\nComplexity=%3d Noise filter=%3s VAD=%3s VBR=%3s\n" ,rakVoice.GetEncoderComplexity() ,(rakVoice.IsNoiseFilterActive()) ? "ON" : "OFF" ,(rakVoice.IsVADActive()) ? "ON" : "OFF" ,(rakVoice.IsVBRActive()) ? "ON" : "OFF"); }
// Keeps a record of the last 20 calls, to give a faster response to traffic change void LogStats(){ const int numStats = 20; static myStat data[numStats]; for(int i=0; i<=numStats-2; i++){ data[i] = data[i+1]; } RakNetStatistics *rss=rakPeer->GetStatistics(rakPeer->GetSystemAddressFromIndex(0)); unsigned int currTime = RakNet::GetTime(); data[numStats-1].time = currTime; data[numStats-1].bitsSent = rss->totalBitsSent; data[numStats-1].bitsRec = rss->bitsReceived; float totalTime = (data[numStats-1].time - data[0].time) / 1000.f ; unsigned int totalBitsSent = data[numStats-1].bitsSent - data[0].bitsSent; unsigned int totalBitsRec = data[numStats-1].bitsRec - data[0].bitsRec; float bpsSent = totalBitsSent/totalTime; float bpsRec = totalBitsRec/totalTime; float avgBpsSent = rss->totalBitsSent/((currTime-rss->connectionStartTime)/1000.0f); float avgBpsRec = rss->bitsReceived/((currTime-rss->connectionStartTime)/1000.0f); printf("avgKbpsSent=%02.1f avgKbpsRec=%02.1f kbpsSent=%02.1f kbpsRec=%02.1f \r", avgBpsSent/1000, avgBpsRec/1000, bpsSent/1000 , bpsRec/1000, rakVoice.GetBufferedBytesToReturn(UNASSIGNED_SYSTEM_ADDRESS)); //printf("MsgBuf=%6i SndBuf=%10i RcvBuf=%10i \r", rakVoice.GetRakPeerInterface()->GetStatistics(UNASSIGNED_SYSTEM_ADDRESS)->messageSendBuffer[HIGH_PRIORITY], rakVoice.GetBufferedBytesToSend(UNASSIGNED_SYSTEM_ADDRESS), rakVoice.GetBufferedBytesToReturn(UNASSIGNED_SYSTEM_ADDRESS)); }
int main(void) { printf("A sample on how to use RakVoice together with DirectSound.\n"); printf("You need a microphone for this sample.\n"); printf("RakVoice relies on Speex for voice encoding and decoding.\n"); printf("See DependentExtensions/RakVoice/speex-1.2beta3 for speex projects.\n"); printf("For windows, I had to define HAVE_CONFIG_H, include win32/config.h,\n"); printf("and include the files under libspeex, except those that start with test.\n"); printf("Difficulty: Advanced\n\n"); bool mute=false; bool quit; char ch; char port[256]; rakPeer = RakNetworkFactory::GetRakPeerInterface(); #if defined(INTERACTIVE) printf("Enter local port: "); gets(port); if (port[0]==0) #endif strcpy(port, "60000"); SocketDescriptor socketDescriptor(atoi(port),0); rakPeer->Startup(4, 30, &socketDescriptor, 1); rakPeer->SetMaximumIncomingConnections(4); rakPeer->AttachPlugin(&rakVoice); rakVoice.Init(SAMPLE_RATE, FRAMES_PER_BUFFER*sizeof(SAMPLE)); // Initialize our connection with DirectSound if (!DSoundVoiceAdapter::Instance()->SetupAdapter(&rakVoice, GetConsoleHwnd(), DSSCL_EXCLUSIVE)) { printf("An error occurred while initializing DirectSound.\n"); exit(-1); } Packet *p; quit=false; #if defined(INTERACTIVE) printf("(Q)uit. (C)onnect. (D)isconnect. (M)ute. ' ' for stats.\n"); printf("(+/-)encoder complexity. (N)oise filter on/off. (V)AD on/off. (B)vbr on/off.\n"); #else rakPeer->Connect("1.1.1.1", 60000, 0,0); #endif PrintParameters(); while (!quit) { #if defined(INTERACTIVE) if (kbhit()) { ch=getch(); if (ch=='+'){ // Increase encoder complexity int v = rakVoice.GetEncoderComplexity(); if (v<10) rakVoice.SetEncoderComplexity(v+1); PrintParameters(); } else if (ch=='-'){ // Decrease encoder complexity int v = rakVoice.GetEncoderComplexity(); if (v>0) rakVoice.SetEncoderComplexity(v-1); PrintParameters(); } else if (ch=='n'){ // Turn on/off noise filter rakVoice.SetNoiseFilter(!rakVoice.IsNoiseFilterActive()); PrintParameters(); } else if (ch=='v') { // Turn on/off Voice detection rakVoice.SetVAD(!rakVoice.IsVADActive()); PrintParameters(); } else if (ch=='b') { // Turn on/off VBR rakVoice.SetVBR(!rakVoice.IsVBRActive()); PrintParameters(); } else if (ch=='y') { quit=true; } else if (ch=='c') { char ip[256]; printf("\nEnter IP of remote system: "); gets(ip); if (ip[0]==0) strcpy(ip, "127.0.0.1"); printf("\nEnter port of remote system: "); gets(port); if (port[0]==0) strcpy(port, "60000"); rakPeer->Connect(ip, atoi(port), 0,0); } else if (ch=='m') { mute=!mute; DSoundVoiceAdapter::Instance()->SetMute(mute); if (mute) printf("\nNow muted.\n"); else printf("\nNo longer muted.\n"); } else if (ch=='d') { rakPeer->Shutdown(100,0); } else if (ch==' ') { char message[2048]; RakNetStatistics *rss=rakPeer->GetStatistics(rakPeer->GetSystemAddressFromIndex(0)); StatisticsToString(rss, message, 2); printf("%s", message); } else if (ch=='q') quit=true; ch=0; } #endif p=rakPeer->Receive(); while (p) { if (p->data[0]==ID_CONNECTION_REQUEST_ACCEPTED) { printf("\nID_CONNECTION_REQUEST_ACCEPTED from %s\n", p->systemAddress.ToString()); rakVoice.RequestVoiceChannel(p->systemAddress); } else if (p->data[0]==ID_RAKVOICE_OPEN_CHANNEL_REQUEST) { printf("\nOpen Channel request from %s\n", p->systemAddress.ToString()); } else if (p->data[0]==ID_RAKVOICE_OPEN_CHANNEL_REPLY) { printf("\nGot new channel from %s\n", p->systemAddress.ToString()); } rakPeer->DeallocatePacket(p); p=rakPeer->Receive(); } // Update our connection with DirectSound DSoundVoiceAdapter::Instance()->Update(); LogStats(); RakSleep(20); } // Release any FMOD resources we used, and shutdown FMOD itself DSoundVoiceAdapter::Instance()->Release(); rakPeer->Shutdown(300); RakNetworkFactory::DestroyRakPeerInterface(rakPeer); return 0; }
int main(void) { PortAudioStream *stream; PaError err; mute=false; bool quit; char ch; printf("A sample on how to use RakVoice. You need a microphone for this sample.\n"); printf("RakVoice relies on Speex for voice encoding and decoding.\n"); printf("See DependentExtensions/RakVoice/speex-1.1.12 for speex projects.\n"); printf("For windows, I had to define HAVE_CONFIG_H, include win32/config.h,\n"); printf("and include the files under libspeex, except those that start with test.\n"); printf("PortAudio is also included and is used to read and write audio data. You\n"); printf("can substitute whatever you want if you do not want to use portaudio.\n"); printf("Difficulty: Advanced\n\n"); // Since voice is peer to peer, we give the option to use the nat punchthrough client if desired. NatPunchthroughClient natPunchthroughClient; char port[256]; rakPeer = RakNetworkFactory::GetRakPeerInterface(); printf("Enter local port (enter for default): "); gets(port); if (port[0]==0) strcpy(port, "60000"); SocketDescriptor socketDescriptor(atoi(port),0); rakPeer->Startup(4, 30, &socketDescriptor, 1); rakPeer->SetMaximumIncomingConnections(4); rakPeer->AttachPlugin(&rakVoice); rakPeer->AttachPlugin(&natPunchthroughClient); rakVoice.Init(SAMPLE_RATE, FRAMES_PER_BUFFER*sizeof(SAMPLE)); err = Pa_Initialize(); if( err != paNoError ) goto error; err = Pa_OpenStream( &stream, Pa_GetDefaultInputDeviceID(), 1, // Num channels, whatever that means PA_SAMPLE_TYPE, NULL, Pa_GetDefaultOutputDeviceID(), 1, // Num channels PA_SAMPLE_TYPE, NULL, SAMPLE_RATE, FRAMES_PER_BUFFER, /* frames per buffer */ 0, /* number of buffers, if zero then use default minimum */ 0, /* paDitherOff, // flags */ PACallback, 0 ); if( err != paNoError ) goto error; err = Pa_StartStream( stream ); if( err != paNoError ) goto error; printf("Support NAT punchthrough? (y/n)? "); bool useNatPunchthrough; useNatPunchthrough=(getche()=='y'); printf("\n"); char facilitatorIP[256]; {//Linux fix. Won't compile without it. Because of the goto error above, the scope is ambigious. Make it a block to define that it will not be used after the jump. //Doesn't change current logic SystemAddress facilitator; if (useNatPunchthrough) { printf("My GUID is %s\n", rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).ToString()); printf("Enter IP of facilitator (enter for default): "); gets(facilitatorIP); if (facilitatorIP[0]==0) strcpy(facilitatorIP, "94.198.81.195"); facilitator.SetBinaryAddress(facilitatorIP); facilitator.port=NAT_PUNCHTHROUGH_FACILITATOR_PORT; rakPeer->Connect(facilitatorIP, NAT_PUNCHTHROUGH_FACILITATOR_PORT, 0, 0); printf("Connecting to facilitator...\n"); } else { printf("Not supporting NAT punchthrough.\n"); } Packet *p; quit=false; if (useNatPunchthrough==false) printf("(Q)uit. (C)onnect. (D)isconnect. C(l)ose voice channels. (M)ute. ' ' for stats.\n"); while (!quit) { if (kbhit()) { ch=getch(); if (ch=='y') { quit=true; } else if (ch=='c') { if (useNatPunchthrough) { RakNetGUID destination; printf("Enter GUID of destination: "); char guidStr[256]; while (1) { gets(guidStr); if (!destination.FromString(guidStr)) printf("Invalid GUID format. Try again.\nEnter GUID of destination: "); else break; } printf("Starting NAT punch. Please wait...\n"); natPunchthroughClient.OpenNAT(destination,facilitator); } else { char ip[256]; printf("Enter IP of remote system: "); gets(ip); if (ip[0]==0) strcpy(ip, "127.0.0.1"); printf("Enter port of remote system: "); gets(port); if (port[0]==0) strcpy(port, "60000"); rakPeer->Connect(ip, atoi(port), 0,0); } } else if (ch=='m') { mute=!mute; if (mute) printf("Now muted.\n"); else printf("No longer muted.\n"); } else if (ch=='d') { rakPeer->Shutdown(100,0); } else if (ch=='l') { rakVoice.CloseAllChannels(); } else if (ch==' ') { char message[2048]; RakNetStatistics *rss=rakPeer->GetStatistics(rakPeer->GetSystemAddressFromIndex(0)); StatisticsToString(rss, message, 2); printf("%s", message); } else if (ch=='q') quit=true; ch=0; } p=rakPeer->Receive(); while (p) { if (p->data[0]==ID_CONNECTION_REQUEST_ACCEPTED) { if (p->systemAddress==facilitator) { printf("Connection to facilitator completed\n"); printf("(Q)uit. (C)onnect. (D)isconnect. (M)ute. ' ' for stats.\n"); } else { printf("ID_CONNECTION_REQUEST_ACCEPTED from %s\n", p->systemAddress.ToString()); rakVoice.RequestVoiceChannel(p->guid); } } else if (p->data[0]==ID_CONNECTION_ATTEMPT_FAILED) { if (p->systemAddress==facilitator) { printf("Connection to facilitator failed. Using direct connections\n"); useNatPunchthrough=false; printf("(Q)uit. (C)onnect. (D)isconnect. (M)ute. ' ' for stats.\n"); } else { printf("ID_CONNECTION_ATTEMPT_FAILED\n"); } } else if (p->data[0]==ID_RAKVOICE_OPEN_CHANNEL_REQUEST || p->data[0]==ID_RAKVOICE_OPEN_CHANNEL_REPLY) { printf("Got new channel from %s\n", p->systemAddress.ToString()); } else if (p->data[0]==ID_NAT_TARGET_NOT_CONNECTED) { RakNetGUID g; RakNet::BitStream b(p->data, p->length, false); b.IgnoreBits(8); // Ignore the ID_... b.Read(g); printf("ID_NAT_TARGET_NOT_CONNECTED for %s\n", g.ToString()); } else if (p->data[0]==ID_NAT_TARGET_UNRESPONSIVE) { RakNetGUID g; RakNet::BitStream b(p->data, p->length, false); b.IgnoreBits(8); // Ignore the ID_... b.Read(g); printf("ID_NAT_TARGET_UNRESPONSIVE for %s\n", g.ToString()); } else if (p->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST) { RakNetGUID g; RakNet::BitStream b(p->data, p->length, false); b.IgnoreBits(8); // Ignore the ID_... b.Read(g); printf("ID_NAT_CONNECTION_TO_TARGET_LOST for %s\n", g.ToString()); } else if (p->data[0]==ID_NAT_ALREADY_IN_PROGRESS) { RakNetGUID g; RakNet::BitStream b(p->data, p->length, false); b.IgnoreBits(8); // Ignore the ID_... b.Read(g); printf("ID_NAT_ALREADY_IN_PROGRESS for %s\n", g.ToString()); } else if (p->data[0]==ID_NAT_PUNCHTHROUGH_FAILED) { printf("ID_NAT_PUNCHTHROUGH_FAILED for %s\n", p->guid.ToString()); } else if (p->data[0]==ID_NAT_PUNCHTHROUGH_SUCCEEDED) { printf("ID_NAT_PUNCHTHROUGH_SUCCEEDED for %s. Connecting...\n", p->guid.ToString()); rakPeer->Connect(p->systemAddress.ToString(false),p->systemAddress.port,0,0); } else if (p->data[0]==ID_ALREADY_CONNECTED) { printf("ID_ALREADY_CONNECTED\n"); } else if (p->data[0]==ID_RAKVOICE_CLOSE_CHANNEL) { printf("ID_RAKVOICE_CLOSE_CHANNEL\n"); } else if (p->data[0]==ID_DISCONNECTION_NOTIFICATION) { printf("ID_DISCONNECTION_NOTIFICATION\n"); } else if (p->data[0]==ID_NEW_INCOMING_CONNECTION) { printf("ID_NEW_INCOMING_CONNECTION\n"); } else { printf("Unknown packet ID %i\n", p->data[0]); } rakPeer->DeallocatePacket(p); p=rakPeer->Receive(); } Pa_Sleep( 30 ); } } err = Pa_CloseStream( stream ); if( err != paNoError ) goto error; Pa_Terminate(); rakPeer->Shutdown(300); RakNetworkFactory::DestroyRakPeerInterface(rakPeer); return 0; error: Pa_Terminate(); fprintf( stderr, "An error occured while using the portaudio stream\n" ); fprintf( stderr, "Error number: %d\n", err ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); return -1; }
void* rakVoiceThread( void* arguments ) #endif { SpeexBits bits; char output[ 2000 ]; int outputLength; unsigned char typeID; PlayerID id; BitStream b; int availableChunks; int MTU; int numberOfChunksPerSend; int i; unsigned long lastSendTime; CoderStateWithPlayerIDMapStruct *cswpims; lastSendTime = RakNet::GetTime(); PlayerID target; RakVoice *rakVoice = ( RakVoice* ) arguments; numberOfChunksPerSend = rakVoice->blockSize / rakVoice->frame_size; speex_bits_init( &bits ); MTU = rakVoice->peer->GetMTUSize(); while ( rakVoice->init ) { if ( rakVoice->writeCursor >= rakVoice->readCursor ) availableChunks = rakVoice->writeCursor - rakVoice->readCursor; else availableChunks = RAK_VOICE_INPUT_LIST_BUFFER_SIZE - rakVoice->readCursor + rakVoice->writeCursor; while ( availableChunks >= numberOfChunksPerSend ) { // Get a bit of a buffer before we start sending so we don't "grind" the data and get popping as data continually arrives and runs out if ( RakNet::GetTime() - lastSendTime > 1000 && availableChunks < numberOfChunksPerSend * 3 ) break; // Grab data at the read cursor and encode it speex_bits_reset( &bits ); target = rakVoice->targetedSendRecipient[ rakVoice->readCursor ]; cswpims = rakVoice->GetCoderFromPlayerID( rakVoice->sampleRate, target, false ); for ( i = 0; i < numberOfChunksPerSend; i++ ) { // For each frame, call speex_encode speex_encode( cswpims->encoderState, rakVoice->inputList[ rakVoice->readCursor ], &bits ); rakVoice->readCursor = ( rakVoice->readCursor + 1 ) % RAK_VOICE_INPUT_LIST_BUFFER_SIZE; } availableChunks -= numberOfChunksPerSend; // Write the encoded bitstream outputLength = speex_bits_write( ( SpeexBits* ) ( &bits ), output, 2000 ); #ifdef _DEBUG static bool printOnce = true; if ( printOnce == true && outputLength > MTU ) { printf( "Warning - compressed data larger than MTU! This will result in split packets and poor speech.\nYou should use a lower blockSize in the call to Init.\n" ); printOnce = false; } else if ( printOnce == true && outputLength < MTU / 4 ) { printf( "Warning - compressed data smaller than 1/4 the MTU. This is not an efficient use of bandwidth.\nYou might want to use a larger blockSize in the call to Init.\n" ); printOnce = false; } #endif b.Reset(); typeID = ID_VOICE_PACKET; b.Write( typeID ); b.Write( ( char* ) & target, sizeof( target ) ); b.Write( rakVoice->bps ); // Write how many bits we are encoding the data with b.Write( rakVoice->sampleRate ); // Write the sampling rate id = rakVoice->peer->GetInternalID(); b.Write( ( char* ) & id, sizeof( id ) ); // Write who is sending this packet b.Write( cswpims->nextPacketNumber ); // Write what speech packet number this is, so we can compensate for lost packets cswpims->nextPacketNumber++; b.Write( output, outputLength ); /* // This is for showing memory usage printf("PCMQueue=%i PCMQueuePool=%i\ndecoderStateList=%i encoderStateList=%i\n", rakVoice->PCMQueue.size(), rakVoice->PCMQueuePool.size(), rakVoice->decoderStateList.size(), rakVoice->encoderStateList.size()); */ // THis was for testing as a feedback loop //if (target==rakVoice->peer->GetInternalID()) // rakVoice->DecodeAndQueueSoundPacket((char*)b.GetData(), b.GetNumberOfBytesUsed()); //else rakVoice->peer->Send( &b, HIGH_PRIORITY, RELIABLE_SEQUENCED, 0, target, false ); lastSendTime = RakNet::GetTime(); } // Send out a packet aggreggate roughly every x ms #ifdef _WIN32 Sleep( 30 ); #else usleep( 30 * 1000 ); #endif } speex_bits_destroy( &bits ); return 0; }