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(domain)
add_subdirectory(mixer)
add_subdirectory(voxel)
add_subdirectory(interface)

View file

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

View file

@ -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 <GLUT/glut.h>\n#include <OpenGL/glext.h>")
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

View file

@ -14,22 +14,36 @@
#include <cstring>
#include "Audio.h"
#include "Util.h"
#include <SharedUtil.h>
#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(&currentTime, NULL);
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)
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);

View file

@ -13,15 +13,19 @@
#include <portaudio.h>
#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);

View file

@ -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;
}

View file

@ -14,18 +14,17 @@
#include <glm/glm.hpp>
#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__) */

View file

@ -7,41 +7,24 @@
//
#include "Hand.h"
#include <sys/time.h>
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;
}
}

View file

@ -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;
};

View file

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

View file

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

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;
}
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;

View file

@ -7,122 +7,172 @@
//
#include "VoxelSystem.h"
#include <AgentList.h>
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) {
}

View file

@ -9,11 +9,13 @@
#ifndef __interface__Cube__
#define __interface__Cube__
#include "InterfaceConfig.h"
#include <glm/glm.hpp>
#include <iostream>
#include <UDPSocket.h>
#include <AgentData.h>
#include "Util.h"
#include "world.h"
#include "InterfaceConfig.h"
#include <iostream>
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

View file

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

View file

@ -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<int16_t>::max();
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_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);

View file

@ -11,7 +11,7 @@
#include <pthread.h>
#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());

View file

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

View file

@ -9,22 +9,27 @@
#include <cstring>
#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;

View file

@ -13,15 +13,9 @@
#include <stdint.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 {
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;

View file

@ -7,6 +7,7 @@
//
#include "SharedUtil.h"
#include <cstdlib>
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;
}

View file

@ -14,5 +14,6 @@
double usecTimestamp(timeval *time);
double usecTimestampNow();
float randFloat();
#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;
}