diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e227c95f6..cd284ff3e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,4 +5,5 @@ project(hifi) add_subdirectory(space) add_subdirectory(domain) add_subdirectory(mixer) +add_subdirectory(voxel) add_subdirectory(interface) \ No newline at end of file diff --git a/domain/src/main.cpp b/domain/src/main.cpp index 9a8b6bc64f..81b2ccc7d9 100644 --- a/domain/src/main.cpp +++ b/domain/src/main.cpp @@ -68,10 +68,10 @@ int main(int argc, const char * argv[]) agentList.startSilentAgentRemovalThread(); - std::map newestSoloAgents; - while (true) { if (agentList.getAgentSocket().receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes)) { + std::map newestSoloAgents; + agentType = packetData[0]; unpackSocket(&packetData[1], (sockaddr *)&agentLocalAddress); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0685aca6cd..d5ac0ddc7f 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -10,7 +10,7 @@ project(interface) if (APPLE) # link in required OS X frameworks and include the right GL headers - set(CMAKE_EXE_LINKER_FLAGS "-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework CoreServices -framework Carbon") + set(CMAKE_EXE_LINKER_FLAGS "-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework CoreServices -framework Carbon -framework GLUT") set(GL_HEADERS "#include \n#include ") else (APPLE) # include the right GL headers for UNIX @@ -21,7 +21,7 @@ endif (APPLE) configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h) # grab the implementation and header files from src dir -file(GLOB INTERFACE_SRCS src/*.cpp src/*.h) +file(GLOB INTERFACE_SRCS src/*.mm src/*.cpp src/*.h) if (APPLE) # set how the icon shows up in the Info.plist file diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 4990e4f5de..60f28ca5df 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -14,22 +14,36 @@ #include #include "Audio.h" #include "Util.h" +#include #include "UDPSocket.h" Oscilloscope * scope; -const short PACKET_LENGTH_BYTES = 1024; -const short PACKET_LENGTH_SAMPLES = PACKET_LENGTH_BYTES / sizeof(int16_t); +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); +const int PACKET_LENGTH_SAMPLES_PER_CHANNEL = PACKET_LENGTH_SAMPLES / 2; + +const int BUFFER_LENGTH_BYTES = 512; +const int BUFFER_LENGTH_SAMPLES = BUFFER_LENGTH_BYTES / sizeof(int16_t); + +const int RING_BUFFER_FRAMES = 10; +const int RING_BUFFER_SAMPLES = RING_BUFFER_FRAMES * BUFFER_LENGTH_SAMPLES; 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 short NUM_AUDIO_SOURCES = 2; -const short ECHO_SERVER_TEST = 1; +const float AUDIO_CALLBACK_MSECS = (float)BUFFER_LENGTH_SAMPLES / (float)SAMPLE_RATE * 1000.0; + + +const int AGENT_LOOPBACK_MODIFIER = 307; const char LOCALHOST_MIXER[] = "0.0.0.0"; const char WORKCLUB_MIXER[] = "192.168.1.19"; @@ -41,7 +55,10 @@ int starve_counter = 0; StDev stdev; bool stopAudioReceiveThread = false; -#define LOG_SAMPLE_DELAY 1 +timeval firstPlaybackTimer; +int packetsReceivedThisPlayback = 0; + +#define LOG_SAMPLE_DELAY 0 std::ofstream logFile; @@ -77,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) { @@ -85,24 +104,40 @@ int audioCallback (const void *inputBuffer, audioMixerSocket.sin_addr.s_addr = data->mixerAddress; audioMixerSocket.sin_port = data->mixerPort; - int leadingBytes = 1 + (sizeof(float) * 3); + int leadingBytes = 1 + (sizeof(float) * 4); // we need the amount of bytes in the buffer + 1 for type + 12 for 3 floats for position - unsigned char *dataPacket = new unsigned char[BUFFER_LENGTH_BYTES + leadingBytes]; + unsigned char dataPacket[BUFFER_LENGTH_BYTES + leadingBytes]; dataPacket[0] = 'I'; + unsigned char *currentPacketPtr = dataPacket + 1; // memcpy the three float positions for (int p = 0; p < 3; p++) { - memcpy(dataPacket + 1 + (p * sizeof(float)), &data->sourcePosition[p], sizeof(float)); + memcpy(currentPacketPtr, &data->linkedHead->getPos()[p], sizeof(float)); + currentPacketPtr += sizeof(float); } - // copy the audio data to the last 1024 bytes of the data packet - memcpy(dataPacket + leadingBytes, inputLeft, BUFFER_LENGTH_BYTES); + // memcpy the corrected render yaw + float correctedYaw = fmodf(data->linkedHead->getRenderYaw(), 360); + + if (correctedYaw > 180) { + correctedYaw -= 360; + } else if (correctedYaw < -180) { + correctedYaw += 360; + } + + if (data->mixerLoopbackFlag) { + correctedYaw = correctedYaw > 0 ? correctedYaw + AGENT_LOOPBACK_MODIFIER : correctedYaw - AGENT_LOOPBACK_MODIFIER; + } + + memcpy(currentPacketPtr, &correctedYaw, sizeof(float)); + currentPacketPtr += sizeof(float); + + // copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet + memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES); data->audioSocket->send((sockaddr *)&audioMixerSocket, dataPacket, BUFFER_LENGTH_BYTES + leadingBytes); - - delete dataPacket; } // @@ -112,6 +147,7 @@ int audioCallback (const void *inputBuffer, for (int i = 0; i < BUFFER_LENGTH_SAMPLES; i++) { loudness += abs(inputLeft[i]); } + loudness /= BUFFER_LENGTH_SAMPLES; data->lastInputLoudness = loudness; data->averagedInputLoudness = 0.66*data->averagedInputLoudness + 0.33*loudness; @@ -128,42 +164,54 @@ int audioCallback (const void *inputBuffer, int16_t *outputLeft = ((int16_t **) outputBuffer)[0]; int16_t *outputRight = ((int16_t **) outputBuffer)[1]; - memset(outputLeft, 0, BUFFER_LENGTH_BYTES); - memset(outputRight, 0, BUFFER_LENGTH_BYTES); - - // Copy output data to oscilloscope - if (scope->getState()) { - for (int i = 0; i < BUFFER_LENGTH_SAMPLES; i++) { - scope->addData((float)outputRight[i]/32767.0, 2, i); - } - } + memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL); + memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL); AudioRingBuffer *ringBuffer = data->ringBuffer; - int16_t *queueBuffer = data->samplesToQueue; - memset(queueBuffer, 0, BUFFER_LENGTH_BYTES); - // if we've been reset, and there isn't any new packets yet // just play some silence 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 - // no sample overlap, either a direct copy of the audio data, or a copy with some appended silence - memcpy(queueBuffer, ringBuffer->getNextOutput(), BUFFER_LENGTH_BYTES); + // check if we have more than we need to play out + int thresholdFrames = ceilf((PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) / (float)PACKET_LENGTH_SAMPLES); + int thresholdSamples = thresholdFrames * PACKET_LENGTH_SAMPLES; - ringBuffer->setNextOutput(ringBuffer->getNextOutput() + BUFFER_LENGTH_SAMPLES); + if (ringBuffer->diffLastWriteNextOutput() > thresholdSamples) { + // we need to push the next output forwards + int samplesToPush = ringBuffer->diffLastWriteNextOutput() - thresholdSamples; + + if (ringBuffer->getNextOutput() + samplesToPush > ringBuffer->getBuffer()) { + ringBuffer->setNextOutput(ringBuffer->getBuffer() + (samplesToPush - (ringBuffer->getBuffer() + RING_BUFFER_SAMPLES - ringBuffer->getNextOutput()))); + } else { + ringBuffer->setNextOutput(ringBuffer->getNextOutput() + samplesToPush); + } + } + + memcpy(outputLeft, ringBuffer->getNextOutput(), PACKET_LENGTH_BYTES_PER_CHANNEL); + memcpy(outputRight, ringBuffer->getNextOutput() + PACKET_LENGTH_SAMPLES_PER_CHANNEL, PACKET_LENGTH_BYTES_PER_CHANNEL); + + ringBuffer->setNextOutput(ringBuffer->getNextOutput() + PACKET_LENGTH_SAMPLES); if (ringBuffer->getNextOutput() == ringBuffer->getBuffer() + RING_BUFFER_SAMPLES) { ringBuffer->setNextOutput(ringBuffer->getBuffer()); @@ -171,10 +219,6 @@ int audioCallback (const void *inputBuffer, } } - // copy whatever is in the queueBuffer to the outputLeft and outputRight buffers - memcpy(outputLeft, queueBuffer, BUFFER_LENGTH_BYTES); - memcpy(outputRight, queueBuffer, BUFFER_LENGTH_BYTES); - gettimeofday(&data->lastCallback, NULL); return paContinue; } @@ -192,7 +236,7 @@ void *receiveAudioViaUDP(void *args) { AudioRecThreadStruct *threadArgs = (AudioRecThreadStruct *) args; AudioData *sharedAudioData = threadArgs->sharedAudioData; - int16_t *receivedData = new int16_t[BUFFER_LENGTH_SAMPLES]; + int16_t *receivedData = new int16_t[PACKET_LENGTH_SAMPLES]; ssize_t receivedBytes; timeval previousReceiveTime, currentReceiveTime = {}; @@ -213,7 +257,7 @@ void *receiveAudioViaUDP(void *args) { delete[] directory; delete[] filename; } - + while (!stopAudioReceiveThread) { if (sharedAudioData->audioSocket->receive((void *)receivedData, &receivedBytes)) { bool firstSample = (currentReceiveTime.tv_sec == 0); @@ -243,6 +287,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) { @@ -254,8 +309,12 @@ void *receiveAudioViaUDP(void *args) { pthread_exit(0); } -void Audio::setSourcePosition(glm::vec3 newPosition) { - audioData->sourcePosition = newPosition; +void Audio::setMixerLoopbackFlag(bool newMixerLoopbackFlag) { + audioData->mixerLoopbackFlag = newMixerLoopbackFlag; +} + +bool Audio::getMixerLoopbackFlag() { + return audioData->mixerLoopbackFlag; } /** @@ -265,18 +324,20 @@ void Audio::setSourcePosition(glm::vec3 newPosition) { * @return Returns true if successful or false if an error occurred. Use Audio::getError() to retrieve the error code. */ -Audio::Audio(Oscilloscope * s) +Audio::Audio(Oscilloscope *s, Head *linkedHead) { paError = Pa_Initialize(); if (paError != paNoError) goto error; scope = s; - audioData = new AudioData(BUFFER_LENGTH_BYTES); + audioData = new AudioData(); + + audioData->linkedHead = linkedHead; // setup a UDPSocket audioData->audioSocket = new UDPSocket(AUDIO_UDP_LISTEN_PORT); - audioData->ringBuffer = new AudioRingBuffer(); + audioData->ringBuffer = new AudioRingBuffer(RING_BUFFER_SAMPLES, PACKET_LENGTH_SAMPLES); AudioRecThreadStruct threadArgs; threadArgs.sharedAudioData = audioData; @@ -287,8 +348,8 @@ Audio::Audio(Oscilloscope * s) 2, // input channels 2, // output channels (paInt16 | paNonInterleaved), // sample format - 22050, // sample rate (hz) - 512, // frames per buffer + SAMPLE_RATE, // sample rate (hz) + BUFFER_LENGTH_SAMPLES, // frames per buffer audioCallback, // callback function (void *) audioData); // user data to be passed to callback if (paError != paNoError) goto error; @@ -299,7 +360,7 @@ Audio::Audio(Oscilloscope * s) Pa_StartStream(stream); if (paError != paNoError) goto error; - + return; error: @@ -317,7 +378,7 @@ void Audio::getInputLoudness(float * lastLoudness, float * averageLoudness) { void Audio::render(int screenWidth, int screenHeight) { - if (initialized && ECHO_SERVER_TEST) { + if (initialized) { glBegin(GL_LINES); glColor3f(1,1,1); @@ -349,10 +410,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)BUFFER_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() / BUFFER_LENGTH_SAMPLES * frameWidth; + remainingBuffer = audioData->ringBuffer->diffLastWriteNextOutput() / PACKET_LENGTH_SAMPLES * AUDIO_CALLBACK_MSECS; if (audioData->wasStarved == 0) glColor3f(0, 1, 0); else { @@ -362,8 +427,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(); @@ -373,19 +438,20 @@ 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)BUFFER_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 - int jitterPels = (float) audioData->measuredJitter/ ((1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE)) * (float)frameWidth; + int jitterPels = (float) audioData->measuredJitter/ ((1000.0*(float)PACKET_LENGTH_SAMPLES/(float)SAMPLE_RATE)) * (float)frameWidth; glColor3f(0,1,1); glBegin(GL_QUADS); diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 0798486106..e4760c4f6f 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -13,15 +13,19 @@ #include #include "AudioData.h" #include "Oscilloscope.h" +#include "Head.h" class Audio { public: // initializes audio I/O - Audio(Oscilloscope * s); + Audio(Oscilloscope *s, Head *linkedHead); void render(); void render(int screenWidth, int screenHeight); + bool getMixerLoopbackFlag(); + void setMixerLoopbackFlag(bool newMixerLoopbackFlag); + void getInputLoudness(float * lastLoudness, float * averageLoudness); void updateMixerParams(in_addr_t mixerAddress, in_port_t mixerPort); diff --git a/interface/src/AudioData.cpp b/interface/src/AudioData.cpp index afba708db4..3a2984a15a 100644 --- a/interface/src/AudioData.cpp +++ b/interface/src/AudioData.cpp @@ -8,20 +8,20 @@ #include "AudioData.h" -AudioData::AudioData(int bufferLength) { +AudioData::AudioData() { mixerAddress = 0; mixerPort = 0; - samplesToQueue = new int16_t[bufferLength / sizeof(int16_t)]; averagedLatency = 0.0; lastCallback.tv_usec = 0; wasStarved = 0; measuredJitter = 0; jitterBuffer = 0; + + mixerLoopbackFlag = false; } AudioData::~AudioData() { - delete[] samplesToQueue; delete audioSocket; } \ No newline at end of file diff --git a/interface/src/AudioData.h b/interface/src/AudioData.h index 4efe39884a..ef55d3a7f8 100644 --- a/interface/src/AudioData.h +++ b/interface/src/AudioData.h @@ -14,18 +14,17 @@ #include #include "AudioRingBuffer.h" #include "UDPSocket.h" +#include "Head.h" class AudioData { public: - AudioData(int bufferLength); + AudioData(); ~AudioData(); AudioRingBuffer *ringBuffer; UDPSocket *audioSocket; - int16_t *samplesToQueue; - - glm::vec3 sourcePosition; + Head *linkedHead; // store current mixer address and port in_addr_t mixerAddress; @@ -39,6 +38,8 @@ class AudioData { float lastInputLoudness; float averagedInputLoudness; + + bool mixerLoopbackFlag; }; #endif /* defined(__interface__AudioData__) */ diff --git a/interface/src/Hand.cpp b/interface/src/Hand.cpp index dc6f73e456..ea57c91a9b 100644 --- a/interface/src/Hand.cpp +++ b/interface/src/Hand.cpp @@ -7,41 +7,24 @@ // #include "Hand.h" +#include const float PHI = 1.618; const float DEFAULT_X = 0; const float DEFAULT_Y = -1.5; const float DEFAULT_Z = 2.0; +const float DEFAULT_TRANSMITTER_HZ = 60.0; Hand::Hand(glm::vec3 initcolor) { color = initcolor; reset(); - noise = 0.2; + noise = 0.0; //0.2; scale.x = 0.07; scale.y = scale.x * 5.0; scale.z = scale.y * 1.0; -} - -void Hand::addAngularVelocity (float pRate, float yRate, float rRate) { - pitchRate += pRate; - yawRate += yRate; - rollRate += rRate; -} - -void Hand::render() -{ - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glRotatef(yaw, 0, 1, 0); - glRotatef(pitch, 1, 0, 0); - glRotatef(roll, 0, 0, 1); - glColor3f(color.x, color.y, color.z); - glScalef(scale.x, scale.y, scale.z); - //glutSolidSphere(1.5, 20, 20); - glutSolidCube(1.0); - glPopMatrix(); + renderPointer = true; } void Hand::reset() @@ -53,18 +36,117 @@ void Hand::reset() pitchRate = yawRate = rollRate = 0; setTarget(position); velocity.x = velocity.y = velocity.z = 0; + transmitterPackets = 0; + transmitterHz = DEFAULT_TRANSMITTER_HZ; +} + +void Hand::render(int isMine) +{ + const float POINTER_LENGTH = 20.0; + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glRotatef(yaw, 0, 1, 0); + glRotatef(pitch, 1, 0, 0); + glRotatef(roll, 0, 0, 1); + glColor3f(color.x, color.y, color.z); + glScalef(scale.x, scale.y, scale.z); + //glutSolidSphere(1.5, 20, 20); + glutSolidCube(1.0); + if (renderPointer) { + glBegin(GL_TRIANGLES); + glColor3f(1,0,0); + glNormal3f(0,-1,0); + glVertex3f(-0.4,0,0); + glVertex3f(0.4,0,0); + glVertex3f(0,0,-POINTER_LENGTH); + glEnd(); + glPushMatrix(); + glTranslatef(0,0,-POINTER_LENGTH); + glutSolidCube(1.0); + glPopMatrix(); + } + glPopMatrix(); + + if (1) { + // Render debug info from the transmitter + /* + glPushMatrix(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + //gluOrtho2D(0, WIDTH, HEIGHT, 0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glPopMatrix(); + */ + + } + +} + +void Hand::addAngularVelocity (float pRate, float yRate, float rRate) { + pitchRate += pRate; + yawRate += yRate; + rollRate += rRate; +} + +void Hand::processTransmitterData(char *packetData, int numBytes) { + // Read a packet from a transmitter app, process the data + float accX, accY, accZ, + graX, graY, graZ, + gyrX, gyrY, gyrZ, + linX, linY, linZ, + rot1, rot2, rot3, rot4; + sscanf((char *)packetData, "tacc %f %f %f gra %f %f %f gyr %f %f %f lin %f %f %f rot %f %f %f %f", + &accX, &accY, &accZ, + &graX, &graY, &graZ, + &gyrX, &gyrY, &gyrZ, + &linX, &linY, &linZ, + &rot1, &rot2, &rot3, &rot4); + + if (transmitterPackets++ == 0) { + gettimeofday(&transmitterTimer, NULL); + } + const int TRANSMITTER_COUNT = 100; + if (transmitterPackets % TRANSMITTER_COUNT == 0) { + // Every 100 packets, record the observed Hz of the transmitter data + timeval now; + gettimeofday(&now, NULL); + double msecsElapsed = diffclock(&transmitterTimer, &now); + transmitterHz = (float)TRANSMITTER_COUNT/(msecsElapsed/1000.0); + //std::cout << "Transmitter Hz: " << (float)TRANSMITTER_COUNT/(msecsElapsed/1000.0) << "\n"; + //memcpy(&transmitterTimer, &now, sizeof(timeval)); + transmitterTimer = now; + } + // Add rotational forces to the hand + const float ANG_VEL_SENSITIVITY = 4.0; + const float ANG_VEL_THRESHOLD = 0.0; + float angVelScale = ANG_VEL_SENSITIVITY*(1.0/getTransmitterHz()); + //addAngularVelocity(gyrX*angVelScale,gyrZ*angVelScale,-gyrY*angVelScale); + addAngularVelocity(fabs(gyrX*angVelScale)>ANG_VEL_THRESHOLD?gyrX*angVelScale:0, + fabs(gyrZ*angVelScale)>ANG_VEL_THRESHOLD?gyrZ*angVelScale: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 = 5.0; + float linVelScale = LINEAR_VEL_SENSITIVITY*(1.0/getTransmitterHz()); + glm::vec3 linVel(linX*linVelScale, linZ*linVelScale, -linY*linVelScale); + addVelocity(linVel); + } void Hand::simulate(float deltaTime) { - const float VNOISE = 0.01; - const float RSPRING = 0.01; - const float PSPRING = 0.4; - const float RNOISE = 0.1; - const float VDECAY = 5.0; - + const float ANGULAR_SPRING_CONSTANT = 0.25; + const float ANGULAR_DAMPING_COEFFICIENT = 5*2.0*powf(ANGULAR_SPRING_CONSTANT,0.5); + const float LINEAR_SPRING_CONSTANT = 100; + const float LINEAR_DAMPING_COEFFICIENT = 2.0*powf(LINEAR_SPRING_CONSTANT,0.5); + // If noise, add a bit of random velocity + const float RNOISE = 0.0; + const float VNOISE = 0.01; if (noise) { + glm::vec3 nVel(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f); nVel *= VNOISE; addVelocity(nVel); @@ -79,24 +161,60 @@ void Hand::simulate(float deltaTime) yaw += yawRate; roll += rollRate; - // Spring effect to return hand to target; - glm::vec3 sVel = target - position; - sVel *= PSPRING; - addVelocity(sVel); - // Decay position of hand toward target - //position -= deltaTime*(position - target); + // The spring method + if (0) { + // Use a linear spring to attempt to return the hand to the target position + glm::vec3 springForce = target - position; + springForce *= LINEAR_SPRING_CONSTANT; + addVelocity(springForce * deltaTime); - // Decay velocity - velocity *= 1.0 - deltaTime*VDECAY; + // Critically damp the linear spring + glm::vec3 dampingForce(velocity); + dampingForce *= LINEAR_DAMPING_COEFFICIENT; + addVelocity(-dampingForce * deltaTime); - // Decay Angular Velocity - pitchRate *= 1.0 - deltaTime; - yawRate *= 1.0 - deltaTime; - rollRate *= 1.0 - deltaTime; + // Use angular spring to return hand to target rotation (0,0,0) + addAngularVelocity(-pitch * ANGULAR_SPRING_CONSTANT * deltaTime, + -yaw * ANGULAR_SPRING_CONSTANT * deltaTime, + -roll * ANGULAR_SPRING_CONSTANT * deltaTime); + + // Critically damp angular spring + addAngularVelocity(-pitchRate*ANGULAR_DAMPING_COEFFICIENT*deltaTime, + -yawRate*ANGULAR_DAMPING_COEFFICIENT*deltaTime, + -rollRate*ANGULAR_DAMPING_COEFFICIENT*deltaTime); + } - // Add spring effect to return hand rotation to zero - pitchRate -= pitch * RSPRING; - yawRate -= yaw * RSPRING; - rollRate -= roll * RSPRING; + // 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; + + } } \ No newline at end of file diff --git a/interface/src/Hand.h b/interface/src/Hand.h index 01478839ee..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; }; @@ -28,11 +28,17 @@ public: glm::vec3 getPos() { return position; }; void setPos(glm::vec3 p) { position = p; }; void setTarget(glm::vec3 t) { target = t; }; + void processTransmitterData(char * packetData, int numBytes); + float getTransmitterHz() { return transmitterHz; }; + void setRenderPointer(bool p) { renderPointer = p; }; private: glm::vec3 position, target, velocity, color, scale; float pitch, yaw, roll, pitchRate, yawRate, rollRate; float noise; - + timeval transmitterTimer; + float transmitterHz; + int transmitterPackets; + bool renderPointer; }; diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 90e40ed180..cc330c396c 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -47,9 +47,9 @@ Head::Head() interBrowDistance = 0.75; NominalPupilSize = 0.10; Yaw = 0.0; - EyebrowPitch[0] = EyebrowPitch[1] = BrowPitchAngle[0]; - EyebrowRoll[0] = 30; - EyebrowRoll[1] = -30; + EyebrowPitch[0] = EyebrowPitch[1] = -30; + EyebrowRoll[0] = 20; + EyebrowRoll[1] = -20; MouthPitch = 0; MouthYaw = 0; MouthWidth = 1.0; @@ -67,6 +67,12 @@ Head::Head() scale = 1.0; renderYaw = 0.0; renderPitch = 0.0; + audioAttack = 0.0; + loudness = 0.0; + averageLoudness = 0.0; + lastLoudness = 0.0; + browAudioLift = 0.0; + setNoise(0); hand = new Hand(glm::vec3(skinColor[0], skinColor[1], skinColor[2])); @@ -89,6 +95,10 @@ Head* Head::clone() const { return new Head(*this); } +Head* Head::clone() const { + return new Head(*this); +} + void Head::reset() { Pitch = Yaw = Roll = 0; @@ -254,7 +264,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; @@ -270,7 +280,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) { @@ -305,13 +315,22 @@ void Head::render(int faceToFace, float * myLocation) // Eyebrows + audioAttack = 0.9*audioAttack + 0.1*fabs(loudness - lastLoudness); + lastLoudness = loudness; + + const float BROW_LIFT_THRESHOLD = 100; + if (audioAttack > BROW_LIFT_THRESHOLD) + browAudioLift += sqrt(audioAttack)/1000.0; + + browAudioLift *= .90; + glPushMatrix(); glTranslatef(-interBrowDistance/2.0,0.4,0.45); for(side = 0; side < 2; side++) { glColor3fv(browColor); glPushMatrix(); - glTranslatef(0, 0.4, 0); + glTranslatef(0, 0.35 + browAudioLift, 0); glRotatef(EyebrowPitch[side]/2.0, 1, 0, 0); glRotatef(EyebrowRoll[side]/2.0, 0, 0, 1); glScalef(browWidth, browThickness, 1); @@ -326,10 +345,10 @@ void Head::render(int faceToFace, float * myLocation) glPushMatrix(); glTranslatef(0,-0.35,0.75); - glColor3f(loudness/1000.0,0,0); + glColor3f(0,0,0); glRotatef(MouthPitch, 1, 0, 0); glRotatef(MouthYaw, 0, 0, 1); - glScalef(MouthWidth*(.7 + sqrt(averageLoudness)/60.0), MouthHeight*(1.0 + sqrt(averageLoudness)/60.0), 1); + glScalef(MouthWidth*(.7 + sqrt(averageLoudness)/60.0), MouthHeight*(1.0 + sqrt(averageLoudness)/30.0), 1); glutSolidCube(0.5); glPopMatrix(); diff --git a/interface/src/Head.h b/interface/src/Head.h index b554d4541e..c7ffc9502d 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 @@ -95,9 +95,11 @@ class Head : public AgentData { float scale; // Sound loudness information - float loudness; + float loudness, lastLoudness; float averageLoudness; - + float audioAttack; + float browAudioLift; + glm::vec3 position; int eyeContact; eyeContactTargets eyeContactTarget; diff --git a/interface/src/InterfaceMacOSX.h b/interface/src/InterfaceMacOSX.h new file mode 100644 index 0000000000..2b36ff811f --- /dev/null +++ b/interface/src/InterfaceMacOSX.h @@ -0,0 +1,8 @@ +#ifndef __interface__InterfaceMacOSX +#define __interface__InterfaceMacOSX + +class Oscilloscope; + +void initMacOSXMenu(Oscilloscope *audioScope); + +#endif diff --git a/interface/src/InterfaceMacOSX.mm b/interface/src/InterfaceMacOSX.mm new file mode 100644 index 0000000000..466212207b --- /dev/null +++ b/interface/src/InterfaceMacOSX.mm @@ -0,0 +1,68 @@ +#include "Oscilloscope.h" + +#import +#import + +#import "InterfaceMacOSX.h" + +@class InterfaceMainMenuTarget; + +static InterfaceMainMenuTarget *sharedInterfaceMainMenuTarget = nil; +static Oscilloscope *sharedAudioScope; + + +@interface InterfaceMainMenuTarget : NSObject + +@property (strong, nonatomic) NSMenuItem *scopeAudioMenuItem; +@property (strong, nonatomic) NSMenuItem *scopeNoneMenuItem; + +@end + + +@implementation InterfaceMainMenuTarget + +- (void)scopeAudioAction { + sharedAudioScope->setState(true); + [self.scopeAudioMenuItem setState:NSOnState]; + [self.scopeNoneMenuItem setState:NSOffState]; +} + +- (void)scopeNoneAction { + sharedAudioScope->setState(false); + [self.scopeAudioMenuItem setState:NSOffState]; + [self.scopeNoneMenuItem setState:NSOnState]; +} + +@end + + +void initMacOSXMenu(Oscilloscope *audioScope) { + @autoreleasepool { + if (NSApp) { + if (!sharedInterfaceMainMenuTarget) { + sharedInterfaceMainMenuTarget = [[InterfaceMainMenuTarget alloc] init]; + } + sharedAudioScope = audioScope; + + NSMenu *mainMenu = [NSApp mainMenu]; + + NSMenuItem *scopeMenuItem = [mainMenu insertItemWithTitle:@"Scope" + action:nil + keyEquivalent:@"" + atIndex:3]; + + NSMenu *scopeMenu = [[[NSMenu alloc] init] initWithTitle:@"Scope"]; + [scopeMenuItem setSubmenu:scopeMenu]; + sharedInterfaceMainMenuTarget.scopeAudioMenuItem = [scopeMenu addItemWithTitle:@"Audio" + action:@selector(scopeAudioAction) + keyEquivalent:@""]; + [sharedInterfaceMainMenuTarget.scopeAudioMenuItem setTarget:sharedInterfaceMainMenuTarget]; + [sharedInterfaceMainMenuTarget.scopeAudioMenuItem setState:NSOnState]; + + sharedInterfaceMainMenuTarget.scopeNoneMenuItem = [scopeMenu addItemWithTitle:@"None" + action:@selector(scopeNoneAction) + keyEquivalent:@""]; + [sharedInterfaceMainMenuTarget.scopeNoneMenuItem setTarget:sharedInterfaceMainMenuTarget]; + } + } +} diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index b6ea560eff..4ef06dfed4 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -55,11 +55,6 @@ float StDev::getStDev() { return 0; } - -float randFloat () { - return (rand()%10000)/10000.f; -} - // Return the azimuth angle in degrees between two points. float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos) { return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) * 180 / PI; diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index d654f71a5e..9330782309 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -7,122 +7,172 @@ // #include "VoxelSystem.h" +#include +const int MAX_VOXELS_PER_SYSTEM = 500000; -bool onSphereShell(float radius, float scale, glm::vec3 * position) { - float vRadius = glm::length(*position); - return ((vRadius + scale/2.0 > radius) && (vRadius - scale/2.0 < radius)); +const int VERTICES_PER_VOXEL = 8; +const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; +const int COLOR_VALUES_PER_VOXEL = 3 * VERTICES_PER_VOXEL; +const int INDICES_PER_VOXEL = 3 * 12; + +const float CUBE_WIDTH = 0.025f; + +float identityVertices[] = { -1, -1, 1, + 1, -1, 1, + 1, -1, -1, + -1, -1, -1, + 1, 1, 1, + -1, 1, 1, + -1, 1, -1, + 1, 1, -1 }; + +GLubyte identityIndices[] = { 0,1,2, 0,2,3, + 0,4,1, 0,4,5, + 0,3,6, 0,5,6, + 1,2,4, 2,4,7, + 2,3,6, 2,6,7, + 4,5,6, 4,6,7 }; + +VoxelSystem::VoxelSystem() { + voxelsRendered = 0; +} + +VoxelSystem::~VoxelSystem() { + delete[] verticesArray; + delete[] colorsArray; +} + +void VoxelSystem::parseData(void *data, int size) { + // ignore the first char, it's a V to tell us that this is voxel data + char *voxelDataPtr = (char *) data + 1; + + float *position = new float[3]; + char *color = new char[3]; + + // get pointers to position of last append of data + GLfloat *parseVerticesPtr = lastAddPointer; + GLubyte *parseColorsPtr = colorsArray + (lastAddPointer - verticesArray); + + int voxelsInData = 0; + + // pull voxels out of the received data and put them into our internal memory structure + while ((voxelDataPtr - (char *) data) < size) { + + memcpy(position, voxelDataPtr, 3 * sizeof(float)); + voxelDataPtr += 3 * sizeof(float); + memcpy(color, voxelDataPtr, 3); + voxelDataPtr += 3; + + for (int v = 0; v < VERTEX_POINTS_PER_VOXEL; v++) { + parseVerticesPtr[v] = position[v % 3] + (identityVertices[v] * CUBE_WIDTH); + } + + parseVerticesPtr += VERTEX_POINTS_PER_VOXEL; + + for (int c = 0; c < COLOR_VALUES_PER_VOXEL; c++) { + parseColorsPtr[c] = color[c % 3]; + } + + parseColorsPtr += COLOR_VALUES_PER_VOXEL; + + voxelsInData++; + } + + // increase the lastAddPointer to the new spot, increase the number of rendered voxels + lastAddPointer = parseVerticesPtr; + voxelsRendered += voxelsInData; +} + +VoxelSystem* VoxelSystem::clone() const { + // this still needs to be implemented, will need to be used if VoxelSystem is attached to agent + return NULL; } void VoxelSystem::init() { - root = new Voxel; + // prep the data structures for incoming voxel data + lastDrawPointer = lastAddPointer = verticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; + colorsArray = new GLubyte[COLOR_VALUES_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; + + GLuint *indicesArray = new GLuint[INDICES_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; + + // populate the indicesArray + // this will not change given new voxels, so we can set it all up now + for (int n = 0; n < MAX_VOXELS_PER_SYSTEM; n++) { + // fill the indices array + int voxelIndexOffset = n * INDICES_PER_VOXEL; + GLuint *currentIndicesPos = indicesArray + voxelIndexOffset; + int startIndex = (n * VERTICES_PER_VOXEL); + + for (int i = 0; i < INDICES_PER_VOXEL; i++) { + // add indices for this side of the cube + currentIndicesPos[i] = startIndex + identityIndices[i]; + } + } + + // VBO for the verticesArray + glGenBuffers(1, &vboVerticesID); + glBindBuffer(GL_ARRAY_BUFFER, vboVerticesID); + glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW); + + // VBO for colorsArray + glGenBuffers(1, &vboColorsID); + glBindBuffer(GL_ARRAY_BUFFER, vboColorsID); + glBufferData(GL_ARRAY_BUFFER, COLOR_VALUES_PER_VOXEL * sizeof(GLubyte) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW); + + // VBO for the indicesArray + glGenBuffers(1, &vboIndicesID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndicesID); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES_PER_VOXEL * sizeof(GLuint) * MAX_VOXELS_PER_SYSTEM, indicesArray, GL_STATIC_DRAW); + + // delete the indices array that is no longer needed + delete[] indicesArray; } -// -// Recursively initialize the voxel tree -// -int VoxelSystem::initVoxels(Voxel * voxel, float scale, glm::vec3 * position) { - glm::vec3 averageColor(0,0,0); - int childrenCreated = 0; - int newVoxels = 0; - if (voxel == NULL) voxel = root; - averageColor[0] = averageColor[1] = averageColor[2] = 0.0; +void VoxelSystem::render() { + // check if there are new voxels to draw + int vertexValuesToDraw = lastAddPointer - lastDrawPointer; - const float RADIUS = 3.9; + if (vertexValuesToDraw > 0) { + // calculate the offset into each VBO, in vertex point values + int vertexBufferOffset = lastDrawPointer - verticesArray; + + // bind the vertices VBO, copy in new data + glBindBuffer(GL_ARRAY_BUFFER, vboVerticesID); + glBufferSubData(GL_ARRAY_BUFFER, vertexBufferOffset * sizeof(float), vertexValuesToDraw * sizeof(float), lastDrawPointer); + + // bind the colors VBO, copy in new data + glBindBuffer(GL_ARRAY_BUFFER, vboColorsID); + glBufferSubData(GL_ARRAY_BUFFER, vertexBufferOffset * sizeof(GLubyte), vertexValuesToDraw * sizeof(GLubyte), (colorsArray + (lastDrawPointer - verticesArray))); + + // increment the lastDrawPointer to the lastAddPointer value used for this draw + lastDrawPointer += vertexValuesToDraw; + } + + // tell OpenGL where to find vertex and color information + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); - // - // First, randomly decide whether to stop here without recursing for children - // - if (onSphereShell(RADIUS, scale, position) && (scale < 0.25) && (randFloat() < 0.01)) - { - voxel->color.x = 0.1; - voxel->color.y = 0.5 + randFloat()*0.5; - voxel->color.z = 0.1; - for (unsigned char i = 0; i < NUM_CHILDREN; i++) voxel->children[i] = NULL; - return 0; - } else { - // Decide whether to make kids, recurse into them - for (unsigned char i = 0; i < NUM_CHILDREN; i++) { - if (scale > 0.01) { - glm::vec3 shift(scale/2.0*((i&4)>>2)-scale/4.0, - scale/2.0*((i&2)>>1)-scale/4.0, - scale/2.0*(i&1)-scale/4.0); - *position += shift; - // Test to see whether the child is also on edge of sphere - if (onSphereShell(RADIUS, scale/2.0, position)) { - voxel->children[i] = new Voxel; - newVoxels++; - childrenCreated++; - newVoxels += initVoxels(voxel->children[i], scale/2.0, position); - averageColor += voxel->children[i]->color; - } else voxel->children[i] = NULL; - *position -= shift; - } else { - // No child made: Set pointer to null, nothing to see here. - voxel->children[i] = NULL; - } - } - if (childrenCreated > 0) { - // If there were children created, the color of this voxel node is average of children - averageColor *= 1.0/childrenCreated; - voxel->color = averageColor; - return newVoxels; - } else { - // Tested and didn't make any children, so choose my color as a leaf, return - voxel->color.x = voxel->color.y = voxel->color.z = 0.5 + randFloat()*0.5; - for (unsigned char i = 0; i < NUM_CHILDREN; i++) voxel->children[i] = NULL; - return 0; - - } - } -} - -// -// The Render Discard is the ratio of the size of the voxel to the distance from the camera -// at which the voxel will no longer be shown. Smaller = show more detail. -// - -const float RENDER_DISCARD = 0.04; //0.01; - -// -// Returns the total number of voxels actually rendered -// -int VoxelSystem::render(Voxel * voxel, float scale, glm::vec3 * distance) { - // If null passed in, start at root - if (voxel == NULL) voxel = root; - unsigned char i; - bool renderedChildren = false; - int vRendered = 0; - // Recursively render children - for (i = 0; i < NUM_CHILDREN; i++) { - glm::vec3 shift(scale/2.0*((i&4)>>2)-scale/4.0, - scale/2.0*((i&2)>>1)-scale/4.0, - scale/2.0*(i&1)-scale/4.0); - if ((voxel->children[i] != NULL) && (scale / glm::length(*distance) > RENDER_DISCARD)) { - glTranslatef(shift.x, shift.y, shift.z); - *distance += shift; - vRendered += render(voxel->children[i], scale/2.0, distance); - *distance -= shift; - glTranslatef(-shift.x, -shift.y, -shift.z); - renderedChildren = true; - } - } - // Render this voxel if the children were not rendered - if (!renderedChildren) - { - // This is the place where we need to copy this data to a VBO to make this FAST - glColor4f(voxel->color.x, voxel->color.y, voxel->color.z, 1.0); - glutSolidCube(scale); - vRendered++; - } - return vRendered; + glBindBuffer(GL_ARRAY_BUFFER, vboVerticesID); + glVertexPointer(3, GL_FLOAT, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, vboColorsID); + glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); + + glNormal3f(0, 1, 0); + + glDrawElements(GL_TRIANGLES, 36 * voxelsRendered, GL_UNSIGNED_INT, 0); + + // deactivate vertex and color arrays after drawing + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + // bind with 0 to switch back to normal operation + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } void VoxelSystem::simulate(float deltaTime) { } - - - - diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 8dc3e97c8c..f18a104e99 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -9,11 +9,13 @@ #ifndef __interface__Cube__ #define __interface__Cube__ +#include "InterfaceConfig.h" #include +#include +#include +#include #include "Util.h" #include "world.h" -#include "InterfaceConfig.h" -#include const int NUM_CHILDREN = 8; @@ -22,17 +24,31 @@ struct Voxel { Voxel * children[NUM_CHILDREN]; }; -class VoxelSystem { +class VoxelSystem : public AgentData { public: + VoxelSystem(); + ~VoxelSystem(); + + void parseData(void *data, int size); + VoxelSystem* clone() const; + + void init(); void simulate(float deltaTime); int render(Voxel * voxel, float scale, glm::vec3 * distance); - void init(); + void render(); int initVoxels(Voxel * root, float scale, glm::vec3 * position); void setVoxelsRendered(int v) {voxelsRendered = v;}; int getVoxelsRendered() {return voxelsRendered;}; Voxel * root; private: int voxelsRendered; + GLfloat *verticesArray; + GLubyte *colorsArray; + GLfloat *lastAddPointer; + GLfloat *lastDrawPointer; + GLuint vboVerticesID; + GLuint vboColorsID; + GLuint vboIndicesID; }; #endif diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 4692189cb6..abe1206afa 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -44,6 +44,12 @@ #include "Oscilloscope.h" #include "UDPSocket.h" +#ifdef __APPLE__ + +#include "InterfaceMacOSX.h" + +#endif + using namespace std; int audio_on = 1; // Whether to turn on the audio support @@ -80,7 +86,7 @@ int fullscreen = 0; in_addr_t localAddress; -Oscilloscope audioScope(512,200,true); +Oscilloscope audioScope(256,200,true); #define HAND_RADIUS 0.25 // Radius of in-world 'hand' of you Head myHead; // The rendered head of oneself @@ -92,10 +98,10 @@ ParticleSystem balls(0, false, // Wrap? 0.02, // Noise 0.3, // Size scale - 0.0 // Gravity + 0.0 // Gravity ); -Cloud cloud(0, // Particles +Cloud cloud(0, // Particles box, // Bounding Box false // Wrap ); @@ -106,7 +112,7 @@ Lattice lattice(160,100); Finger myFinger(WIDTH, HEIGHT); Field field; -Audio audio(&audioScope); +Audio audio(&audioScope, &myHead); #define RENDER_FRAME_MSECS 8 int steps_per_frame = 0; @@ -314,9 +320,6 @@ void initDisplay(void) void init(void) { voxels.init(); - glm::vec3 position(0,0,0); - int voxelsMade = voxels.initVoxels(NULL, 10.0, &position); - std::cout << voxelsMade << " voxels made. \n"; myHead.setRenderYaw(start_yaw); @@ -329,11 +332,13 @@ void init(void) field = Field(); printf( "Field Initialized.\n" ); - if (noise_on) - { + if (noise_on) { myHead.setNoise(noise); } + char output[] = "I"; + char address[] = "10.0.0.10"; + agentList.getAgentSocket().send(address, 40106, output, 1); #ifdef MARKER_CAPTURE @@ -390,7 +395,20 @@ void reset_sensors() } } -void update_pos(float frametime) +void simulateHand(float deltaTime) { + // If mouse is being dragged, send current force to the hand controller + if (mouse_pressed == 1) + { + // Add a velocity to the hand corresponding to the detected size of the drag vector + const float MOUSE_HAND_FORCE = 1.5; + float dx = mouse_x - mouse_start_x; + float dy = mouse_y - mouse_start_y; + glm::vec3 vel(dx*MOUSE_HAND_FORCE, -dy*MOUSE_HAND_FORCE*(WIDTH/HEIGHT), 0); + myHead.hand->addVelocity(vel*deltaTime); + } +} + +void simulateHead(float frametime) // Using serial data, update avatar/render position and angles { // float measured_pitch_rate = serialPort.getRelativeValue(PITCH_RATE); @@ -499,7 +517,6 @@ void update_pos(float frametime) myHead.setRenderYaw(myHead.getRenderYaw() + render_yaw_rate); myHead.setRenderPitch(render_pitch); myHead.setPos(glm::vec3(location[0], location[1], location[2])); - audio.setSourcePosition(glm::vec3(location[0], location[1], location[2])); // Get audio loudness data from audio input device float loudness, averageLoudness; @@ -524,6 +541,7 @@ void display(void) glEnable(GL_LINE_SMOOTH); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); + glPushMatrix(); glLoadIdentity(); @@ -531,7 +549,7 @@ void display(void) glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - GLfloat light_position0[] = { 1.0, 1.0, 0.75, 0.0 }; + GLfloat light_position0[] = { 1.0, 1.0, 0.0, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position0); GLfloat ambient_color[] = { 0.125, 0.305, 0.5 }; glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color); @@ -539,10 +557,10 @@ void display(void) glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color); GLfloat specular_color[] = { 1.0, 1.0, 1.0, 1.0}; glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color); - + glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color); glMateriali(GL_FRONT, GL_SHININESS, 96); - + // Rotate, translate to camera location glRotatef(myHead.getRenderPitch(), 1, 0, 0); glRotatef(myHead.getRenderYaw(), 0, 1, 0); @@ -551,21 +569,14 @@ void display(void) glColor3f(1,0,0); glutSolidSphere(0.25, 15, 15); + // Draw cloud of dots glDisable( GL_POINT_SPRITE_ARB ); glDisable( GL_TEXTURE_2D ); if (!display_head) cloud.render(); // Draw voxels - glPushMatrix(); - glTranslatef(WORLD_SIZE/2.0, WORLD_SIZE/2.0, WORLD_SIZE/2.0); - glm::vec3 distance(5.0 + location[0], 5.0 + location[1], 5.0 + location[2]); - //std::cout << "length: " << glm::length(distance) << "\n"; - int voxelsRendered = voxels.render(NULL, 10.0, &distance); - voxels.setVoxelsRendered(voxelsRendered); - //glColor4f(0,0,1,0.5); - //glutSolidCube(10.0); - glPopMatrix(); + voxels.render(); // Draw field vectors if (display_field) field.render(); @@ -577,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(); } } @@ -591,12 +602,13 @@ 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); //render_vector(&test); - + + glPopMatrix(); // Render 2D overlay: I/O level bar graphs and text @@ -665,6 +677,7 @@ void display(void) glPopMatrix(); + glutSwapBuffers(); framecount++; } @@ -707,7 +720,12 @@ void key(unsigned char k, int x, int y) } } - if (k == 'h') display_head = !display_head; + + if (k == 'h') { + display_head = !display_head; + audio.setMixerLoopbackFlag(display_head); + } + if (k == 'm') head_mirror = !head_mirror; if (k == 'f') display_field = !display_field; @@ -739,7 +757,7 @@ void key(unsigned char k, int x, int y) // Receive packets from other agents/servers and decide what to do with them! // void *networkReceive(void *args) -{ +{ sockaddr senderAddress; ssize_t bytesReceived; char *incomingPacket = new char[MAX_PACKET_SIZE]; @@ -749,7 +767,14 @@ void *networkReceive(void *args) packetcount++; bytescount += bytesReceived; - agentList.processAgentData(&senderAddress, incomingPacket, bytesReceived); + if (incomingPacket[0] == 't') { + // Pass everything but transmitter data to the agent list + myHead.hand->processTransmitterData(incomingPacket, bytesReceived); + } else if (incomingPacket[0] == 'V') { + voxels.parseData(incomingPacket, bytesReceived); + } else { + agentList.processAgentData(&senderAddress, incomingPacket, bytesReceived); + } } } @@ -766,7 +791,9 @@ void idle(void) { steps_per_frame++; // Simulation - update_pos(1.f/FPS); + simulateHead(1.f/FPS); + simulateHand(1.f/FPS); + if (simulate_on) { field.simulate(1.f/FPS); myHead.simulate(1.f/FPS); @@ -830,22 +857,8 @@ void motionFunc( int x, int y) { mouse_x = x; mouse_y = y; - if (mouse_pressed == 1) - { - // Send network packet containing mouse location - char mouse_string[20]; - sprintf(mouse_string, "M %d %d\n", mouse_x, mouse_y); - //network_send(UDP_socket, mouse_string, strlen(mouse_string)); - - // Send dragged mouse vector to the hand; - float dx = mouse_x - mouse_start_x; - float dy = mouse_y - mouse_start_y; - glm::vec3 vel(dx*0.003, -dy*0.003, 0); - myHead.hand->addVelocity(vel); - } lattice.mouseClick((float)x/(float)WIDTH,(float)y/(float)HEIGHT); - } void mouseoverFunc( int x, int y) @@ -869,6 +882,12 @@ void audioMixerUpdate(in_addr_t newMixerAddress, in_port_t newMixerPort) { audio.updateMixerParams(newMixerAddress, newMixerPort); } +void voxelServerAddCallback(sockaddr *voxelServerAddress) { + char voxelAsk[] = "I"; + printf("Asking VS for data!\n"); + agentList.getAgentSocket().send(voxelServerAddress, voxelAsk, 1); +} + int main(int argc, char** argv) { struct ifaddrs * ifAddrStruct=NULL; @@ -924,12 +943,10 @@ int main(int argc, char** argv) // the callback for our instance of AgentList is attachNewHeadToAgent agentList.linkedDataCreateCallback = &attachNewHeadToAgent; agentList.audioMixerSocketUpdate = &audioMixerUpdate; + agentList.voxelServerAddCallback = &voxelServerAddCallback; // start the thread which checks for silent agents agentList.startSilentAgentRemovalThread(); - - // create thread for receipt of data via UDP - pthread_create(&networkReceiveThread, NULL, networkReceive, NULL); glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); @@ -953,10 +970,17 @@ int main(int argc, char** argv) init(); + // create thread for receipt of data via UDP + pthread_create(&networkReceiveThread, NULL, networkReceive, NULL); + printf( "Init() complete.\n" ); glutTimerFunc(1000, Timer, 0); - + +#ifdef __APPLE__ + initMacOSXMenu(&audioScope); +#endif + glutMainLoop(); ::terminate(); diff --git a/mixer/src/main.cpp b/mixer/src/main.cpp index 37093396ce..566b48dfed 100644 --- a/mixer/src/main.cpp +++ b/mixer/src/main.cpp @@ -19,98 +19,210 @@ const unsigned short MIXER_LISTEN_PORT = 55443; const float SAMPLE_RATE = 22050.0; -const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES/SAMPLE_RATE) * 1000000; -const short JITTER_BUFFER_MSECS = 20; +const short JITTER_BUFFER_MSECS = 2; const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0); +const int BUFFER_LENGTH_BYTES = 1024; +const int BUFFER_LENGTH_SAMPLES_PER_CHANNEL = (BUFFER_LENGTH_BYTES / 2) / sizeof(int16_t); + +const short RING_BUFFER_FRAMES = 10; +const short RING_BUFFER_SAMPLES = RING_BUFFER_FRAMES * BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + +const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000; + const long MAX_SAMPLE_VALUE = std::numeric_limits::max(); const long MIN_SAMPLE_VALUE = std::numeric_limits::min(); +const float DISTANCE_RATIO = 3.0/4.2; +const int PHASE_DELAY_AT_90 = 20; + +const int AGENT_LOOPBACK_MODIFIER = 307; + +const int LOOPBACK_SANITY_CHECK = 0; + char DOMAIN_HOSTNAME[] = "highfidelity.below92.com"; char DOMAIN_IP[100] = ""; // IP Address will be re-set by lookup on startup -const int DOMAINSERVER_PORT = 40102; - +const int DOMAINSERVER_PORT = 40102; AgentList agentList(MIXER_LISTEN_PORT); +void plateauAdditionOfSamples(int16_t &mixSample, int16_t sampleToAdd) { + long sumSample = sampleToAdd + mixSample; + + long normalizedSample = std::min(MAX_SAMPLE_VALUE, sumSample); + normalizedSample = std::max(MIN_SAMPLE_VALUE, sumSample); + + mixSample = normalizedSample; +} + void *sendBuffer(void *args) { int sentBytes; int nextFrame = 0; - timeval startTime; - - int16_t *clientMix = new int16_t[BUFFER_LENGTH_SAMPLES]; - long *masterMix = new long[BUFFER_LENGTH_SAMPLES]; + timeval startTime, lastSend; gettimeofday(&startTime, NULL); while (true) { sentBytes = 0; - for (int ms = 0; ms < BUFFER_LENGTH_SAMPLES; ms++) { - masterMix[ms] = 0; - } - - for (int ab = 0; ab < agentList.getAgents().size(); ab++) { - AudioRingBuffer *agentBuffer = (AudioRingBuffer *)agentList.getAgents()[ab].getLinkedData(); + for (int i = 0; i < agentList.getAgents().size(); i++) { + AudioRingBuffer *agentBuffer = (AudioRingBuffer *) agentList.getAgents()[i].getLinkedData(); if (agentBuffer != NULL && agentBuffer->getEndOfLastWrite() != NULL) { - if (!agentBuffer->isStarted() && agentBuffer->diffLastWriteNextOutput() <= BUFFER_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) { - printf("Held back buffer %d.\n", ab); - } else if (agentBuffer->diffLastWriteNextOutput() < BUFFER_LENGTH_SAMPLES) { - printf("Buffer %d starved.\n", ab); + + if (!agentBuffer->isStarted() + && agentBuffer->diffLastWriteNextOutput() <= BUFFER_LENGTH_SAMPLES_PER_CHANNEL + JITTER_BUFFER_SAMPLES) { + printf("Held back buffer %d.\n", i); + } else if (agentBuffer->diffLastWriteNextOutput() < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { + printf("Buffer %d starved.\n", i); agentBuffer->setStarted(false); } else { + + // check if we have more than we need to play out + int thresholdFrames = ceilf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL + JITTER_BUFFER_SAMPLES) / (float)BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + int thresholdSamples = thresholdFrames * BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + + if (agentBuffer->diffLastWriteNextOutput() > thresholdSamples) { + // we need to push the next output forwards + int samplesToPush = agentBuffer->diffLastWriteNextOutput() - thresholdSamples; + + if (agentBuffer->getNextOutput() + samplesToPush > agentBuffer->getBuffer()) { + agentBuffer->setNextOutput(agentBuffer->getBuffer() + (samplesToPush - (agentBuffer->getBuffer() + RING_BUFFER_SAMPLES - agentBuffer->getNextOutput()))); + } else { + agentBuffer->setNextOutput(agentBuffer->getNextOutput() + samplesToPush); + } + } + // good buffer, add this to the mix agentBuffer->setStarted(true); agentBuffer->setAddedToMix(true); - - for (int s = 0; s < BUFFER_LENGTH_SAMPLES; s++) { - masterMix[s] += agentBuffer->getNextOutput()[s]; - } - - agentBuffer->setNextOutput(agentBuffer->getNextOutput() + BUFFER_LENGTH_SAMPLES); - - if (agentBuffer->getNextOutput() >= agentBuffer->getBuffer() + RING_BUFFER_SAMPLES) { - agentBuffer->setNextOutput(agentBuffer->getBuffer()); - } } } } - for (int ab = 0; ab < agentList.getAgents().size(); ab++) { - Agent *agent = &agentList.getAgents()[ab]; - AudioRingBuffer *agentBuffer = (AudioRingBuffer *)agent->getLinkedData(); + int numAgents = agentList.getAgents().size(); + float distanceCoeffs[numAgents][numAgents]; + memset(distanceCoeffs, 0, sizeof(distanceCoeffs)); - int16_t *previousOutput = NULL; - - if (agentBuffer != NULL && agentBuffer->wasAddedToMix()) { - previousOutput = (agentBuffer->getNextOutput() == agentBuffer->getBuffer()) - ? agentBuffer->getBuffer() + RING_BUFFER_SAMPLES - BUFFER_LENGTH_SAMPLES - : agentBuffer->getNextOutput() - BUFFER_LENGTH_SAMPLES; - agentBuffer->setAddedToMix(false); - } - - for (int s = 0; s < BUFFER_LENGTH_SAMPLES; s++) { - long longSample = (previousOutput != NULL) - ? masterMix[s] - previousOutput[s] - : masterMix[s]; + for (int i = 0; i < agentList.getAgents().size(); i++) { + Agent *agent = &agentList.getAgents()[i]; - int16_t shortSample; + AudioRingBuffer *agentRingBuffer = (AudioRingBuffer *) agent->getLinkedData(); + float agentBearing = agentRingBuffer->getBearing(); + bool agentWantsLoopback = false; + + if (agentBearing > 180 || agentBearing < -180) { + // we were passed an invalid bearing because this agent wants loopback (pressed the H key) + agentWantsLoopback = true; - if (longSample < 0) { - shortSample = std::max(longSample, MIN_SAMPLE_VALUE); - } else { - shortSample = std::min(longSample, MAX_SAMPLE_VALUE); + // correct the bearing + agentBearing = agentBearing > 0 ? agentBearing - AGENT_LOOPBACK_MODIFIER : agentBearing + AGENT_LOOPBACK_MODIFIER; + } + + int16_t clientMix[BUFFER_LENGTH_SAMPLES_PER_CHANNEL * 2] = {}; + + + for (int j = 0; j < agentList.getAgents().size(); j++) { + if (i != j || ( i == j && agentWantsLoopback)) { + AudioRingBuffer *otherAgentBuffer = (AudioRingBuffer *)agentList.getAgents()[j].getLinkedData(); + + float *agentPosition = agentRingBuffer->getPosition(); + float *otherAgentPosition = otherAgentBuffer->getPosition(); + + // calculate the distance to the other agent + + // use the distance to the other agent to calculate the change in volume for this frame + int lowAgentIndex = std::min(i, j); + int highAgentIndex = std::max(i, j); + + if (distanceCoeffs[lowAgentIndex][highAgentIndex] == 0) { + float distanceToAgent = sqrtf(powf(agentPosition[0] - otherAgentPosition[0], 2) + + powf(agentPosition[1] - otherAgentPosition[1], 2) + + powf(agentPosition[2] - otherAgentPosition[2], 2)); + + distanceCoeffs[lowAgentIndex][highAgentIndex] = std::min(1.0f, powf(0.5, (logf(DISTANCE_RATIO * distanceToAgent) / logf(3)) - 1)); + } + + + // get the angle from the right-angle triangle + float triangleAngle = atan2f(fabsf(agentPosition[2] - otherAgentPosition[2]), fabsf(agentPosition[0] - otherAgentPosition[0])) * (180 / M_PI); + float angleToSource; + + if (agentWantsLoopback) { + + } + + // find the angle we need for calculation based on the orientation of the triangle + if (otherAgentPosition[0] > agentPosition[0]) { + if (otherAgentPosition[2] > agentPosition[2]) { + angleToSource = -90 + triangleAngle - agentBearing; + } else { + angleToSource = -90 - triangleAngle - agentBearing; + } + } else { + if (otherAgentPosition[2] > agentPosition[2]) { + angleToSource = 90 - triangleAngle - agentBearing; + } else { + angleToSource = 90 + triangleAngle - agentBearing; + } + } + + if (angleToSource > 180) { + angleToSource -= 360; + } else if (angleToSource < -180) { + angleToSource += 360; + } + + angleToSource *= (M_PI / 180); + + float sinRatio = fabsf(sinf(angleToSource)); + int numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; + + int16_t *goodChannel = angleToSource > 0 ? clientMix + BUFFER_LENGTH_SAMPLES_PER_CHANNEL : clientMix; + int16_t *delayedChannel = angleToSource > 0 ? clientMix : clientMix + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + + int16_t *delaySamplePointer = otherAgentBuffer->getNextOutput() == otherAgentBuffer->getBuffer() + ? otherAgentBuffer->getBuffer() + RING_BUFFER_SAMPLES - numSamplesDelay + : otherAgentBuffer->getNextOutput() - numSamplesDelay; + + + for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) { + + if (s < numSamplesDelay) { + // pull the earlier sample for the delayed channel + + int earlierSample = delaySamplePointer[s] * distanceCoeffs[lowAgentIndex][highAgentIndex]; + plateauAdditionOfSamples(delayedChannel[s], earlierSample); + } + + int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] * distanceCoeffs[lowAgentIndex][highAgentIndex]); + plateauAdditionOfSamples(goodChannel[s], currentSample); + + if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { + plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay], currentSample); + } + } } - - clientMix[s] = shortSample; } agentList.getAgentSocket().send(agent->getPublicSocket(), clientMix, BUFFER_LENGTH_BYTES); } + for (int i = 0; i < agentList.getAgents().size(); i++) { + AudioRingBuffer *agentBuffer = (AudioRingBuffer *)agentList.getAgents()[i].getLinkedData(); + if (agentBuffer->wasAddedToMix()) { + agentBuffer->setNextOutput(agentBuffer->getNextOutput() + BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + + if (agentBuffer->getNextOutput() >= agentBuffer->getBuffer() + RING_BUFFER_SAMPLES) { + agentBuffer->setNextOutput(agentBuffer->getBuffer()); + } + + agentBuffer->setAddedToMix(false); + } + } + double usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { @@ -133,6 +245,7 @@ void *reportAliveToDS(void *args) { *output = 'M'; packSocket(output + 1, 895283510, htons(MIXER_LISTEN_PORT)); +// packSocket(output + 1, 788637888, htons(MIXER_LISTEN_PORT)); agentList.getAgentSocket().send(DOMAIN_IP, DOMAINSERVER_PORT, output, 7); double usecToSleep = 1000000 - (usecTimestampNow() - usecTimestamp(&lastSend)); @@ -147,7 +260,7 @@ void *reportAliveToDS(void *args) { void attachNewBufferToAgent(Agent *newAgent) { if (newAgent->getLinkedData() == NULL) { - newAgent->setLinkedData(new AudioRingBuffer()); + newAgent->setLinkedData(new AudioRingBuffer(RING_BUFFER_SAMPLES, BUFFER_LENGTH_SAMPLES_PER_CHANNEL)); } } @@ -157,9 +270,7 @@ int main(int argc, const char * argv[]) agentList.linkedDataCreateCallback = attachNewBufferToAgent; - // setup the agentSocket to report to domain server - pthread_t reportAliveThread; - pthread_create(&reportAliveThread, NULL, reportAliveToDS, NULL); + agentList.startSilentAgentRemovalThread(); // Lookup the IP address of things we have hostnames if (atoi(DOMAIN_IP) == 0) { @@ -176,24 +287,45 @@ int main(int argc, const char * argv[]) } else { printf("Using static domainserver IP: %s\n", DOMAIN_IP); } + + // setup the agentSocket to report to domain server + pthread_t reportAliveThread; + pthread_create(&reportAliveThread, NULL, reportAliveToDS, NULL); unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; pthread_t sendBufferThread; pthread_create(&sendBufferThread, NULL, sendBuffer, NULL); + int16_t *loopbackAudioPacket; + if (LOOPBACK_SANITY_CHECK) { + loopbackAudioPacket = new int16_t[1024]; + } + sockaddr *agentAddress = new sockaddr; + timeval lastReceive; + gettimeofday(&lastReceive, NULL); while (true) { if(agentList.getAgentSocket().receive(agentAddress, packetData, &receivedBytes)) { - if (receivedBytes > BUFFER_LENGTH_BYTES) { + if (packetData[0] == 'I') { + + printf("Last receive was %f ms ago\n", (usecTimestampNow() - usecTimestamp(&lastReceive)) / 1000); + gettimeofday(&lastReceive, NULL); + // add or update the existing interface agent - agentList.addOrUpdateAgent(agentAddress, agentAddress, packetData[0]); - agentList.updateAgentWithData(agentAddress, (void *)packetData, receivedBytes); + if (!LOOPBACK_SANITY_CHECK) { + agentList.addOrUpdateAgent(agentAddress, agentAddress, packetData[0]); + agentList.updateAgentWithData(agentAddress, (void *)packetData, receivedBytes); + } else { + memcpy(loopbackAudioPacket, packetData + 1 + (sizeof(float) * 4), 1024); + agentList.getAgentSocket().send(agentAddress, loopbackAudioPacket, 1024); + } } } } + agentList.stopSilentAgentRemovalThread(); pthread_join(reportAliveThread, NULL); pthread_join(sendBufferThread, NULL); diff --git a/shared/src/AgentList.cpp b/shared/src/AgentList.cpp index d1c4db24ce..0bdedb10cd 100644 --- a/shared/src/AgentList.cpp +++ b/shared/src/AgentList.cpp @@ -11,7 +11,7 @@ #include #include "SharedUtil.h" -const char * SOLO_AGENT_TYPES_STRING = "M"; +const char * SOLO_AGENT_TYPES_STRING = "MV"; bool stopAgentRemovalThread = false; pthread_mutex_t vectorChangeMutex = PTHREAD_MUTEX_INITIALIZER; @@ -19,11 +19,13 @@ pthread_mutex_t vectorChangeMutex = PTHREAD_MUTEX_INITIALIZER; AgentList::AgentList() : agentSocket(AGENT_SOCKET_LISTEN_PORT) { linkedDataCreateCallback = NULL; audioMixerSocketUpdate = NULL; + voxelServerAddCallback = NULL; } AgentList::AgentList(int socketListenPort) : agentSocket(socketListenPort) { linkedDataCreateCallback = NULL; audioMixerSocketUpdate = NULL; + voxelServerAddCallback = NULL; } AgentList::~AgentList() { @@ -55,13 +57,15 @@ void AgentList::processAgentData(sockaddr *senderAddress, void *packetData, size case 'P': { // ping from another agent + //std::cout << "Got ping from " << inet_ntoa(((sockaddr_in *)senderAddress)->sin_addr) << "\n"; char reply[] = "R"; - agentSocket.send(senderAddress, reply, 1); + agentSocket.send(senderAddress, reply, 1); break; } case 'R': { // ping reply from another agent + //std::cout << "Got ping reply from " << inet_ntoa(((sockaddr_in *)senderAddress)->sin_addr) << "\n"; handlePingReply(senderAddress); break; } @@ -149,6 +153,8 @@ bool AgentList::addOrUpdateAgent(sockaddr *publicSocket, sockaddr *localSocket, // to use the local socket information the domain server gave us sockaddr_in *localSocketIn = (sockaddr_in *)localSocket; audioMixerSocketUpdate(localSocketIn->sin_addr.s_addr, localSocketIn->sin_port); + } else if (newAgent.getType() == 'V' && voxelServerAddCallback != NULL) { + voxelServerAddCallback(localSocket); } std::cout << "Added agent - " << &newAgent << "\n"; @@ -160,7 +166,7 @@ bool AgentList::addOrUpdateAgent(sockaddr *publicSocket, sockaddr *localSocket, return true; } else { - if (agent->getType() == 'M') { + if (agent->getType() == 'M' || agent->getType() == 'V') { // until the Audio class also uses our agentList, we need to update // the lastRecvTimeUsecs for the audio mixer so it doesn't get killed and re-added continously agent->setLastRecvTimeUsecs(usecTimestampNow()); diff --git a/shared/src/AgentList.h b/shared/src/AgentList.h index 4fb6a2d989..d4c9b69d23 100644 --- a/shared/src/AgentList.h +++ b/shared/src/AgentList.h @@ -27,6 +27,7 @@ class AgentList { void(*linkedDataCreateCallback)(Agent *); void(*audioMixerSocketUpdate)(in_addr_t, in_port_t); + void(*voxelServerAddCallback)(sockaddr *); std::vector& getAgents(); UDPSocket& getAgentSocket(); diff --git a/shared/src/AudioRingBuffer.cpp b/shared/src/AudioRingBuffer.cpp index e20860a7bf..f6443db066 100644 --- a/shared/src/AudioRingBuffer.cpp +++ b/shared/src/AudioRingBuffer.cpp @@ -9,22 +9,27 @@ #include #include "AudioRingBuffer.h" -AudioRingBuffer::AudioRingBuffer() { +AudioRingBuffer::AudioRingBuffer(int ringSamples, int bufferSamples) { + ringBufferLengthSamples = ringSamples; + bufferLengthSamples = bufferSamples; + started = false; addedToMix = false; endOfLastWrite = NULL; - buffer = new int16_t[RING_BUFFER_SAMPLES]; + buffer = new int16_t[ringBufferLengthSamples]; nextOutput = buffer; }; AudioRingBuffer::AudioRingBuffer(const AudioRingBuffer &otherRingBuffer) { + ringBufferLengthSamples = otherRingBuffer.ringBufferLengthSamples; + bufferLengthSamples = otherRingBuffer.bufferLengthSamples; started = otherRingBuffer.started; addedToMix = otherRingBuffer.addedToMix; - buffer = new int16_t[RING_BUFFER_SAMPLES]; - memcpy(buffer, otherRingBuffer.buffer, sizeof(int16_t) * RING_BUFFER_SAMPLES); + buffer = new int16_t[ringBufferLengthSamples]; + memcpy(buffer, otherRingBuffer.buffer, sizeof(int16_t) * ringBufferLengthSamples); nextOutput = buffer + (otherRingBuffer.nextOutput - otherRingBuffer.buffer); endOfLastWrite = buffer + (otherRingBuffer.endOfLastWrite - otherRingBuffer.buffer); @@ -74,33 +79,57 @@ void AudioRingBuffer::setAddedToMix(bool added) { addedToMix = added; } +float* AudioRingBuffer::getPosition() { + return position; +} + +void AudioRingBuffer::setPosition(float *newPosition) { + position[0] = newPosition[0]; + position[1] = newPosition[1]; + position[2] = newPosition[2]; +} + +float AudioRingBuffer::getBearing() { + return bearing; +} + +void AudioRingBuffer::setBearing(float newBearing) { + bearing = newBearing; +} + void AudioRingBuffer::parseData(void *data, int size) { - int16_t *audioDataStart = (int16_t *) data; + unsigned char *audioDataStart = (unsigned char *) data; - if (size > BUFFER_LENGTH_BYTES) { - float position[3]; - unsigned char *charData = (unsigned char *) data; + if (size > (bufferLengthSamples * sizeof(int16_t))) { + + unsigned char *dataPtr = audioDataStart + 1; for (int p = 0; p < 3; p ++) { - memcpy(&position[p], charData + 1 + (sizeof(float) * p), sizeof(float)); + memcpy(&position[p], dataPtr, sizeof(float)); + dataPtr += sizeof(float); } - audioDataStart = (int16_t *) charData + 1 + (sizeof(float) * 3); + memcpy(&bearing, dataPtr, sizeof(float)); + dataPtr += sizeof(float); + + audioDataStart = dataPtr; } - if (endOfLastWrite == NULL) { endOfLastWrite = buffer; - } else if (diffLastWriteNextOutput() > RING_BUFFER_SAMPLES - BUFFER_LENGTH_SAMPLES) { + } else if (diffLastWriteNextOutput() > ringBufferLengthSamples - bufferLengthSamples) { endOfLastWrite = buffer; nextOutput = buffer; started = false; } - memcpy(endOfLastWrite, audioDataStart, BUFFER_LENGTH_BYTES); - endOfLastWrite += BUFFER_LENGTH_SAMPLES; + memcpy(endOfLastWrite, audioDataStart, bufferLengthSamples * sizeof(int16_t)); - if (endOfLastWrite >= buffer + RING_BUFFER_SAMPLES) { + endOfLastWrite += bufferLengthSamples; + + addedToMix = false; + + if (endOfLastWrite >= buffer + ringBufferLengthSamples) { endOfLastWrite = buffer; } } @@ -113,7 +142,7 @@ short AudioRingBuffer::diffLastWriteNextOutput() short sampleDifference = endOfLastWrite - nextOutput; if (sampleDifference < 0) { - sampleDifference += RING_BUFFER_SAMPLES; + sampleDifference += ringBufferLengthSamples; } return sampleDifference; diff --git a/shared/src/AudioRingBuffer.h b/shared/src/AudioRingBuffer.h index c8a656728d..90d3550bb0 100644 --- a/shared/src/AudioRingBuffer.h +++ b/shared/src/AudioRingBuffer.h @@ -13,15 +13,9 @@ #include #include "AgentData.h" -const int BUFFER_LENGTH_BYTES = 1024; -const int BUFFER_LENGTH_SAMPLES = BUFFER_LENGTH_BYTES / sizeof(int16_t); - -const short RING_BUFFER_FRAMES = 10; -const short RING_BUFFER_SAMPLES = RING_BUFFER_FRAMES * BUFFER_LENGTH_SAMPLES; - class AudioRingBuffer : public AgentData { public: - AudioRingBuffer(); + AudioRingBuffer(int ringSamples, int bufferSamples); ~AudioRingBuffer(); AudioRingBuffer(const AudioRingBuffer &otherRingBuffer); @@ -37,9 +31,17 @@ class AudioRingBuffer : public AgentData { void setStarted(bool status); bool wasAddedToMix(); void setAddedToMix(bool added); + float* getPosition(); + void setPosition(float newPosition[]); + float getBearing(); + void setBearing(float newBearing); short diffLastWriteNextOutput(); private: + int ringBufferLengthSamples; + int bufferLengthSamples; + float position[3]; + float bearing; int16_t *nextOutput; int16_t *endOfLastWrite; int16_t *buffer; diff --git a/shared/src/SharedUtil.cpp b/shared/src/SharedUtil.cpp index d9de883024..4515eafe40 100644 --- a/shared/src/SharedUtil.cpp +++ b/shared/src/SharedUtil.cpp @@ -7,6 +7,7 @@ // #include "SharedUtil.h" +#include double usecTimestamp(timeval *time) { return (time->tv_sec * 1000000.0 + time->tv_usec); @@ -16,4 +17,9 @@ double usecTimestampNow() { timeval now; gettimeofday(&now, NULL); return (now.tv_sec * 1000000.0 + now.tv_usec); +} + + +float randFloat () { + return (rand()%10000)/10000.f; } \ No newline at end of file diff --git a/shared/src/SharedUtil.h b/shared/src/SharedUtil.h index c8b88a8a55..d5525ffdb9 100644 --- a/shared/src/SharedUtil.h +++ b/shared/src/SharedUtil.h @@ -14,5 +14,6 @@ double usecTimestamp(timeval *time); double usecTimestampNow(); +float randFloat(); #endif /* defined(__hifi__SharedUtil__) */ diff --git a/voxel/CMakeLists.txt b/voxel/CMakeLists.txt new file mode 100644 index 0000000000..627dafdfcd --- /dev/null +++ b/voxel/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.8) + +project(voxel) + +file(GLOB VOXEL_SRCS src/*.cpp src/*.h) + +add_executable(voxel ${VOXEL_SRCS}) + +include(../LinkHifiShared.cmake) +link_hifi_shared_library(voxel) \ No newline at end of file diff --git a/voxel/src/main.cpp b/voxel/src/main.cpp new file mode 100644 index 0000000000..689395ac5b --- /dev/null +++ b/voxel/src/main.cpp @@ -0,0 +1,192 @@ +// +// main.cpp +// Voxel Server +// +// Created by Stephen Birara on 03/06/13. +// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include + +const int VOXEL_LISTEN_PORT = 40106; + +const int NUMBER_OF_VOXELS = 250000; + +const float MAX_UNIT_ANY_AXIS = 20.0f; + +const int VERTICES_PER_VOXEL = 8; +const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; +const int COLOR_VALUES_PER_VOXEL = 3 * VERTICES_PER_VOXEL; + +const int VOXEL_SIZE_BYTES = 3 + (3 * sizeof(float)); +const int VOXELS_PER_PACKET = (MAX_PACKET_SIZE - 1) / VOXEL_SIZE_BYTES; + +const int MIN_BRIGHTNESS = 64; +const float DEATH_STAR_RADIUS = 4.0; +const float MAX_CUBE = 0.05; + +char DOMAIN_HOSTNAME[] = "highfidelity.below92.com"; +char DOMAIN_IP[100] = "192.168.1.47"; // IP Address will be re-set by lookup on startup +const int DOMAINSERVER_PORT = 40102; + +AgentList agentList(VOXEL_LISTEN_PORT); +in_addr_t localAddress; + +unsigned char randomColorValue() { + return MIN_BRIGHTNESS + (rand() % (255 - MIN_BRIGHTNESS)); +} + +void *reportAliveToDS(void *args) { + + timeval lastSend; + unsigned char output[7]; + + while (true) { + gettimeofday(&lastSend, NULL); + + *output = 'V'; + packSocket(output + 1, localAddress, htons(VOXEL_LISTEN_PORT)); + agentList.getAgentSocket().send(DOMAIN_IP, DOMAINSERVER_PORT, output, 7); + + double usecToSleep = 1000000 - (usecTimestampNow() - usecTimestamp(&lastSend)); + + if (usecToSleep > 0) { + usleep(usecToSleep); + } else { + std::cout << "No sleep required!"; + } + } +} + +int main(int argc, const char * argv[]) +{ + // get the local address of the voxel server + struct ifaddrs * ifAddrStruct=NULL; + struct ifaddrs * ifa=NULL; + + getifaddrs(&ifAddrStruct); + + for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4 + // is a valid IP4 Address + localAddress = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; + } + } + + // Lookup the IP address of things we have hostnames + if (atoi(DOMAIN_IP) == 0) { + struct hostent* pHostInfo; + if ((pHostInfo = gethostbyname(DOMAIN_HOSTNAME)) != NULL) { + sockaddr_in tempAddress; + memcpy(&tempAddress.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length); + strcpy(DOMAIN_IP, inet_ntoa(tempAddress.sin_addr)); + printf("Domain server %s: %s\n", DOMAIN_HOSTNAME, DOMAIN_IP); + + } else { + printf("Failed lookup domainserver\n"); + } + } else { + printf("Using static domainserver IP: %s\n", DOMAIN_IP); + } + + // setup the agentSocket to report to domain server + pthread_t reportAliveThread; + pthread_create(&reportAliveThread, NULL, reportAliveToDS, NULL); + + // new seed based on time now so voxels are different each time + srand((unsigned)time(0)); + + // create the vertex and color arrays to send back to requesting clients + + // setup data structures for our array of points and array of colors + float *pointsArray = new float[NUMBER_OF_VOXELS * 3]; + char *colorsArray = new char[NUMBER_OF_VOXELS * 3]; + + for (int n = 0; n < NUMBER_OF_VOXELS; n++) { + + // pick a random point for the center of the cube + float azimuth = randFloat() * 2 * M_PI; + float altitude = randFloat() * M_PI - M_PI/2; + float radius = DEATH_STAR_RADIUS; + float radius_twiddle = (DEATH_STAR_RADIUS/100) * powf(2, (float)(rand()%8)); + float thisScale = MAX_CUBE * 1 / (float)(rand() % 8 + 1); + radius += radius_twiddle + (randFloat() * DEATH_STAR_RADIUS/12 - DEATH_STAR_RADIUS / 24); + + // fill the vertices array + float *currentPointsPos = pointsArray + (n * 3); + currentPointsPos[0] = radius * cosf(azimuth) * cosf(altitude); + currentPointsPos[1] = radius * sinf(azimuth) * cosf(altitude); + currentPointsPos[2] = radius * sinf(altitude); + + // fill the colors array + char *currentColorPos = colorsArray + (n * 3); + currentColorPos[0] = randomColorValue(); + currentColorPos[1] = randomColorValue(); + currentColorPos[2] = randomColorValue(); + } + + // we need space for each voxel and for the 'V' characters at the beginning of each voxel packet + int voxelDataBytes = VOXEL_SIZE_BYTES * NUMBER_OF_VOXELS + ceil(NUMBER_OF_VOXELS / VOXELS_PER_PACKET); + char *voxelData = new char[voxelDataBytes]; + + // setup the interleaved voxelData packet + for (int v = 0; v < NUMBER_OF_VOXELS; v++) { + char *startPointer = voxelData + (v * VOXEL_SIZE_BYTES) + ((v / VOXELS_PER_PACKET) + 1); + + // if this is the start of a voxel packet we need to prepend with a 'V' + if (v % VOXELS_PER_PACKET == 0) { + *(startPointer - 1) = 'V'; + } + + memcpy(startPointer, pointsArray + (v * 3), sizeof(float) * 3); + memcpy(startPointer + (3 * sizeof(float)), colorsArray + (v * 3), 3); + } + + // delete the pointsArray and colorsArray that we no longer need + delete[] pointsArray; + delete[] colorsArray; + + sockaddr_in agentPublicAddress; + + char *packetData = new char[MAX_PACKET_SIZE]; + ssize_t receivedBytes; + + int sentVoxels = 0; + + // loop to send to agents requesting data + while (true) { + if (agentList.getAgentSocket().receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes)) { + if (packetData[0] == 'I') { + printf("Sending voxels to agent at address %s\n", inet_ntoa(agentPublicAddress.sin_addr)); + + // send the agent all voxels for now + // each packet has VOXELS_PER_PACKET, unless it's the last + while (sentVoxels != NUMBER_OF_VOXELS) { + int voxelsToSend = std::min(VOXELS_PER_PACKET, NUMBER_OF_VOXELS - sentVoxels); + + agentList.getAgentSocket().send((sockaddr *)&agentPublicAddress, + voxelData + (sentVoxels * VOXEL_SIZE_BYTES) + ((sentVoxels / VOXELS_PER_PACKET)), + (voxelsToSend * VOXEL_SIZE_BYTES + 1)); + + // sleep for 500 microseconds to not overload send and have lost packets + usleep(500); + + sentVoxels += voxelsToSend; + } + + printf("Sent %d voxels to agent.\n", sentVoxels); + sentVoxels = 0; + } + } + } + + pthread_join(reportAliveThread, NULL); + + return 0; +} \ No newline at end of file