diff --git a/Source/AudioData.cpp b/Source/AudioData.cpp index 1b093d3165..a9c7de60bc 100644 --- a/Source/AudioData.cpp +++ b/Source/AudioData.cpp @@ -15,7 +15,6 @@ AudioData::AudioData(int numberOfSources, int bufferLength) { for(int s = 0; s < numberOfSources; s++) { sources[s] = new AudioSource(); - std::cout << "Created a new audio source!\n"; } samplesToQueue = new int16_t[bufferLength / sizeof(int16_t)]; diff --git a/Source/AudioData.h b/Source/AudioData.h index 606cda8bfa..a0fc1dcb11 100644 --- a/Source/AudioData.h +++ b/Source/AudioData.h @@ -12,11 +12,13 @@ #include #include "AudioSource.h" #include "head.h" +#include "UDPSocket.h" class AudioData { public: Head *linkedHead; AudioSource **sources; + UDPSocket *audioSocket; int16_t *samplesToQueue; diff --git a/Source/AudioSource.h b/Source/AudioSource.h index adb68c3df4..33cbace6e2 100644 --- a/Source/AudioSource.h +++ b/Source/AudioSource.h @@ -19,7 +19,7 @@ class AudioSource { int lengthInSamples; int samplePointer; - AudioSource() { samplePointer = 0; sourceData = NULL; } + AudioSource() { samplePointer = 0; sourceData = NULL; lengthInSamples = 0; } ~AudioSource(); int loadDataFromFile(const char *filename); diff --git a/Source/UDPSocket.cpp b/Source/UDPSocket.cpp index e4f3171dfd..f60005a940 100644 --- a/Source/UDPSocket.cpp +++ b/Source/UDPSocket.cpp @@ -11,17 +11,8 @@ #include #include -struct sockaddr_in UDPSocket::sockaddr_util(char* hostname, int port) { - sockaddr_in dest_address; - - dest_address.sin_family = AF_INET; - dest_address.sin_addr.s_addr = inet_addr(hostname); - dest_address.sin_port = htons((uint16_t)port); - - return dest_address; -} - -struct sockaddr_in dest_sockaddr; +sockaddr_in destSockaddr, senderAddress; +socklen_t addLength = sizeof(senderAddress); UDPSocket::UDPSocket(int listeningPort) { // create the socket @@ -33,10 +24,16 @@ UDPSocket::UDPSocket(int listeningPort) { } // instantiate the re-usable dest_sockaddr with a dummy IP and port - dest_sockaddr = UDPSocket::sockaddr_util((char *) "1.0.0.0", 1); + sockaddr_in dest_sockaddr; + dest_sockaddr.sin_family = AF_INET; + dest_sockaddr.sin_addr.s_addr = inet_addr("1.0.0.0"); + dest_sockaddr.sin_port = htons((uint16_t) 1); // bind the socket to the passed listeningPort - sockaddr_in bind_address = UDPSocket::sockaddr_util(INADDR_ANY, listeningPort); + sockaddr_in bind_address; + bind_address.sin_family = AF_INET; + bind_address.sin_addr.s_addr = INADDR_ANY; + bind_address.sin_port = htons((uint16_t) listeningPort); if (bind(handle, (const sockaddr*) &bind_address, sizeof(sockaddr_in)) < 0) { printf("Failed to bind socket to port %d.\n", listeningPort); @@ -49,19 +46,29 @@ UDPSocket::UDPSocket(int listeningPort) { printf("Failed to set non-blocking socket\n"); return; } + + printf("Created UDP socket listening on port %d.\n", listeningPort); } -int UDPSocket::send(char * dest_address, int dest_port, const void *data, int length_in_bytes) { +bool UDPSocket::receive(void *receivedData, int *receivedBytes) { + + *receivedBytes = recvfrom(handle, receivedData, MAX_BUFFER_LENGTH_BYTES, + 0, (sockaddr *)&senderAddress, &addLength); + + return (*receivedBytes > 0); +} + +int UDPSocket::send(char * destAddress, int destPort, const void *data, int byteLength) { // change address and port on reusable global to passed variables - dest_sockaddr.sin_addr.s_addr = inet_addr(dest_address); - dest_sockaddr.sin_port = htons((uint16_t)dest_port); + destSockaddr.sin_addr.s_addr = inet_addr(destAddress); + destSockaddr.sin_port = htons((uint16_t)destPort); // send data via UDP - int sent_bytes = sendto(handle, (const char*)data, length_in_bytes, - 0, (sockaddr*)&dest_address, sizeof(sockaddr_in)); + int sent_bytes = sendto(handle, (const char*)data, byteLength, + 0, (sockaddr *)&destSockaddr, sizeof(sockaddr_in)); - if (sent_bytes != length_in_bytes) { + if (sent_bytes != byteLength) { printf("Failed to send packet: return value = %d\n", sent_bytes); return false; } diff --git a/Source/UDPSocket.h b/Source/UDPSocket.h index 06198c19f4..a360c55287 100644 --- a/Source/UDPSocket.h +++ b/Source/UDPSocket.h @@ -12,16 +12,18 @@ #include #include +#define MAX_BUFFER_LENGTH_BYTES 1024 + class UDPSocket { - public: - static struct sockaddr_in sockaddr_util(char *address, int port); - - UDPSocket(int listening_port); - int send(char * dest_address, int dest_port, const void *data, int length_in_bytes); - private: - UDPSocket(); // private default constructor + public: + UDPSocket(int listening_port); + int send(char *destAddress, int destPort, const void *data, int byteLength); + bool receive(void *receivedData, int *receivedBytes); + private: int handle; + UDPSocket(); // private default constructor + struct AgentData { char * address; int listening_port; diff --git a/Source/audio.cpp b/Source/audio.cpp index 8fb21f9978..36f5a19ee0 100644 --- a/Source/audio.cpp +++ b/Source/audio.cpp @@ -8,9 +8,23 @@ #include #include +#include #include "audio.h" #include "util.h" #include "AudioSource.h" +#include "UDPSocket.h" + +const int BUFFER_LENGTH_BYTES = 1024; +const int BUFFER_LENGTH_SAMPLES = BUFFER_LENGTH_BYTES / sizeof(int16_t); +const int RING_BUFFER_LENGTH_SAMPLES = BUFFER_LENGTH_SAMPLES * 10; + +const int PHASE_DELAY_AT_90 = 20; +const int AMPLITUDE_RATIO_AT_90 = 0.5; + +const int NUM_AUDIO_SOURCES = 1; +const int ECHO_SERVER_TEST = 1; + +const int AUDIO_UDP_LISTEN_PORT = 55444; bool Audio::initialized; PaError Audio::err; @@ -46,6 +60,12 @@ int audioCallback (const void *inputBuffer, { AudioData *data = (AudioData *) userData; + int16_t *inBuffer = (int16_t *) inputBuffer; + + if (inBuffer != NULL) { + data->audioSocket->send((char *) "0.0.0.0", 55443, (void *)inBuffer, BUFFER_LENGTH_BYTES); + } + int16_t *outputLeft = ((int16_t **) outputBuffer)[0]; int16_t *outputRight = ((int16_t **) outputBuffer)[1]; @@ -56,49 +76,64 @@ int audioCallback (const void *inputBuffer, AudioSource *source = data->sources[s]; - glm::vec3 headPos = data->linkedHead->getPos(); - glm::vec3 sourcePos = source->position; - - int startPointer = source->samplePointer; - int wrapAroundSamples = (BUFFER_LENGTH_BYTES / sizeof(int16_t)) - (source->lengthInSamples - source->samplePointer); - - if (wrapAroundSamples <= 0) { - memcpy(data->samplesToQueue, source->sourceData + source->samplePointer, BUFFER_LENGTH_BYTES); - source->samplePointer += (BUFFER_LENGTH_BYTES / sizeof(int16_t)); - } else { - memcpy(data->samplesToQueue, source->sourceData + source->samplePointer, (source->lengthInSamples - source->samplePointer) * sizeof(int16_t)); - memcpy(data->samplesToQueue + (source->lengthInSamples - source->samplePointer), source->sourceData, wrapAroundSamples * sizeof(int16_t)); - source->samplePointer = wrapAroundSamples; - } - - float distance = sqrtf(powf(-headPos[0] - sourcePos[0], 2) + powf(-headPos[2] - sourcePos[2], 2)); - float distanceAmpRatio = powf(0.5, cbrtf(distance * 10)); - - float angleToSource = angle_to(headPos * -1.f, sourcePos, data->linkedHead->getRenderYaw(), data->linkedHead->getYaw()) * M_PI/180; - float sinRatio = sqrt(fabsf(sinf(angleToSource))); - int numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; - - float phaseAmpRatio = 1.f - (AMPLITUDE_RATIO_AT_90 * sinRatio); - -// std::cout << "S: " << numSamplesDelay << " A: " << angleToSource << " S: " << sinRatio << " AR: " << phaseAmpRatio << "\n"; - - int16_t *leadingOutput = angleToSource > 0 ? outputLeft : outputRight; - int16_t *trailingOutput = angleToSource > 0 ? outputRight : outputLeft; - - for (int i = 0; i < BUFFER_LENGTH_BYTES / sizeof(int16_t); i++) { - data->samplesToQueue[i] *= distanceAmpRatio / NUM_AUDIO_SOURCES; - leadingOutput[i] += data->samplesToQueue[i]; + if (ECHO_SERVER_TEST) { - if (i >= numSamplesDelay) { - trailingOutput[i] += data->samplesToQueue[i - numSamplesDelay]; + // copy whatever is source->sourceData to the left and right output channels + memcpy(outputLeft, source->sourceData + source->samplePointer, BUFFER_LENGTH_BYTES); + memcpy(outputRight, source->sourceData + source->samplePointer, BUFFER_LENGTH_BYTES); + + + if (source->samplePointer < RING_BUFFER_LENGTH_SAMPLES - BUFFER_LENGTH_SAMPLES) { + source->samplePointer += BUFFER_LENGTH_SAMPLES; } else { - int sampleIndex = startPointer - numSamplesDelay + i; + source->samplePointer = 0; + } + + } else { + glm::vec3 headPos = data->linkedHead->getPos(); + glm::vec3 sourcePos = source->position; + + int startPointer = source->samplePointer; + int wrapAroundSamples = (BUFFER_LENGTH_SAMPLES) - (source->lengthInSamples - source->samplePointer); + + if (wrapAroundSamples <= 0) { + memcpy(data->samplesToQueue, source->sourceData + source->samplePointer, BUFFER_LENGTH_BYTES); + source->samplePointer += (BUFFER_LENGTH_SAMPLES); + } else { + memcpy(data->samplesToQueue, source->sourceData + source->samplePointer, (source->lengthInSamples - source->samplePointer) * sizeof(int16_t)); + memcpy(data->samplesToQueue + (source->lengthInSamples - source->samplePointer), source->sourceData, wrapAroundSamples * sizeof(int16_t)); + source->samplePointer = wrapAroundSamples; + } + + float distance = sqrtf(powf(-headPos[0] - sourcePos[0], 2) + powf(-headPos[2] - sourcePos[2], 2)); + float distanceAmpRatio = powf(0.5, cbrtf(distance * 10)); + + float angleToSource = angle_to(headPos * -1.f, sourcePos, data->linkedHead->getRenderYaw(), data->linkedHead->getYaw()) * M_PI/180; + float sinRatio = sqrt(fabsf(sinf(angleToSource))); + int numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; + + float phaseAmpRatio = 1.f - (AMPLITUDE_RATIO_AT_90 * sinRatio); + + // std::cout << "S: " << numSamplesDelay << " A: " << angleToSource << " S: " << sinRatio << " AR: " << phaseAmpRatio << "\n"; + + int16_t *leadingOutput = angleToSource > 0 ? outputLeft : outputRight; + int16_t *trailingOutput = angleToSource > 0 ? outputRight : outputLeft; + + for (int i = 0; i < BUFFER_LENGTH_SAMPLES; i++) { + data->samplesToQueue[i] *= distanceAmpRatio / NUM_AUDIO_SOURCES; + leadingOutput[i] += data->samplesToQueue[i]; - if (sampleIndex < 0) { - sampleIndex += source->lengthInSamples; + if (i >= numSamplesDelay) { + trailingOutput[i] += data->samplesToQueue[i - numSamplesDelay]; + } else { + int sampleIndex = startPointer - numSamplesDelay + i; + + if (sampleIndex < 0) { + sampleIndex += source->lengthInSamples; + } + + trailingOutput[i] += source->sourceData[sampleIndex] * (distanceAmpRatio * phaseAmpRatio / NUM_AUDIO_SOURCES); } - - trailingOutput[i] += source->sourceData[sampleIndex] * (distanceAmpRatio * phaseAmpRatio / NUM_AUDIO_SOURCES); } } } @@ -106,6 +141,32 @@ int audioCallback (const void *inputBuffer, return paContinue; } +struct AudioRecThreadStruct { + AudioData *sharedAudioData; +}; + +void *receiveAudioViaUDP(void *args) { + AudioRecThreadStruct *threadArgs = (AudioRecThreadStruct *) args; + AudioData *sharedAudioData = threadArgs->sharedAudioData; + + int16_t *receivedData = new int16_t[BUFFER_LENGTH_SAMPLES]; + int *receivedBytes = new int; + int streamSamplePointer = 0; + + while (true) { + if (sharedAudioData->audioSocket->receive((void *)receivedData, receivedBytes)) { + // add the received data to the shared memory + memcpy(sharedAudioData->sources[0]->sourceData + streamSamplePointer, receivedData, *receivedBytes); + + if (streamSamplePointer < RING_BUFFER_LENGTH_SAMPLES - BUFFER_LENGTH_SAMPLES) { + streamSamplePointer += BUFFER_LENGTH_SAMPLES; + } else { + streamSamplePointer = 0; + } + } + } +} + /** * Initialize portaudio and start an audio stream. * Should be called at the beginning of program exection. @@ -115,26 +176,45 @@ Use Audio::getError() to retrieve the error code. */ bool Audio::init() { - Head deadHead = Head(); - return Audio::init(&deadHead); + Head *deadHead = new Head(); + return Audio::init(deadHead); } -bool Audio::init(Head* mainHead) -{ - data = new AudioData(NUM_AUDIO_SOURCES, BUFFER_LENGTH_BYTES); - data->linkedHead = mainHead; - +bool Audio::init(Head *mainHead) +{ err = Pa_Initialize(); if (err != paNoError) goto error; - data->sources[0]->position = glm::vec3(6, 0, -1); - data->sources[0]->loadDataFromFile("jeska.raw"); + if (ECHO_SERVER_TEST) { + data = new AudioData(1, BUFFER_LENGTH_BYTES); + + // setup a UDPSocket + data->audioSocket = new UDPSocket(AUDIO_UDP_LISTEN_PORT); + + // setup the ring buffer source for the streamed audio + data->sources[0]->sourceData = new int16_t[RING_BUFFER_LENGTH_SAMPLES]; + memset(data->sources[0]->sourceData, 0, RING_BUFFER_LENGTH_SAMPLES * sizeof(int16_t)); + + pthread_t audioReceiveThread; + + AudioRecThreadStruct threadArgs; + threadArgs.sharedAudioData = data; + + pthread_create(&audioReceiveThread, NULL, receiveAudioViaUDP, (void *) &threadArgs); + } else { + data = new AudioData(NUM_AUDIO_SOURCES, BUFFER_LENGTH_BYTES); + + data->sources[0]->position = glm::vec3(6, 0, -1); + data->sources[0]->loadDataFromFile("jeska.raw"); + + data->sources[1]->position = glm::vec3(6, 0, 6); + data->sources[1]->loadDataFromFile("grayson.raw"); + } - data->sources[1]->position = glm::vec3(6, 0, 6); - data->sources[1]->loadDataFromFile("grayson.raw"); + data->linkedHead = mainHead; err = Pa_OpenDefaultStream(&stream, - NULL, // input channels + 1, // input channels 2, // output channels (paInt16 | paNonInterleaved), // sample format 22050, // sample rate (hz) @@ -158,9 +238,9 @@ error: return false; } -void Audio::sourceSetup() +void Audio::render() { - if (initialized) { + if (initialized && !ECHO_SERVER_TEST) { for (int s = 0; s < NUM_AUDIO_SOURCES; s++) { // render gl objects on screen for our sources diff --git a/Source/audio.h b/Source/audio.h index ec390cce0a..3922b72e6a 100644 --- a/Source/audio.h +++ b/Source/audio.h @@ -14,18 +14,13 @@ #include "head.h" #include "AudioData.h" -#define BUFFER_LENGTH_BYTES 1024 -#define PHASE_DELAY_AT_90 20 -#define AMPLITUDE_RATIO_AT_90 0.5 -#define NUM_AUDIO_SOURCES 2 - class Audio { public: // initializes audio I/O static bool init(); static bool init(Head* mainHead); - static void sourceSetup(); + static void render(); // terminates audio I/O static bool terminate(); diff --git a/Source/main.cpp b/Source/main.cpp index 8d99d7e11d..66a13c4833 100644 --- a/Source/main.cpp +++ b/Source/main.cpp @@ -610,7 +610,7 @@ void display(void) // render audio sources and start them if (audio_on) { - Audio::sourceSetup(); + Audio::render(); } //glm::vec3 test(0.5, 0.5, 0.5);