From 674bd8d798586755503b0e645c1bc0d8b6d82963 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 11 Mar 2013 16:19:55 -0700 Subject: [PATCH] transmitter full movement, and audio jitter buffer tweaks/debugging data --- interface/src/Audio.cpp | 70 ++++++++++++++++++++++++++++++----------- interface/src/Hand.cpp | 37 +++++++++++++++++----- interface/src/Hand.h | 2 +- interface/src/Head.cpp | 4 +-- interface/src/Head.h | 2 +- interface/src/main.cpp | 4 +-- 6 files changed, 88 insertions(+), 31 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 7db5a87ee5..b03d96f5ab 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -14,10 +14,13 @@ #include #include "Audio.h" #include "Util.h" +#include #include "UDPSocket.h" Oscilloscope * scope; +const int NUM_AUDIO_CHANNELS = 2; + const int PACKET_LENGTH_BYTES = 1024; const int PACKET_LENGTH_BYTES_PER_CHANNEL = PACKET_LENGTH_BYTES / 2; const int PACKET_LENGTH_SAMPLES = PACKET_LENGTH_BYTES / sizeof(int16_t); @@ -33,8 +36,11 @@ const int PHASE_DELAY_AT_90 = 20; const float AMPLITUDE_RATIO_AT_90 = 0.5; const int SAMPLE_RATE = 22050; -const float JITTER_BUFFER_LENGTH_MSECS = 30.0; -const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS * (SAMPLE_RATE / 1000.0); +const float JITTER_BUFFER_LENGTH_MSECS = 4; +const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS * + NUM_AUDIO_CHANNELS * (SAMPLE_RATE / 1000.0); + +const float AUDIO_CALLBACK_MSECS = (float)BUFFER_LENGTH_SAMPLES / (float)SAMPLE_RATE * 1000.0; const int FLANGE_EFFECT_THRESHOLD = 200; const float FLANGE_RATE = 4; @@ -49,7 +55,10 @@ StDev stdev; bool stopAudioReceiveThread = false; int samplesLeftForFlange = 0; -#define LOG_SAMPLE_DELAY 1 +timeval firstPlaybackTimer; +int packetsReceivedThisPlayback = 0; + +#define LOG_SAMPLE_DELAY 0 std::ofstream logFile; @@ -85,6 +94,8 @@ int audioCallback (const void *inputBuffer, int16_t *inputLeft = ((int16_t **) inputBuffer)[0]; // int16_t *inputRight = ((int16_t **) inputBuffer)[1]; + //printf("Audio callback at %6.0f\n", usecTimestampNow()/1000); + if (inputLeft != NULL) { if (data->mixerAddress != 0) { @@ -163,16 +174,23 @@ int audioCallback (const void *inputBuffer, if (ringBuffer->getEndOfLastWrite() != NULL) { - if (!ringBuffer->isStarted() && ringBuffer->diffLastWriteNextOutput() <= PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) { - printf("Held back\n"); + if (!ringBuffer->isStarted() && ringBuffer->diffLastWriteNextOutput() < PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) { + printf("Held back, buffer has %d of %d samples required.\n", ringBuffer->diffLastWriteNextOutput(), PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES); } else if (ringBuffer->diffLastWriteNextOutput() < PACKET_LENGTH_SAMPLES) { ringBuffer->setStarted(false); starve_counter++; + packetsReceivedThisPlayback = 0; + printf("Starved #%d\n", starve_counter); data->wasStarved = 10; // Frames to render the indication that the system was starved. } else { - ringBuffer->setStarted(true); + if (!ringBuffer->isStarted()) { + ringBuffer->setStarted(true); + printf("starting playback %3.1f msecs delayed, \n", (usecTimestampNow() - usecTimestamp(&firstPlaybackTimer))/1000.0); + } else { + //printf("pushing buffer\n"); + } // play whatever we have in the audio buffer // if we haven't fired off the flange effect, check if we should @@ -265,7 +283,7 @@ void *receiveAudioViaUDP(void *args) { delete[] directory; delete[] filename; } - + while (!stopAudioReceiveThread) { if (sharedAudioData->audioSocket->receive((void *)receivedData, &receivedBytes)) { bool firstSample = (currentReceiveTime.tv_sec == 0); @@ -295,6 +313,17 @@ void *receiveAudioViaUDP(void *args) { } AudioRingBuffer *ringBuffer = sharedAudioData->ringBuffer; + + + if (!ringBuffer->isStarted()) { + printf("Audio packet %d received at %6.0f\n", ++packetsReceivedThisPlayback, usecTimestampNow()/1000); + } + else { + //printf("Audio packet received at %6.0f\n", usecTimestampNow()/1000); + } + + if (packetsReceivedThisPlayback == 1) gettimeofday(&firstPlaybackTimer, NULL); + ringBuffer->parseData(receivedData, PACKET_LENGTH_BYTES); if (LOG_SAMPLE_DELAY) { @@ -407,10 +436,14 @@ void Audio::render(int screenWidth, int screenHeight) timeval currentTime; gettimeofday(¤tTime, NULL); float timeLeftInCurrentBuffer = 0; - if (audioData->lastCallback.tv_usec > 0) timeLeftInCurrentBuffer = diffclock(&audioData->lastCallback, ¤tTime)/(1000.0*(float)PACKET_LENGTH_SAMPLES/(float)SAMPLE_RATE) * frameWidth; + if (audioData->lastCallback.tv_usec > 0) { + timeLeftInCurrentBuffer = AUDIO_CALLBACK_MSECS - diffclock(&audioData->lastCallback, ¤tTime); + } + + // /(1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE) * frameWidth if (audioData->ringBuffer->getEndOfLastWrite() != NULL) - remainingBuffer = audioData->ringBuffer->diffLastWriteNextOutput() / PACKET_LENGTH_SAMPLES * frameWidth; + remainingBuffer = audioData->ringBuffer->diffLastWriteNextOutput() / PACKET_LENGTH_SAMPLES * AUDIO_CALLBACK_MSECS; if (audioData->wasStarved == 0) glColor3f(0, 1, 0); else { @@ -420,8 +453,8 @@ void Audio::render(int screenWidth, int screenHeight) glBegin(GL_QUADS); glVertex2f(startX, topY + 5); - glVertex2f(startX + remainingBuffer + timeLeftInCurrentBuffer, topY + 5); - glVertex2f(startX + remainingBuffer + timeLeftInCurrentBuffer, bottomY - 5); + glVertex2f(startX + (remainingBuffer + timeLeftInCurrentBuffer)/AUDIO_CALLBACK_MSECS*frameWidth, topY + 5); + glVertex2f(startX + (remainingBuffer + timeLeftInCurrentBuffer)/AUDIO_CALLBACK_MSECS*frameWidth, bottomY - 5); glVertex2f(startX, bottomY - 5); glEnd(); @@ -431,15 +464,16 @@ void Audio::render(int screenWidth, int screenHeight) // Show a yellow bar with the averaged msecs latency you are hearing (from time of packet receipt) glColor3f(1,1,0); glBegin(GL_QUADS); - glVertex2f(startX + audioData->averagedLatency - 2, topY - 2); - glVertex2f(startX + audioData->averagedLatency + 2, topY - 2); - glVertex2f(startX + audioData->averagedLatency + 2, bottomY + 2); - glVertex2f(startX + audioData->averagedLatency - 2, bottomY + 2); + glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth - 2, topY - 2); + glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth + 2, topY - 2); + glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth + 2, bottomY + 2); + glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth - 2, bottomY + 2); glEnd(); - char out[20]; - sprintf(out, "%3.0f\n", audioData->averagedLatency/(float)frameWidth*(1000.0*(float)PACKET_LENGTH_SAMPLES/(float)SAMPLE_RATE)); - drawtext(startX + audioData->averagedLatency - 10, topY-10, 0.08, 0, 1, 0, out, 1,1,0); + char out[40]; + sprintf(out, "%3.0f\n", audioData->averagedLatency); + drawtext(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth - 10, topY-10, 0.08, 0, 1, 0, out, 1,1,0); + //drawtext(startX + 0, topY-10, 0.08, 0, 1, 0, out, 1,1,0); // Show a Cyan bar with the most recently measured jitter stdev diff --git a/interface/src/Hand.cpp b/interface/src/Hand.cpp index b0d8acf8bd..ea57c91a9b 100644 --- a/interface/src/Hand.cpp +++ b/interface/src/Hand.cpp @@ -40,7 +40,7 @@ void Hand::reset() transmitterHz = DEFAULT_TRANSMITTER_HZ; } -void Hand::render() +void Hand::render(int isMine) { const float POINTER_LENGTH = 20.0; glPushMatrix(); @@ -122,13 +122,13 @@ void Hand::processTransmitterData(char *packetData, int numBytes) { const float ANG_VEL_THRESHOLD = 0.0; float angVelScale = ANG_VEL_SENSITIVITY*(1.0/getTransmitterHz()); //addAngularVelocity(gyrX*angVelScale,gyrZ*angVelScale,-gyrY*angVelScale); - addAngularVelocity(0, + addAngularVelocity(fabs(gyrX*angVelScale)>ANG_VEL_THRESHOLD?gyrX*angVelScale:0, fabs(gyrZ*angVelScale)>ANG_VEL_THRESHOLD?gyrZ*angVelScale:0, - 0); + fabs(-gyrY*angVelScale)>ANG_VEL_THRESHOLD?-gyrY*angVelScale:0); // Add linear forces to the hand //const float LINEAR_VEL_SENSITIVITY = 50.0; - const float LINEAR_VEL_SENSITIVITY = 0.0; + const float LINEAR_VEL_SENSITIVITY = 5.0; float linVelScale = LINEAR_VEL_SENSITIVITY*(1.0/getTransmitterHz()); glm::vec3 linVel(linX*linVelScale, linZ*linVelScale, -linY*linVelScale); addVelocity(linVel); @@ -184,14 +184,37 @@ void Hand::simulate(float deltaTime) -rollRate*ANGULAR_DAMPING_COEFFICIENT*deltaTime); } - // The absolute threshold method + // The absolute limits method (no springs) if (1) { + // Limit rotation const float YAW_LIMIT = 20; + const float PITCH_LIMIT = 20; + if (yaw > YAW_LIMIT) { yaw = YAW_LIMIT; yawRate = 0.0; } if (yaw < -YAW_LIMIT) { yaw = -YAW_LIMIT; yawRate = 0.0; } + if (pitch > PITCH_LIMIT) { pitch = PITCH_LIMIT; pitchRate = 0.0; } + if (pitch < -PITCH_LIMIT) { pitch = -PITCH_LIMIT; pitchRate = 0.0; } + + // Damp Rotation Rates + yawRate *= 0.99; + pitchRate *= 0.99; + rollRate *= 0.99; + + // Limit position + const float X_LIMIT = 1.0; + const float Y_LIMIT = 1.0; + const float Z_LIMIT = 1.0; + + if (position.x > DEFAULT_X + X_LIMIT) { position.x = DEFAULT_X + X_LIMIT; velocity.x = 0; } + if (position.x < DEFAULT_X - X_LIMIT) { position.x = DEFAULT_X - X_LIMIT; velocity.x = 0; } + if (position.y > DEFAULT_Y + Y_LIMIT) { position.y = DEFAULT_Y + Y_LIMIT; velocity.y = 0; } + if (position.y < DEFAULT_Y - Y_LIMIT) { position.y = DEFAULT_Y - Y_LIMIT; velocity.y = 0; } + if (position.z > DEFAULT_Z + Z_LIMIT) { position.z = DEFAULT_Z + Z_LIMIT; velocity.z = 0; } + if (position.z < DEFAULT_Z - Z_LIMIT) { position.z = DEFAULT_Z - Z_LIMIT; velocity.z = 0; } + + // Damp Velocity + velocity *= 0.99; - // Damp Yaw Rate - yawRate *= 0.99; } } \ No newline at end of file diff --git a/interface/src/Hand.h b/interface/src/Hand.h index 7019cdea22..3c32c3553d 100644 --- a/interface/src/Hand.h +++ b/interface/src/Hand.h @@ -20,7 +20,7 @@ class Hand { public: Hand(glm::vec3 color); void simulate (float deltaTime); - void render (); + void render (int isMine); void reset (); void setNoise (float mag) { noise = mag; }; void addVelocity (glm::vec3 v) { velocity += v; }; diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index d3caf98f95..029653592a 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -239,7 +239,7 @@ void Head::simulate(float deltaTime) } -void Head::render(int faceToFace, float * myLocation) +void Head::render(int faceToFace, int isMine, float * myLocation) { int side = 0; @@ -255,7 +255,7 @@ void Head::render(int faceToFace, float * myLocation) glRotatef(Yaw, 0, 1, 0); - hand->render(); + hand->render(1); // Don't render a head if it is really close to your location, because that is your own head! if ((distanceToCamera > 1.0) || faceToFace) { diff --git a/interface/src/Head.h b/interface/src/Head.h index 2b1863b2bd..bf905c93d4 100644 --- a/interface/src/Head.h +++ b/interface/src/Head.h @@ -47,7 +47,7 @@ class Head : public AgentData { float getRoll() {return Roll;} float getYaw() {return Yaw;} - void render(int faceToFace, float * myLocation); + void render(int faceToFace, int isMine, float * myLocation); void simulate(float); // Send and receive network data diff --git a/interface/src/main.cpp b/interface/src/main.cpp index df4d95b7da..abe1206afa 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -588,7 +588,7 @@ void display(void) glPushMatrix(); glm::vec3 pos = agentHead->getPos(); glTranslatef(-pos.x, -pos.y, -pos.z); - agentHead->render(0, &location[0]); + agentHead->render(0, 0, &location[0]); glPopMatrix(); } } @@ -602,7 +602,7 @@ void display(void) glPushMatrix(); glLoadIdentity(); glTranslatef(0.f, 0.f, -7.f); - myHead.render(display_head, &location[0]); + myHead.render(display_head, 1, &location[0]); glPopMatrix(); //glm::vec3 test(0.5, 0.5, 0.5);