merge conflicts

This commit is contained in:
stojce 2013-03-12 18:12:58 +01:00
commit 8e6c98661b
26 changed files with 1129 additions and 372 deletions

View file

@ -5,4 +5,5 @@ project(hifi)
add_subdirectory(space) add_subdirectory(space)
add_subdirectory(domain) add_subdirectory(domain)
add_subdirectory(mixer) add_subdirectory(mixer)
add_subdirectory(voxel)
add_subdirectory(interface) add_subdirectory(interface)

View file

@ -68,10 +68,10 @@ int main(int argc, const char * argv[])
agentList.startSilentAgentRemovalThread(); agentList.startSilentAgentRemovalThread();
std::map<char, Agent *> newestSoloAgents;
while (true) { while (true) {
if (agentList.getAgentSocket().receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes)) { if (agentList.getAgentSocket().receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes)) {
std::map<char, Agent *> newestSoloAgents;
agentType = packetData[0]; agentType = packetData[0];
unpackSocket(&packetData[1], (sockaddr *)&agentLocalAddress); unpackSocket(&packetData[1], (sockaddr *)&agentLocalAddress);

View file

@ -10,7 +10,7 @@ project(interface)
if (APPLE) if (APPLE)
# link in required OS X frameworks and include the right GL headers # 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 <GLUT/glut.h>\n#include <OpenGL/glext.h>") set(GL_HEADERS "#include <GLUT/glut.h>\n#include <OpenGL/glext.h>")
else (APPLE) else (APPLE)
# include the right GL headers for UNIX # include the right GL headers for UNIX
@ -21,7 +21,7 @@ endif (APPLE)
configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h) configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h)
# grab the implementation and header files from src dir # 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) if (APPLE)
# set how the icon shows up in the Info.plist file # set how the icon shows up in the Info.plist file

View file

@ -14,22 +14,36 @@
#include <cstring> #include <cstring>
#include "Audio.h" #include "Audio.h"
#include "Util.h" #include "Util.h"
#include <SharedUtil.h>
#include "UDPSocket.h" #include "UDPSocket.h"
Oscilloscope * scope; Oscilloscope * scope;
const short PACKET_LENGTH_BYTES = 1024; const int NUM_AUDIO_CHANNELS = 2;
const short PACKET_LENGTH_SAMPLES = PACKET_LENGTH_BYTES / sizeof(int16_t);
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 int PHASE_DELAY_AT_90 = 20;
const float AMPLITUDE_RATIO_AT_90 = 0.5; const float AMPLITUDE_RATIO_AT_90 = 0.5;
const int SAMPLE_RATE = 22050; const int SAMPLE_RATE = 22050;
const float JITTER_BUFFER_LENGTH_MSECS = 30.0; const float JITTER_BUFFER_LENGTH_MSECS = 4;
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS * (SAMPLE_RATE / 1000.0); const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS *
NUM_AUDIO_CHANNELS * (SAMPLE_RATE / 1000.0);
const short NUM_AUDIO_SOURCES = 2; const float AUDIO_CALLBACK_MSECS = (float)BUFFER_LENGTH_SAMPLES / (float)SAMPLE_RATE * 1000.0;
const short ECHO_SERVER_TEST = 1;
const int AGENT_LOOPBACK_MODIFIER = 307;
const char LOCALHOST_MIXER[] = "0.0.0.0"; const char LOCALHOST_MIXER[] = "0.0.0.0";
const char WORKCLUB_MIXER[] = "192.168.1.19"; const char WORKCLUB_MIXER[] = "192.168.1.19";
@ -41,7 +55,10 @@ int starve_counter = 0;
StDev stdev; StDev stdev;
bool stopAudioReceiveThread = false; bool stopAudioReceiveThread = false;
#define LOG_SAMPLE_DELAY 1 timeval firstPlaybackTimer;
int packetsReceivedThisPlayback = 0;
#define LOG_SAMPLE_DELAY 0
std::ofstream logFile; std::ofstream logFile;
@ -77,6 +94,8 @@ int audioCallback (const void *inputBuffer,
int16_t *inputLeft = ((int16_t **) inputBuffer)[0]; int16_t *inputLeft = ((int16_t **) inputBuffer)[0];
// int16_t *inputRight = ((int16_t **) inputBuffer)[1]; // int16_t *inputRight = ((int16_t **) inputBuffer)[1];
//printf("Audio callback at %6.0f\n", usecTimestampNow()/1000);
if (inputLeft != NULL) { if (inputLeft != NULL) {
if (data->mixerAddress != 0) { if (data->mixerAddress != 0) {
@ -85,24 +104,40 @@ int audioCallback (const void *inputBuffer,
audioMixerSocket.sin_addr.s_addr = data->mixerAddress; audioMixerSocket.sin_addr.s_addr = data->mixerAddress;
audioMixerSocket.sin_port = data->mixerPort; 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 // 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'; dataPacket[0] = 'I';
unsigned char *currentPacketPtr = dataPacket + 1;
// memcpy the three float positions // memcpy the three float positions
for (int p = 0; p < 3; p++) { 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 the corrected render yaw
memcpy(dataPacket + leadingBytes, inputLeft, BUFFER_LENGTH_BYTES); 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); 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++) { for (int i = 0; i < BUFFER_LENGTH_SAMPLES; i++) {
loudness += abs(inputLeft[i]); loudness += abs(inputLeft[i]);
} }
loudness /= BUFFER_LENGTH_SAMPLES; loudness /= BUFFER_LENGTH_SAMPLES;
data->lastInputLoudness = loudness; data->lastInputLoudness = loudness;
data->averagedInputLoudness = 0.66*data->averagedInputLoudness + 0.33*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 *outputLeft = ((int16_t **) outputBuffer)[0];
int16_t *outputRight = ((int16_t **) outputBuffer)[1]; int16_t *outputRight = ((int16_t **) outputBuffer)[1];
memset(outputLeft, 0, BUFFER_LENGTH_BYTES); memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
memset(outputRight, 0, BUFFER_LENGTH_BYTES); memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
// 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);
}
}
AudioRingBuffer *ringBuffer = data->ringBuffer; 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 // if we've been reset, and there isn't any new packets yet
// just play some silence // just play some silence
if (ringBuffer->getEndOfLastWrite() != NULL) { if (ringBuffer->getEndOfLastWrite() != NULL) {
if (!ringBuffer->isStarted() && ringBuffer->diffLastWriteNextOutput() <= PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) { if (!ringBuffer->isStarted() && ringBuffer->diffLastWriteNextOutput() < PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) {
printf("Held back\n"); 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) { } else if (ringBuffer->diffLastWriteNextOutput() < PACKET_LENGTH_SAMPLES) {
ringBuffer->setStarted(false); ringBuffer->setStarted(false);
starve_counter++; starve_counter++;
packetsReceivedThisPlayback = 0;
printf("Starved #%d\n", starve_counter); printf("Starved #%d\n", starve_counter);
data->wasStarved = 10; // Frames to render the indication that the system was starved. data->wasStarved = 10; // Frames to render the indication that the system was starved.
} else { } 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 // 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 // check if we have more than we need to play out
memcpy(queueBuffer, ringBuffer->getNextOutput(), BUFFER_LENGTH_BYTES); 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) { if (ringBuffer->getNextOutput() == ringBuffer->getBuffer() + RING_BUFFER_SAMPLES) {
ringBuffer->setNextOutput(ringBuffer->getBuffer()); 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); gettimeofday(&data->lastCallback, NULL);
return paContinue; return paContinue;
} }
@ -192,7 +236,7 @@ void *receiveAudioViaUDP(void *args) {
AudioRecThreadStruct *threadArgs = (AudioRecThreadStruct *) args; AudioRecThreadStruct *threadArgs = (AudioRecThreadStruct *) args;
AudioData *sharedAudioData = threadArgs->sharedAudioData; 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; ssize_t receivedBytes;
timeval previousReceiveTime, currentReceiveTime = {}; timeval previousReceiveTime, currentReceiveTime = {};
@ -243,6 +287,17 @@ void *receiveAudioViaUDP(void *args) {
} }
AudioRingBuffer *ringBuffer = sharedAudioData->ringBuffer; 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); ringBuffer->parseData(receivedData, PACKET_LENGTH_BYTES);
if (LOG_SAMPLE_DELAY) { if (LOG_SAMPLE_DELAY) {
@ -254,8 +309,12 @@ void *receiveAudioViaUDP(void *args) {
pthread_exit(0); pthread_exit(0);
} }
void Audio::setSourcePosition(glm::vec3 newPosition) { void Audio::setMixerLoopbackFlag(bool newMixerLoopbackFlag) {
audioData->sourcePosition = newPosition; 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. * @return Returns true if successful or false if an error occurred.
Use Audio::getError() to retrieve the error code. Use Audio::getError() to retrieve the error code.
*/ */
Audio::Audio(Oscilloscope * s) Audio::Audio(Oscilloscope *s, Head *linkedHead)
{ {
paError = Pa_Initialize(); paError = Pa_Initialize();
if (paError != paNoError) goto error; if (paError != paNoError) goto error;
scope = s; scope = s;
audioData = new AudioData(BUFFER_LENGTH_BYTES); audioData = new AudioData();
audioData->linkedHead = linkedHead;
// setup a UDPSocket // setup a UDPSocket
audioData->audioSocket = new UDPSocket(AUDIO_UDP_LISTEN_PORT); audioData->audioSocket = new UDPSocket(AUDIO_UDP_LISTEN_PORT);
audioData->ringBuffer = new AudioRingBuffer(); audioData->ringBuffer = new AudioRingBuffer(RING_BUFFER_SAMPLES, PACKET_LENGTH_SAMPLES);
AudioRecThreadStruct threadArgs; AudioRecThreadStruct threadArgs;
threadArgs.sharedAudioData = audioData; threadArgs.sharedAudioData = audioData;
@ -287,8 +348,8 @@ Audio::Audio(Oscilloscope * s)
2, // input channels 2, // input channels
2, // output channels 2, // output channels
(paInt16 | paNonInterleaved), // sample format (paInt16 | paNonInterleaved), // sample format
22050, // sample rate (hz) SAMPLE_RATE, // sample rate (hz)
512, // frames per buffer BUFFER_LENGTH_SAMPLES, // frames per buffer
audioCallback, // callback function audioCallback, // callback function
(void *) audioData); // user data to be passed to callback (void *) audioData); // user data to be passed to callback
if (paError != paNoError) goto error; if (paError != paNoError) goto error;
@ -317,7 +378,7 @@ void Audio::getInputLoudness(float * lastLoudness, float * averageLoudness) {
void Audio::render(int screenWidth, int screenHeight) void Audio::render(int screenWidth, int screenHeight)
{ {
if (initialized && ECHO_SERVER_TEST) { if (initialized) {
glBegin(GL_LINES); glBegin(GL_LINES);
glColor3f(1,1,1); glColor3f(1,1,1);
@ -349,10 +410,14 @@ void Audio::render(int screenWidth, int screenHeight)
timeval currentTime; timeval currentTime;
gettimeofday(&currentTime, NULL); gettimeofday(&currentTime, NULL);
float timeLeftInCurrentBuffer = 0; float timeLeftInCurrentBuffer = 0;
if (audioData->lastCallback.tv_usec > 0) timeLeftInCurrentBuffer = diffclock(&audioData->lastCallback, &currentTime)/(1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE) * frameWidth; if (audioData->lastCallback.tv_usec > 0) {
timeLeftInCurrentBuffer = AUDIO_CALLBACK_MSECS - diffclock(&audioData->lastCallback, &currentTime);
}
// /(1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE) * frameWidth
if (audioData->ringBuffer->getEndOfLastWrite() != NULL) 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); if (audioData->wasStarved == 0) glColor3f(0, 1, 0);
else { else {
@ -362,8 +427,8 @@ void Audio::render(int screenWidth, int screenHeight)
glBegin(GL_QUADS); glBegin(GL_QUADS);
glVertex2f(startX, topY + 5); glVertex2f(startX, topY + 5);
glVertex2f(startX + remainingBuffer + timeLeftInCurrentBuffer, topY + 5); glVertex2f(startX + (remainingBuffer + timeLeftInCurrentBuffer)/AUDIO_CALLBACK_MSECS*frameWidth, topY + 5);
glVertex2f(startX + remainingBuffer + timeLeftInCurrentBuffer, bottomY - 5); glVertex2f(startX + (remainingBuffer + timeLeftInCurrentBuffer)/AUDIO_CALLBACK_MSECS*frameWidth, bottomY - 5);
glVertex2f(startX, bottomY - 5); glVertex2f(startX, bottomY - 5);
glEnd(); 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) // Show a yellow bar with the averaged msecs latency you are hearing (from time of packet receipt)
glColor3f(1,1,0); glColor3f(1,1,0);
glBegin(GL_QUADS); glBegin(GL_QUADS);
glVertex2f(startX + audioData->averagedLatency - 2, topY - 2); glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth - 2, topY - 2);
glVertex2f(startX + audioData->averagedLatency + 2, topY - 2); glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth + 2, topY - 2);
glVertex2f(startX + audioData->averagedLatency + 2, bottomY + 2); glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth + 2, bottomY + 2);
glVertex2f(startX + audioData->averagedLatency - 2, bottomY + 2); glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth - 2, bottomY + 2);
glEnd(); glEnd();
char out[20]; char out[40];
sprintf(out, "%3.0f\n", audioData->averagedLatency/(float)frameWidth*(1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE)); sprintf(out, "%3.0f\n", audioData->averagedLatency);
drawtext(startX + audioData->averagedLatency - 10, topY-10, 0.08, 0, 1, 0, out, 1,1,0); 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 // 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); glColor3f(0,1,1);
glBegin(GL_QUADS); glBegin(GL_QUADS);

View file

@ -13,15 +13,19 @@
#include <portaudio.h> #include <portaudio.h>
#include "AudioData.h" #include "AudioData.h"
#include "Oscilloscope.h" #include "Oscilloscope.h"
#include "Head.h"
class Audio { class Audio {
public: public:
// initializes audio I/O // initializes audio I/O
Audio(Oscilloscope * s); Audio(Oscilloscope *s, Head *linkedHead);
void render(); void render();
void render(int screenWidth, int screenHeight); void render(int screenWidth, int screenHeight);
bool getMixerLoopbackFlag();
void setMixerLoopbackFlag(bool newMixerLoopbackFlag);
void getInputLoudness(float * lastLoudness, float * averageLoudness); void getInputLoudness(float * lastLoudness, float * averageLoudness);
void updateMixerParams(in_addr_t mixerAddress, in_port_t mixerPort); void updateMixerParams(in_addr_t mixerAddress, in_port_t mixerPort);

View file

@ -8,20 +8,20 @@
#include "AudioData.h" #include "AudioData.h"
AudioData::AudioData(int bufferLength) { AudioData::AudioData() {
mixerAddress = 0; mixerAddress = 0;
mixerPort = 0; mixerPort = 0;
samplesToQueue = new int16_t[bufferLength / sizeof(int16_t)];
averagedLatency = 0.0; averagedLatency = 0.0;
lastCallback.tv_usec = 0; lastCallback.tv_usec = 0;
wasStarved = 0; wasStarved = 0;
measuredJitter = 0; measuredJitter = 0;
jitterBuffer = 0; jitterBuffer = 0;
mixerLoopbackFlag = false;
} }
AudioData::~AudioData() { AudioData::~AudioData() {
delete[] samplesToQueue;
delete audioSocket; delete audioSocket;
} }

View file

@ -14,18 +14,17 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "AudioRingBuffer.h" #include "AudioRingBuffer.h"
#include "UDPSocket.h" #include "UDPSocket.h"
#include "Head.h"
class AudioData { class AudioData {
public: public:
AudioData(int bufferLength); AudioData();
~AudioData(); ~AudioData();
AudioRingBuffer *ringBuffer; AudioRingBuffer *ringBuffer;
UDPSocket *audioSocket; UDPSocket *audioSocket;
int16_t *samplesToQueue; Head *linkedHead;
glm::vec3 sourcePosition;
// store current mixer address and port // store current mixer address and port
in_addr_t mixerAddress; in_addr_t mixerAddress;
@ -39,6 +38,8 @@ class AudioData {
float lastInputLoudness; float lastInputLoudness;
float averagedInputLoudness; float averagedInputLoudness;
bool mixerLoopbackFlag;
}; };
#endif /* defined(__interface__AudioData__) */ #endif /* defined(__interface__AudioData__) */

View file

@ -7,41 +7,24 @@
// //
#include "Hand.h" #include "Hand.h"
#include <sys/time.h>
const float PHI = 1.618; const float PHI = 1.618;
const float DEFAULT_X = 0; const float DEFAULT_X = 0;
const float DEFAULT_Y = -1.5; const float DEFAULT_Y = -1.5;
const float DEFAULT_Z = 2.0; const float DEFAULT_Z = 2.0;
const float DEFAULT_TRANSMITTER_HZ = 60.0;
Hand::Hand(glm::vec3 initcolor) Hand::Hand(glm::vec3 initcolor)
{ {
color = initcolor; color = initcolor;
reset(); reset();
noise = 0.2; noise = 0.0; //0.2;
scale.x = 0.07; scale.x = 0.07;
scale.y = scale.x * 5.0; scale.y = scale.x * 5.0;
scale.z = scale.y * 1.0; scale.z = scale.y * 1.0;
} renderPointer = true;
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();
} }
void Hand::reset() void Hand::reset()
@ -53,18 +36,117 @@ void Hand::reset()
pitchRate = yawRate = rollRate = 0; pitchRate = yawRate = rollRate = 0;
setTarget(position); setTarget(position);
velocity.x = velocity.y = velocity.z = 0; 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) void Hand::simulate(float deltaTime)
{ {
const float VNOISE = 0.01; const float ANGULAR_SPRING_CONSTANT = 0.25;
const float RSPRING = 0.01; const float ANGULAR_DAMPING_COEFFICIENT = 5*2.0*powf(ANGULAR_SPRING_CONSTANT,0.5);
const float PSPRING = 0.4; const float LINEAR_SPRING_CONSTANT = 100;
const float RNOISE = 0.1; const float LINEAR_DAMPING_COEFFICIENT = 2.0*powf(LINEAR_SPRING_CONSTANT,0.5);
const float VDECAY = 5.0;
// If noise, add a bit of random velocity // If noise, add a bit of random velocity
const float RNOISE = 0.0;
const float VNOISE = 0.01;
if (noise) { if (noise) {
glm::vec3 nVel(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f); glm::vec3 nVel(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f);
nVel *= VNOISE; nVel *= VNOISE;
addVelocity(nVel); addVelocity(nVel);
@ -79,24 +161,60 @@ void Hand::simulate(float deltaTime)
yaw += yawRate; yaw += yawRate;
roll += rollRate; roll += rollRate;
// Spring effect to return hand to target; // The spring method
glm::vec3 sVel = target - position; if (0) {
sVel *= PSPRING; // Use a linear spring to attempt to return the hand to the target position
addVelocity(sVel); glm::vec3 springForce = target - position;
// Decay position of hand toward target springForce *= LINEAR_SPRING_CONSTANT;
//position -= deltaTime*(position - target); addVelocity(springForce * deltaTime);
// Decay velocity // Critically damp the linear spring
velocity *= 1.0 - deltaTime*VDECAY; glm::vec3 dampingForce(velocity);
dampingForce *= LINEAR_DAMPING_COEFFICIENT;
addVelocity(-dampingForce * deltaTime);
// Decay Angular Velocity // Use angular spring to return hand to target rotation (0,0,0)
pitchRate *= 1.0 - deltaTime; addAngularVelocity(-pitch * ANGULAR_SPRING_CONSTANT * deltaTime,
yawRate *= 1.0 - deltaTime; -yaw * ANGULAR_SPRING_CONSTANT * deltaTime,
rollRate *= 1.0 - deltaTime; -roll * ANGULAR_SPRING_CONSTANT * deltaTime);
// Add spring effect to return hand rotation to zero // Critically damp angular spring
pitchRate -= pitch * RSPRING; addAngularVelocity(-pitchRate*ANGULAR_DAMPING_COEFFICIENT*deltaTime,
yawRate -= yaw * RSPRING; -yawRate*ANGULAR_DAMPING_COEFFICIENT*deltaTime,
rollRate -= roll * RSPRING; -rollRate*ANGULAR_DAMPING_COEFFICIENT*deltaTime);
}
// 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;
}
} }

View file

@ -20,7 +20,7 @@ class Hand {
public: public:
Hand(glm::vec3 color); Hand(glm::vec3 color);
void simulate (float deltaTime); void simulate (float deltaTime);
void render (); void render (int isMine);
void reset (); void reset ();
void setNoise (float mag) { noise = mag; }; void setNoise (float mag) { noise = mag; };
void addVelocity (glm::vec3 v) { velocity += v; }; void addVelocity (glm::vec3 v) { velocity += v; };
@ -28,11 +28,17 @@ public:
glm::vec3 getPos() { return position; }; glm::vec3 getPos() { return position; };
void setPos(glm::vec3 p) { position = p; }; void setPos(glm::vec3 p) { position = p; };
void setTarget(glm::vec3 t) { target = t; }; void setTarget(glm::vec3 t) { target = t; };
void processTransmitterData(char * packetData, int numBytes);
float getTransmitterHz() { return transmitterHz; };
void setRenderPointer(bool p) { renderPointer = p; };
private: private:
glm::vec3 position, target, velocity, color, scale; glm::vec3 position, target, velocity, color, scale;
float pitch, yaw, roll, pitchRate, yawRate, rollRate; float pitch, yaw, roll, pitchRate, yawRate, rollRate;
float noise; float noise;
timeval transmitterTimer;
float transmitterHz;
int transmitterPackets;
bool renderPointer;
}; };

View file

@ -47,9 +47,9 @@ Head::Head()
interBrowDistance = 0.75; interBrowDistance = 0.75;
NominalPupilSize = 0.10; NominalPupilSize = 0.10;
Yaw = 0.0; Yaw = 0.0;
EyebrowPitch[0] = EyebrowPitch[1] = BrowPitchAngle[0]; EyebrowPitch[0] = EyebrowPitch[1] = -30;
EyebrowRoll[0] = 30; EyebrowRoll[0] = 20;
EyebrowRoll[1] = -30; EyebrowRoll[1] = -20;
MouthPitch = 0; MouthPitch = 0;
MouthYaw = 0; MouthYaw = 0;
MouthWidth = 1.0; MouthWidth = 1.0;
@ -67,6 +67,12 @@ Head::Head()
scale = 1.0; scale = 1.0;
renderYaw = 0.0; renderYaw = 0.0;
renderPitch = 0.0; renderPitch = 0.0;
audioAttack = 0.0;
loudness = 0.0;
averageLoudness = 0.0;
lastLoudness = 0.0;
browAudioLift = 0.0;
setNoise(0); setNoise(0);
hand = new Hand(glm::vec3(skinColor[0], skinColor[1], skinColor[2])); hand = new Hand(glm::vec3(skinColor[0], skinColor[1], skinColor[2]));
@ -89,6 +95,10 @@ Head* Head::clone() const {
return new Head(*this); return new Head(*this);
} }
Head* Head::clone() const {
return new Head(*this);
}
void Head::reset() void Head::reset()
{ {
Pitch = Yaw = Roll = 0; 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; int side = 0;
@ -270,7 +280,7 @@ void Head::render(int faceToFace, float * myLocation)
glRotatef(Yaw, 0, 1, 0); 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! // Don't render a head if it is really close to your location, because that is your own head!
if ((distanceToCamera > 1.0) || faceToFace) { if ((distanceToCamera > 1.0) || faceToFace) {
@ -305,13 +315,22 @@ void Head::render(int faceToFace, float * myLocation)
// Eyebrows // 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(); glPushMatrix();
glTranslatef(-interBrowDistance/2.0,0.4,0.45); glTranslatef(-interBrowDistance/2.0,0.4,0.45);
for(side = 0; side < 2; side++) for(side = 0; side < 2; side++)
{ {
glColor3fv(browColor); glColor3fv(browColor);
glPushMatrix(); glPushMatrix();
glTranslatef(0, 0.4, 0); glTranslatef(0, 0.35 + browAudioLift, 0);
glRotatef(EyebrowPitch[side]/2.0, 1, 0, 0); glRotatef(EyebrowPitch[side]/2.0, 1, 0, 0);
glRotatef(EyebrowRoll[side]/2.0, 0, 0, 1); glRotatef(EyebrowRoll[side]/2.0, 0, 0, 1);
glScalef(browWidth, browThickness, 1); glScalef(browWidth, browThickness, 1);
@ -326,10 +345,10 @@ void Head::render(int faceToFace, float * myLocation)
glPushMatrix(); glPushMatrix();
glTranslatef(0,-0.35,0.75); glTranslatef(0,-0.35,0.75);
glColor3f(loudness/1000.0,0,0); glColor3f(0,0,0);
glRotatef(MouthPitch, 1, 0, 0); glRotatef(MouthPitch, 1, 0, 0);
glRotatef(MouthYaw, 0, 0, 1); 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); glutSolidCube(0.5);
glPopMatrix(); glPopMatrix();

View file

@ -47,7 +47,7 @@ class Head : public AgentData {
float getRoll() {return Roll;} float getRoll() {return Roll;}
float getYaw() {return Yaw;} float getYaw() {return Yaw;}
void render(int faceToFace, float * myLocation); void render(int faceToFace, int isMine, float * myLocation);
void simulate(float); void simulate(float);
// Send and receive network data // Send and receive network data
@ -95,8 +95,10 @@ class Head : public AgentData {
float scale; float scale;
// Sound loudness information // Sound loudness information
float loudness; float loudness, lastLoudness;
float averageLoudness; float averageLoudness;
float audioAttack;
float browAudioLift;
glm::vec3 position; glm::vec3 position;
int eyeContact; int eyeContact;

View file

@ -0,0 +1,8 @@
#ifndef __interface__InterfaceMacOSX
#define __interface__InterfaceMacOSX
class Oscilloscope;
void initMacOSXMenu(Oscilloscope *audioScope);
#endif

View file

@ -0,0 +1,68 @@
#include "Oscilloscope.h"
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#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];
}
}
}

View file

@ -55,11 +55,6 @@ float StDev::getStDev() {
return 0; return 0;
} }
float randFloat () {
return (rand()%10000)/10000.f;
}
// Return the azimuth angle in degrees between two points. // Return the azimuth angle in degrees between two points.
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos) { 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; return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) * 180 / PI;

View file

@ -7,122 +7,172 @@
// //
#include "VoxelSystem.h" #include "VoxelSystem.h"
#include <AgentList.h>
const int MAX_VOXELS_PER_SYSTEM = 500000;
bool onSphereShell(float radius, float scale, glm::vec3 * position) { const int VERTICES_PER_VOXEL = 8;
float vRadius = glm::length(*position); const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
return ((vRadius + scale/2.0 > radius) && (vRadius - scale/2.0 < radius)); 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() { 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;
} }
// void VoxelSystem::render() {
// Recursively initialize the voxel tree // check if there are new voxels to draw
// int vertexValuesToDraw = lastAddPointer - lastDrawPointer;
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;
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
// First, randomly decide whether to stop here without recursing for children glBindBuffer(GL_ARRAY_BUFFER, vboVerticesID);
// glBufferSubData(GL_ARRAY_BUFFER, vertexBufferOffset * sizeof(float), vertexValuesToDraw * sizeof(float), lastDrawPointer);
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;
} // 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
// The Render Discard is the ratio of the size of the voxel to the distance from the camera glEnableClientState(GL_VERTEX_ARRAY);
// at which the voxel will no longer be shown. Smaller = show more detail. glEnableClientState(GL_COLOR_ARRAY);
//
const float RENDER_DISCARD = 0.04; //0.01; glBindBuffer(GL_ARRAY_BUFFER, vboVerticesID);
glVertexPointer(3, GL_FLOAT, 0, 0);
// glBindBuffer(GL_ARRAY_BUFFER, vboColorsID);
// Returns the total number of voxels actually rendered glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0);
//
int VoxelSystem::render(Voxel * voxel, float scale, glm::vec3 * distance) { glNormal3f(0, 1, 0);
// If null passed in, start at root
if (voxel == NULL) voxel = root; glDrawElements(GL_TRIANGLES, 36 * voxelsRendered, GL_UNSIGNED_INT, 0);
unsigned char i;
bool renderedChildren = false; // deactivate vertex and color arrays after drawing
int vRendered = 0; glDisableClientState(GL_VERTEX_ARRAY);
// Recursively render children glDisableClientState(GL_COLOR_ARRAY);
for (i = 0; i < NUM_CHILDREN; i++) {
glm::vec3 shift(scale/2.0*((i&4)>>2)-scale/4.0, // bind with 0 to switch back to normal operation
scale/2.0*((i&2)>>1)-scale/4.0, glBindBuffer(GL_ARRAY_BUFFER, 0);
scale/2.0*(i&1)-scale/4.0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 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;
} }
void VoxelSystem::simulate(float deltaTime) { void VoxelSystem::simulate(float deltaTime) {
} }

View file

@ -9,11 +9,13 @@
#ifndef __interface__Cube__ #ifndef __interface__Cube__
#define __interface__Cube__ #define __interface__Cube__
#include "InterfaceConfig.h"
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <iostream>
#include <UDPSocket.h>
#include <AgentData.h>
#include "Util.h" #include "Util.h"
#include "world.h" #include "world.h"
#include "InterfaceConfig.h"
#include <iostream>
const int NUM_CHILDREN = 8; const int NUM_CHILDREN = 8;
@ -22,17 +24,31 @@ struct Voxel {
Voxel * children[NUM_CHILDREN]; Voxel * children[NUM_CHILDREN];
}; };
class VoxelSystem { class VoxelSystem : public AgentData {
public: public:
VoxelSystem();
~VoxelSystem();
void parseData(void *data, int size);
VoxelSystem* clone() const;
void init();
void simulate(float deltaTime); void simulate(float deltaTime);
int render(Voxel * voxel, float scale, glm::vec3 * distance); int render(Voxel * voxel, float scale, glm::vec3 * distance);
void init(); void render();
int initVoxels(Voxel * root, float scale, glm::vec3 * position); int initVoxels(Voxel * root, float scale, glm::vec3 * position);
void setVoxelsRendered(int v) {voxelsRendered = v;}; void setVoxelsRendered(int v) {voxelsRendered = v;};
int getVoxelsRendered() {return voxelsRendered;}; int getVoxelsRendered() {return voxelsRendered;};
Voxel * root; Voxel * root;
private: private:
int voxelsRendered; int voxelsRendered;
GLfloat *verticesArray;
GLubyte *colorsArray;
GLfloat *lastAddPointer;
GLfloat *lastDrawPointer;
GLuint vboVerticesID;
GLuint vboColorsID;
GLuint vboIndicesID;
}; };
#endif #endif

View file

@ -44,6 +44,12 @@
#include "Oscilloscope.h" #include "Oscilloscope.h"
#include "UDPSocket.h" #include "UDPSocket.h"
#ifdef __APPLE__
#include "InterfaceMacOSX.h"
#endif
using namespace std; using namespace std;
int audio_on = 1; // Whether to turn on the audio support int audio_on = 1; // Whether to turn on the audio support
@ -80,7 +86,7 @@ int fullscreen = 0;
in_addr_t localAddress; 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 #define HAND_RADIUS 0.25 // Radius of in-world 'hand' of you
Head myHead; // The rendered head of oneself Head myHead; // The rendered head of oneself
@ -95,7 +101,7 @@ ParticleSystem balls(0,
0.0 // Gravity 0.0 // Gravity
); );
Cloud cloud(0, // Particles Cloud cloud(0, // Particles
box, // Bounding Box box, // Bounding Box
false // Wrap false // Wrap
); );
@ -106,7 +112,7 @@ Lattice lattice(160,100);
Finger myFinger(WIDTH, HEIGHT); Finger myFinger(WIDTH, HEIGHT);
Field field; Field field;
Audio audio(&audioScope); Audio audio(&audioScope, &myHead);
#define RENDER_FRAME_MSECS 8 #define RENDER_FRAME_MSECS 8
int steps_per_frame = 0; int steps_per_frame = 0;
@ -314,9 +320,6 @@ void initDisplay(void)
void init(void) void init(void)
{ {
voxels.init(); 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); myHead.setRenderYaw(start_yaw);
@ -329,11 +332,13 @@ void init(void)
field = Field(); field = Field();
printf( "Field Initialized.\n" ); printf( "Field Initialized.\n" );
if (noise_on) if (noise_on) {
{
myHead.setNoise(noise); myHead.setNoise(noise);
} }
char output[] = "I";
char address[] = "10.0.0.10";
agentList.getAgentSocket().send(address, 40106, output, 1);
#ifdef MARKER_CAPTURE #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 // Using serial data, update avatar/render position and angles
{ {
// float measured_pitch_rate = serialPort.getRelativeValue(PITCH_RATE); // 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.setRenderYaw(myHead.getRenderYaw() + render_yaw_rate);
myHead.setRenderPitch(render_pitch); myHead.setRenderPitch(render_pitch);
myHead.setPos(glm::vec3(location[0], location[1], location[2])); 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 // Get audio loudness data from audio input device
float loudness, averageLoudness; float loudness, averageLoudness;
@ -524,6 +541,7 @@ void display(void)
glEnable(GL_LINE_SMOOTH); glEnable(GL_LINE_SMOOTH);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glPushMatrix(); glPushMatrix();
glLoadIdentity(); glLoadIdentity();
@ -531,7 +549,7 @@ void display(void)
glEnable(GL_COLOR_MATERIAL); glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); 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); glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
GLfloat ambient_color[] = { 0.125, 0.305, 0.5 }; GLfloat ambient_color[] = { 0.125, 0.305, 0.5 };
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color); glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
@ -551,21 +569,14 @@ void display(void)
glColor3f(1,0,0); glColor3f(1,0,0);
glutSolidSphere(0.25, 15, 15); glutSolidSphere(0.25, 15, 15);
// Draw cloud of dots // Draw cloud of dots
glDisable( GL_POINT_SPRITE_ARB ); glDisable( GL_POINT_SPRITE_ARB );
glDisable( GL_TEXTURE_2D ); glDisable( GL_TEXTURE_2D );
if (!display_head) cloud.render(); if (!display_head) cloud.render();
// Draw voxels // Draw voxels
glPushMatrix(); voxels.render();
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();
// Draw field vectors // Draw field vectors
if (display_field) field.render(); if (display_field) field.render();
@ -577,7 +588,7 @@ void display(void)
glPushMatrix(); glPushMatrix();
glm::vec3 pos = agentHead->getPos(); glm::vec3 pos = agentHead->getPos();
glTranslatef(-pos.x, -pos.y, -pos.z); glTranslatef(-pos.x, -pos.y, -pos.z);
agentHead->render(0, &location[0]); agentHead->render(0, 0, &location[0]);
glPopMatrix(); glPopMatrix();
} }
} }
@ -591,12 +602,13 @@ void display(void)
glPushMatrix(); glPushMatrix();
glLoadIdentity(); glLoadIdentity();
glTranslatef(0.f, 0.f, -7.f); glTranslatef(0.f, 0.f, -7.f);
myHead.render(display_head, &location[0]); myHead.render(display_head, 1, &location[0]);
glPopMatrix(); glPopMatrix();
//glm::vec3 test(0.5, 0.5, 0.5); //glm::vec3 test(0.5, 0.5, 0.5);
//render_vector(&test); //render_vector(&test);
glPopMatrix(); glPopMatrix();
// Render 2D overlay: I/O level bar graphs and text // Render 2D overlay: I/O level bar graphs and text
@ -665,6 +677,7 @@ void display(void)
glPopMatrix(); glPopMatrix();
glutSwapBuffers(); glutSwapBuffers();
framecount++; 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 == 'm') head_mirror = !head_mirror;
if (k == 'f') display_field = !display_field; if (k == 'f') display_field = !display_field;
@ -749,7 +767,14 @@ void *networkReceive(void *args)
packetcount++; packetcount++;
bytescount += bytesReceived; 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++; steps_per_frame++;
// Simulation // Simulation
update_pos(1.f/FPS); simulateHead(1.f/FPS);
simulateHand(1.f/FPS);
if (simulate_on) { if (simulate_on) {
field.simulate(1.f/FPS); field.simulate(1.f/FPS);
myHead.simulate(1.f/FPS); myHead.simulate(1.f/FPS);
@ -830,22 +857,8 @@ void motionFunc( int x, int y)
{ {
mouse_x = x; mouse_x = x;
mouse_y = y; 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); lattice.mouseClick((float)x/(float)WIDTH,(float)y/(float)HEIGHT);
} }
void mouseoverFunc( int x, int y) void mouseoverFunc( int x, int y)
@ -869,6 +882,12 @@ void audioMixerUpdate(in_addr_t newMixerAddress, in_port_t newMixerPort) {
audio.updateMixerParams(newMixerAddress, 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) int main(int argc, char** argv)
{ {
struct ifaddrs * ifAddrStruct=NULL; struct ifaddrs * ifAddrStruct=NULL;
@ -924,13 +943,11 @@ int main(int argc, char** argv)
// the callback for our instance of AgentList is attachNewHeadToAgent // the callback for our instance of AgentList is attachNewHeadToAgent
agentList.linkedDataCreateCallback = &attachNewHeadToAgent; agentList.linkedDataCreateCallback = &attachNewHeadToAgent;
agentList.audioMixerSocketUpdate = &audioMixerUpdate; agentList.audioMixerSocketUpdate = &audioMixerUpdate;
agentList.voxelServerAddCallback = &voxelServerAddCallback;
// start the thread which checks for silent agents // start the thread which checks for silent agents
agentList.startSilentAgentRemovalThread(); agentList.startSilentAgentRemovalThread();
// create thread for receipt of data via UDP
pthread_create(&networkReceiveThread, NULL, networkReceive, NULL);
glutInit(&argc, argv); glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(WIDTH, HEIGHT); glutInitWindowSize(WIDTH, HEIGHT);
@ -953,10 +970,17 @@ int main(int argc, char** argv)
init(); init();
// create thread for receipt of data via UDP
pthread_create(&networkReceiveThread, NULL, networkReceive, NULL);
printf( "Init() complete.\n" ); printf( "Init() complete.\n" );
glutTimerFunc(1000, Timer, 0); glutTimerFunc(1000, Timer, 0);
#ifdef __APPLE__
initMacOSXMenu(&audioScope);
#endif
glutMainLoop(); glutMainLoop();
::terminate(); ::terminate();

View file

@ -19,98 +19,210 @@
const unsigned short MIXER_LISTEN_PORT = 55443; const unsigned short MIXER_LISTEN_PORT = 55443;
const float SAMPLE_RATE = 22050.0; 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 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<int16_t>::max(); const long MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
const long MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min(); const long MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::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_HOSTNAME[] = "highfidelity.below92.com";
char DOMAIN_IP[100] = ""; // IP Address will be re-set by lookup on startup 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); 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) void *sendBuffer(void *args)
{ {
int sentBytes; int sentBytes;
int nextFrame = 0; int nextFrame = 0;
timeval startTime; timeval startTime, lastSend;
int16_t *clientMix = new int16_t[BUFFER_LENGTH_SAMPLES];
long *masterMix = new long[BUFFER_LENGTH_SAMPLES];
gettimeofday(&startTime, NULL); gettimeofday(&startTime, NULL);
while (true) { while (true) {
sentBytes = 0; sentBytes = 0;
for (int ms = 0; ms < BUFFER_LENGTH_SAMPLES; ms++) { for (int i = 0; i < agentList.getAgents().size(); i++) {
masterMix[ms] = 0; AudioRingBuffer *agentBuffer = (AudioRingBuffer *) agentList.getAgents()[i].getLinkedData();
}
for (int ab = 0; ab < agentList.getAgents().size(); ab++) {
AudioRingBuffer *agentBuffer = (AudioRingBuffer *)agentList.getAgents()[ab].getLinkedData();
if (agentBuffer != NULL && agentBuffer->getEndOfLastWrite() != NULL) { if (agentBuffer != NULL && agentBuffer->getEndOfLastWrite() != NULL) {
if (!agentBuffer->isStarted() && agentBuffer->diffLastWriteNextOutput() <= BUFFER_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) {
printf("Held back buffer %d.\n", ab); if (!agentBuffer->isStarted()
} else if (agentBuffer->diffLastWriteNextOutput() < BUFFER_LENGTH_SAMPLES) { && agentBuffer->diffLastWriteNextOutput() <= BUFFER_LENGTH_SAMPLES_PER_CHANNEL + JITTER_BUFFER_SAMPLES) {
printf("Buffer %d starved.\n", ab); 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); agentBuffer->setStarted(false);
} else { } 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 // good buffer, add this to the mix
agentBuffer->setStarted(true); agentBuffer->setStarted(true);
agentBuffer->setAddedToMix(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++) { int numAgents = agentList.getAgents().size();
Agent *agent = &agentList.getAgents()[ab]; float distanceCoeffs[numAgents][numAgents];
AudioRingBuffer *agentBuffer = (AudioRingBuffer *)agent->getLinkedData(); memset(distanceCoeffs, 0, sizeof(distanceCoeffs));
int16_t *previousOutput = NULL; for (int i = 0; i < agentList.getAgents().size(); i++) {
Agent *agent = &agentList.getAgents()[i];
if (agentBuffer != NULL && agentBuffer->wasAddedToMix()) { AudioRingBuffer *agentRingBuffer = (AudioRingBuffer *) agent->getLinkedData();
previousOutput = (agentBuffer->getNextOutput() == agentBuffer->getBuffer()) float agentBearing = agentRingBuffer->getBearing();
? agentBuffer->getBuffer() + RING_BUFFER_SAMPLES - BUFFER_LENGTH_SAMPLES bool agentWantsLoopback = false;
: agentBuffer->getNextOutput() - BUFFER_LENGTH_SAMPLES;
agentBuffer->setAddedToMix(false); if (agentBearing > 180 || agentBearing < -180) {
// we were passed an invalid bearing because this agent wants loopback (pressed the H key)
agentWantsLoopback = true;
// correct the bearing
agentBearing = agentBearing > 0 ? agentBearing - AGENT_LOOPBACK_MODIFIER : agentBearing + AGENT_LOOPBACK_MODIFIER;
} }
for (int s = 0; s < BUFFER_LENGTH_SAMPLES; s++) { int16_t clientMix[BUFFER_LENGTH_SAMPLES_PER_CHANNEL * 2] = {};
long longSample = (previousOutput != NULL)
? masterMix[s] - previousOutput[s]
: masterMix[s];
int16_t shortSample;
if (longSample < 0) { for (int j = 0; j < agentList.getAgents().size(); j++) {
shortSample = std::max(longSample, MIN_SAMPLE_VALUE); if (i != j || ( i == j && agentWantsLoopback)) {
} else { AudioRingBuffer *otherAgentBuffer = (AudioRingBuffer *)agentList.getAgents()[j].getLinkedData();
shortSample = std::min(longSample, MAX_SAMPLE_VALUE);
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); 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(); double usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) { if (usecToSleep > 0) {
@ -133,6 +245,7 @@ void *reportAliveToDS(void *args) {
*output = 'M'; *output = 'M';
packSocket(output + 1, 895283510, htons(MIXER_LISTEN_PORT)); 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); agentList.getAgentSocket().send(DOMAIN_IP, DOMAINSERVER_PORT, output, 7);
double usecToSleep = 1000000 - (usecTimestampNow() - usecTimestamp(&lastSend)); double usecToSleep = 1000000 - (usecTimestampNow() - usecTimestamp(&lastSend));
@ -147,7 +260,7 @@ void *reportAliveToDS(void *args) {
void attachNewBufferToAgent(Agent *newAgent) { void attachNewBufferToAgent(Agent *newAgent) {
if (newAgent->getLinkedData() == NULL) { 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; agentList.linkedDataCreateCallback = attachNewBufferToAgent;
// setup the agentSocket to report to domain server agentList.startSilentAgentRemovalThread();
pthread_t reportAliveThread;
pthread_create(&reportAliveThread, NULL, reportAliveToDS, NULL);
// Lookup the IP address of things we have hostnames // Lookup the IP address of things we have hostnames
if (atoi(DOMAIN_IP) == 0) { if (atoi(DOMAIN_IP) == 0) {
@ -177,23 +288,44 @@ int main(int argc, const char * argv[])
printf("Using static domainserver IP: %s\n", DOMAIN_IP); 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]; unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE];
pthread_t sendBufferThread; pthread_t sendBufferThread;
pthread_create(&sendBufferThread, NULL, sendBuffer, NULL); pthread_create(&sendBufferThread, NULL, sendBuffer, NULL);
int16_t *loopbackAudioPacket;
if (LOOPBACK_SANITY_CHECK) {
loopbackAudioPacket = new int16_t[1024];
}
sockaddr *agentAddress = new sockaddr; sockaddr *agentAddress = new sockaddr;
timeval lastReceive;
gettimeofday(&lastReceive, NULL);
while (true) { while (true) {
if(agentList.getAgentSocket().receive(agentAddress, packetData, &receivedBytes)) { 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 // add or update the existing interface agent
agentList.addOrUpdateAgent(agentAddress, agentAddress, packetData[0]); if (!LOOPBACK_SANITY_CHECK) {
agentList.updateAgentWithData(agentAddress, (void *)packetData, receivedBytes); 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(reportAliveThread, NULL);
pthread_join(sendBufferThread, NULL); pthread_join(sendBufferThread, NULL);

View file

@ -11,7 +11,7 @@
#include <pthread.h> #include <pthread.h>
#include "SharedUtil.h" #include "SharedUtil.h"
const char * SOLO_AGENT_TYPES_STRING = "M"; const char * SOLO_AGENT_TYPES_STRING = "MV";
bool stopAgentRemovalThread = false; bool stopAgentRemovalThread = false;
pthread_mutex_t vectorChangeMutex = PTHREAD_MUTEX_INITIALIZER; 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) { AgentList::AgentList() : agentSocket(AGENT_SOCKET_LISTEN_PORT) {
linkedDataCreateCallback = NULL; linkedDataCreateCallback = NULL;
audioMixerSocketUpdate = NULL; audioMixerSocketUpdate = NULL;
voxelServerAddCallback = NULL;
} }
AgentList::AgentList(int socketListenPort) : agentSocket(socketListenPort) { AgentList::AgentList(int socketListenPort) : agentSocket(socketListenPort) {
linkedDataCreateCallback = NULL; linkedDataCreateCallback = NULL;
audioMixerSocketUpdate = NULL; audioMixerSocketUpdate = NULL;
voxelServerAddCallback = NULL;
} }
AgentList::~AgentList() { AgentList::~AgentList() {
@ -55,6 +57,7 @@ void AgentList::processAgentData(sockaddr *senderAddress, void *packetData, size
case 'P': case 'P':
{ {
// ping from another agent // ping from another agent
//std::cout << "Got ping from " << inet_ntoa(((sockaddr_in *)senderAddress)->sin_addr) << "\n";
char reply[] = "R"; char reply[] = "R";
agentSocket.send(senderAddress, reply, 1); agentSocket.send(senderAddress, reply, 1);
break; break;
@ -62,6 +65,7 @@ void AgentList::processAgentData(sockaddr *senderAddress, void *packetData, size
case 'R': case 'R':
{ {
// ping reply from another agent // ping reply from another agent
//std::cout << "Got ping reply from " << inet_ntoa(((sockaddr_in *)senderAddress)->sin_addr) << "\n";
handlePingReply(senderAddress); handlePingReply(senderAddress);
break; break;
} }
@ -149,6 +153,8 @@ bool AgentList::addOrUpdateAgent(sockaddr *publicSocket, sockaddr *localSocket,
// to use the local socket information the domain server gave us // to use the local socket information the domain server gave us
sockaddr_in *localSocketIn = (sockaddr_in *)localSocket; sockaddr_in *localSocketIn = (sockaddr_in *)localSocket;
audioMixerSocketUpdate(localSocketIn->sin_addr.s_addr, localSocketIn->sin_port); audioMixerSocketUpdate(localSocketIn->sin_addr.s_addr, localSocketIn->sin_port);
} else if (newAgent.getType() == 'V' && voxelServerAddCallback != NULL) {
voxelServerAddCallback(localSocket);
} }
std::cout << "Added agent - " << &newAgent << "\n"; std::cout << "Added agent - " << &newAgent << "\n";
@ -160,7 +166,7 @@ bool AgentList::addOrUpdateAgent(sockaddr *publicSocket, sockaddr *localSocket,
return true; return true;
} else { } else {
if (agent->getType() == 'M') { if (agent->getType() == 'M' || agent->getType() == 'V') {
// until the Audio class also uses our agentList, we need to update // 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 // the lastRecvTimeUsecs for the audio mixer so it doesn't get killed and re-added continously
agent->setLastRecvTimeUsecs(usecTimestampNow()); agent->setLastRecvTimeUsecs(usecTimestampNow());

View file

@ -27,6 +27,7 @@ class AgentList {
void(*linkedDataCreateCallback)(Agent *); void(*linkedDataCreateCallback)(Agent *);
void(*audioMixerSocketUpdate)(in_addr_t, in_port_t); void(*audioMixerSocketUpdate)(in_addr_t, in_port_t);
void(*voxelServerAddCallback)(sockaddr *);
std::vector<Agent>& getAgents(); std::vector<Agent>& getAgents();
UDPSocket& getAgentSocket(); UDPSocket& getAgentSocket();

View file

@ -9,22 +9,27 @@
#include <cstring> #include <cstring>
#include "AudioRingBuffer.h" #include "AudioRingBuffer.h"
AudioRingBuffer::AudioRingBuffer() { AudioRingBuffer::AudioRingBuffer(int ringSamples, int bufferSamples) {
ringBufferLengthSamples = ringSamples;
bufferLengthSamples = bufferSamples;
started = false; started = false;
addedToMix = false; addedToMix = false;
endOfLastWrite = NULL; endOfLastWrite = NULL;
buffer = new int16_t[RING_BUFFER_SAMPLES]; buffer = new int16_t[ringBufferLengthSamples];
nextOutput = buffer; nextOutput = buffer;
}; };
AudioRingBuffer::AudioRingBuffer(const AudioRingBuffer &otherRingBuffer) { AudioRingBuffer::AudioRingBuffer(const AudioRingBuffer &otherRingBuffer) {
ringBufferLengthSamples = otherRingBuffer.ringBufferLengthSamples;
bufferLengthSamples = otherRingBuffer.bufferLengthSamples;
started = otherRingBuffer.started; started = otherRingBuffer.started;
addedToMix = otherRingBuffer.addedToMix; addedToMix = otherRingBuffer.addedToMix;
buffer = new int16_t[RING_BUFFER_SAMPLES]; buffer = new int16_t[ringBufferLengthSamples];
memcpy(buffer, otherRingBuffer.buffer, sizeof(int16_t) * RING_BUFFER_SAMPLES); memcpy(buffer, otherRingBuffer.buffer, sizeof(int16_t) * ringBufferLengthSamples);
nextOutput = buffer + (otherRingBuffer.nextOutput - otherRingBuffer.buffer); nextOutput = buffer + (otherRingBuffer.nextOutput - otherRingBuffer.buffer);
endOfLastWrite = buffer + (otherRingBuffer.endOfLastWrite - otherRingBuffer.buffer); endOfLastWrite = buffer + (otherRingBuffer.endOfLastWrite - otherRingBuffer.buffer);
@ -74,33 +79,57 @@ void AudioRingBuffer::setAddedToMix(bool added) {
addedToMix = added; addedToMix = added;
} }
void AudioRingBuffer::parseData(void *data, int size) { float* AudioRingBuffer::getPosition() {
int16_t *audioDataStart = (int16_t *) data; return position;
}
if (size > BUFFER_LENGTH_BYTES) { void AudioRingBuffer::setPosition(float *newPosition) {
float position[3]; position[0] = newPosition[0];
unsigned char *charData = (unsigned char *) data; 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) {
unsigned char *audioDataStart = (unsigned char *) data;
if (size > (bufferLengthSamples * sizeof(int16_t))) {
unsigned char *dataPtr = audioDataStart + 1;
for (int p = 0; p < 3; p ++) { 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) { if (endOfLastWrite == NULL) {
endOfLastWrite = buffer; endOfLastWrite = buffer;
} else if (diffLastWriteNextOutput() > RING_BUFFER_SAMPLES - BUFFER_LENGTH_SAMPLES) { } else if (diffLastWriteNextOutput() > ringBufferLengthSamples - bufferLengthSamples) {
endOfLastWrite = buffer; endOfLastWrite = buffer;
nextOutput = buffer; nextOutput = buffer;
started = false; started = false;
} }
memcpy(endOfLastWrite, audioDataStart, BUFFER_LENGTH_BYTES); memcpy(endOfLastWrite, audioDataStart, bufferLengthSamples * sizeof(int16_t));
endOfLastWrite += BUFFER_LENGTH_SAMPLES;
if (endOfLastWrite >= buffer + RING_BUFFER_SAMPLES) { endOfLastWrite += bufferLengthSamples;
addedToMix = false;
if (endOfLastWrite >= buffer + ringBufferLengthSamples) {
endOfLastWrite = buffer; endOfLastWrite = buffer;
} }
} }
@ -113,7 +142,7 @@ short AudioRingBuffer::diffLastWriteNextOutput()
short sampleDifference = endOfLastWrite - nextOutput; short sampleDifference = endOfLastWrite - nextOutput;
if (sampleDifference < 0) { if (sampleDifference < 0) {
sampleDifference += RING_BUFFER_SAMPLES; sampleDifference += ringBufferLengthSamples;
} }
return sampleDifference; return sampleDifference;

View file

@ -13,15 +13,9 @@
#include <stdint.h> #include <stdint.h>
#include "AgentData.h" #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 { class AudioRingBuffer : public AgentData {
public: public:
AudioRingBuffer(); AudioRingBuffer(int ringSamples, int bufferSamples);
~AudioRingBuffer(); ~AudioRingBuffer();
AudioRingBuffer(const AudioRingBuffer &otherRingBuffer); AudioRingBuffer(const AudioRingBuffer &otherRingBuffer);
@ -37,9 +31,17 @@ class AudioRingBuffer : public AgentData {
void setStarted(bool status); void setStarted(bool status);
bool wasAddedToMix(); bool wasAddedToMix();
void setAddedToMix(bool added); void setAddedToMix(bool added);
float* getPosition();
void setPosition(float newPosition[]);
float getBearing();
void setBearing(float newBearing);
short diffLastWriteNextOutput(); short diffLastWriteNextOutput();
private: private:
int ringBufferLengthSamples;
int bufferLengthSamples;
float position[3];
float bearing;
int16_t *nextOutput; int16_t *nextOutput;
int16_t *endOfLastWrite; int16_t *endOfLastWrite;
int16_t *buffer; int16_t *buffer;

View file

@ -7,6 +7,7 @@
// //
#include "SharedUtil.h" #include "SharedUtil.h"
#include <cstdlib>
double usecTimestamp(timeval *time) { double usecTimestamp(timeval *time) {
return (time->tv_sec * 1000000.0 + time->tv_usec); return (time->tv_sec * 1000000.0 + time->tv_usec);
@ -17,3 +18,8 @@ double usecTimestampNow() {
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
return (now.tv_sec * 1000000.0 + now.tv_usec); return (now.tv_sec * 1000000.0 + now.tv_usec);
} }
float randFloat () {
return (rand()%10000)/10000.f;
}

View file

@ -14,5 +14,6 @@
double usecTimestamp(timeval *time); double usecTimestamp(timeval *time);
double usecTimestampNow(); double usecTimestampNow();
float randFloat();
#endif /* defined(__hifi__SharedUtil__) */ #endif /* defined(__hifi__SharedUtil__) */

10
voxel/CMakeLists.txt Normal file
View file

@ -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)

192
voxel/src/main.cpp Normal file
View file

@ -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 <AgentList.h>
#include <sys/time.h>
#include <SharedUtil.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <cmath>
#include <SharedUtil.h>
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;
}