mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
merge conflicts
This commit is contained in:
commit
8e6c98661b
26 changed files with 1129 additions and 372 deletions
|
@ -5,4 +5,5 @@ project(hifi)
|
|||
add_subdirectory(space)
|
||||
add_subdirectory(domain)
|
||||
add_subdirectory(mixer)
|
||||
add_subdirectory(voxel)
|
||||
add_subdirectory(interface)
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(¤tTime, NULL);
|
||||
float timeLeftInCurrentBuffer = 0;
|
||||
if (audioData->lastCallback.tv_usec > 0) timeLeftInCurrentBuffer = diffclock(&audioData->lastCallback, ¤tTime)/(1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE) * frameWidth;
|
||||
if (audioData->lastCallback.tv_usec > 0) {
|
||||
timeLeftInCurrentBuffer = AUDIO_CALLBACK_MSECS - diffclock(&audioData->lastCallback, ¤tTime);
|
||||
}
|
||||
|
||||
// /(1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE) * frameWidth
|
||||
|
||||
if (audioData->ringBuffer->getEndOfLastWrite() != NULL)
|
||||
remainingBuffer = audioData->ringBuffer->diffLastWriteNextOutput() / BUFFER_LENGTH_SAMPLES * frameWidth;
|
||||
remainingBuffer = audioData->ringBuffer->diffLastWriteNextOutput() / PACKET_LENGTH_SAMPLES * AUDIO_CALLBACK_MSECS;
|
||||
|
||||
if (audioData->wasStarved == 0) glColor3f(0, 1, 0);
|
||||
else {
|
||||
|
@ -362,8 +427,8 @@ void Audio::render(int screenWidth, int screenHeight)
|
|||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(startX, topY + 5);
|
||||
glVertex2f(startX + remainingBuffer + timeLeftInCurrentBuffer, topY + 5);
|
||||
glVertex2f(startX + remainingBuffer + timeLeftInCurrentBuffer, bottomY - 5);
|
||||
glVertex2f(startX + (remainingBuffer + timeLeftInCurrentBuffer)/AUDIO_CALLBACK_MSECS*frameWidth, topY + 5);
|
||||
glVertex2f(startX + (remainingBuffer + timeLeftInCurrentBuffer)/AUDIO_CALLBACK_MSECS*frameWidth, bottomY - 5);
|
||||
glVertex2f(startX, bottomY - 5);
|
||||
glEnd();
|
||||
|
||||
|
@ -373,19 +438,20 @@ void Audio::render(int screenWidth, int screenHeight)
|
|||
// Show a yellow bar with the averaged msecs latency you are hearing (from time of packet receipt)
|
||||
glColor3f(1,1,0);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(startX + audioData->averagedLatency - 2, topY - 2);
|
||||
glVertex2f(startX + audioData->averagedLatency + 2, topY - 2);
|
||||
glVertex2f(startX + audioData->averagedLatency + 2, bottomY + 2);
|
||||
glVertex2f(startX + audioData->averagedLatency - 2, bottomY + 2);
|
||||
glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth - 2, topY - 2);
|
||||
glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth + 2, topY - 2);
|
||||
glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth + 2, bottomY + 2);
|
||||
glVertex2f(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth - 2, bottomY + 2);
|
||||
glEnd();
|
||||
|
||||
char out[20];
|
||||
sprintf(out, "%3.0f\n", audioData->averagedLatency/(float)frameWidth*(1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE));
|
||||
drawtext(startX + audioData->averagedLatency - 10, topY-10, 0.08, 0, 1, 0, out, 1,1,0);
|
||||
char out[40];
|
||||
sprintf(out, "%3.0f\n", audioData->averagedLatency);
|
||||
drawtext(startX + audioData->averagedLatency/AUDIO_CALLBACK_MSECS*frameWidth - 10, topY-10, 0.08, 0, 1, 0, out, 1,1,0);
|
||||
//drawtext(startX + 0, topY-10, 0.08, 0, 1, 0, out, 1,1,0);
|
||||
|
||||
// Show a Cyan bar with the most recently measured jitter stdev
|
||||
|
||||
int jitterPels = (float) audioData->measuredJitter/ ((1000.0*(float)BUFFER_LENGTH_SAMPLES/(float)SAMPLE_RATE)) * (float)frameWidth;
|
||||
int jitterPels = (float) audioData->measuredJitter/ ((1000.0*(float)PACKET_LENGTH_SAMPLES/(float)SAMPLE_RATE)) * (float)frameWidth;
|
||||
|
||||
glColor3f(0,1,1);
|
||||
glBegin(GL_QUADS);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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__) */
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
8
interface/src/InterfaceMacOSX.h
Normal file
8
interface/src/InterfaceMacOSX.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef __interface__InterfaceMacOSX
|
||||
#define __interface__InterfaceMacOSX
|
||||
|
||||
class Oscilloscope;
|
||||
|
||||
void initMacOSXMenu(Oscilloscope *audioScope);
|
||||
|
||||
#endif
|
68
interface/src/InterfaceMacOSX.mm
Normal file
68
interface/src/InterfaceMacOSX.mm
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -14,5 +14,6 @@
|
|||
|
||||
double usecTimestamp(timeval *time);
|
||||
double usecTimestampNow();
|
||||
float randFloat();
|
||||
|
||||
#endif /* defined(__hifi__SharedUtil__) */
|
||||
|
|
10
voxel/CMakeLists.txt
Normal file
10
voxel/CMakeLists.txt
Normal 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
192
voxel/src/main.cpp
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue