diff --git a/audio-mixer/src/main.cpp b/audio-mixer/src/main.cpp index 982ebc526c..d94a1d59d6 100644 --- a/audio-mixer/src/main.cpp +++ b/audio-mixer/src/main.cpp @@ -160,68 +160,100 @@ int main(int argc, const char* argv[]) { int lowAgentIndex = std::min(agent.getAgentIndex(), otherAgent.getAgentIndex()); int highAgentIndex = std::max(agent.getAgentIndex(), otherAgent.getAgentIndex()); + bool insideSphericalInjector = false; + if (distanceCoefficients[lowAgentIndex][highAgentIndex] == 0) { - float distanceToAgent = sqrtf(powf(agentPosition.x - otherAgentPosition.x, 2) + - powf(agentPosition.y - otherAgentPosition.y, 2) + - powf(agentPosition.z - otherAgentPosition.z, 2)); + float distanceToAgent = glm::distance(agentPosition, otherAgentPosition); + + float minCoefficient = 1.0f; + + if (otherAgentBuffer->getRadius() == 0 || distanceToAgent > otherAgentBuffer->getRadius()) { + // this is either not a spherical source, or the listener is outside the sphere + + if (otherAgentBuffer->getRadius() > 0) { + // this is a spherical source - the distance used for the coefficient + // needs to be the closest point on the boundary to the source + + // multiply the normalized vector between the center of the sphere + // and the position of the source by the radius to get the + // closest point on the boundary of the sphere to the source + glm::vec3 difference = agentPosition - otherAgentPosition; + glm::vec3 closestPoint = glm::normalize(difference) * otherAgentBuffer->getRadius(); + + // for the other calculations the agent position is the closest point on the sphere + otherAgentPosition = closestPoint; + + // ovveride the distance to the agent with the distance to the point on the + // boundary of the sphere + distanceToAgent = glm::distance(agentPosition, closestPoint); + } + + // calculate the distance coefficient using the distance to this agent + minCoefficient = std::min(1.0f, + powf(0.3, (logf(DISTANCE_SCALE * distanceToAgent) / + logf(2.5)) - 1)); + } else { + insideSphericalInjector = true; + } + - float minCoefficient = std::min(1.0f, - powf(0.3, - (logf(DISTANCE_SCALE * distanceToAgent) / logf(2.5)) - - 1)); distanceCoefficients[lowAgentIndex][highAgentIndex] = minCoefficient; } - - // get the angle from the right-angle triangle - float triangleAngle = atan2f(fabsf(agentPosition.z - otherAgentPosition.z), - fabsf(agentPosition.x - otherAgentPosition.x)) * (180 / M_PI); - float absoluteAngleToSource = 0; - bearingRelativeAngleToSource = 0; - - // find the angle we need for calculation based on the orientation of the triangle - if (otherAgentPosition.x > agentPosition.x) { - if (otherAgentPosition.z > agentPosition.z) { - absoluteAngleToSource = -90 + triangleAngle; + if (!insideSphericalInjector) { + // off-axis attenuation and spatialization of audio is not performed + // if the listener is inside a spherical injector + + // get the angle from the right-angle triangle + float triangleAngle = atan2f(fabsf(agentPosition.z - otherAgentPosition.z), + fabsf(agentPosition.x - otherAgentPosition.x)) * (180 / M_PI); + float absoluteAngleToSource = 0; + bearingRelativeAngleToSource = 0; + + // find the angle we need for calculation based on the orientation of the triangle + if (otherAgentPosition.x > agentPosition.x) { + if (otherAgentPosition.z > agentPosition.z) { + absoluteAngleToSource = -90 + triangleAngle; + } else { + absoluteAngleToSource = -90 - triangleAngle; + } } else { - absoluteAngleToSource = -90 - triangleAngle; + if (otherAgentPosition.z > agentPosition.z) { + absoluteAngleToSource = 90 - triangleAngle; + } else { + absoluteAngleToSource = 90 + triangleAngle; + } } - } else { - if (otherAgentPosition.z > agentPosition.z) { - absoluteAngleToSource = 90 - triangleAngle; - } else { - absoluteAngleToSource = 90 + triangleAngle; + + bearingRelativeAngleToSource = absoluteAngleToSource - agentRingBuffer->getBearing(); + + if (bearingRelativeAngleToSource > 180) { + bearingRelativeAngleToSource -= 360; + } else if (bearingRelativeAngleToSource < -180) { + bearingRelativeAngleToSource += 360; } + + float angleOfDelivery = absoluteAngleToSource - otherAgentBuffer->getBearing(); + + if (angleOfDelivery > 180) { + angleOfDelivery -= 360; + } else if (angleOfDelivery < -180) { + angleOfDelivery += 360; + } + + float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + + (OFF_AXIS_ATTENUATION_FORMULA_STEP * (fabsf(angleOfDelivery) / 90.0f)); + + attenuationCoefficient = distanceCoefficients[lowAgentIndex][highAgentIndex] + * otherAgentBuffer->getAttenuationRatio() + * offAxisCoefficient; + + bearingRelativeAngleToSource *= (M_PI / 180); + + float sinRatio = fabsf(sinf(bearingRelativeAngleToSource)); + numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; + weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio); } - - bearingRelativeAngleToSource = absoluteAngleToSource - agentRingBuffer->getBearing(); - - if (bearingRelativeAngleToSource > 180) { - bearingRelativeAngleToSource -= 360; - } else if (bearingRelativeAngleToSource < -180) { - bearingRelativeAngleToSource += 360; - } - - float angleOfDelivery = absoluteAngleToSource - otherAgentBuffer->getBearing(); - - if (angleOfDelivery > 180) { - angleOfDelivery -= 360; - } else if (angleOfDelivery < -180) { - angleOfDelivery += 360; - } - - float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + - (OFF_AXIS_ATTENUATION_FORMULA_STEP * (fabsf(angleOfDelivery) / 90.0f)); - - attenuationCoefficient = distanceCoefficients[lowAgentIndex][highAgentIndex] - * otherAgentBuffer->getAttenuationRatio() - * offAxisCoefficient; - - bearingRelativeAngleToSource *= (M_PI / 180); - - float sinRatio = fabsf(sinf(bearingRelativeAngleToSource)); - numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; - weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio); } int16_t* goodChannel = bearingRelativeAngleToSource > 0.0f diff --git a/injector/src/main.cpp b/injector/src/main.cpp index 512882c374..18fa1f27df 100644 --- a/injector/src/main.cpp +++ b/injector/src/main.cpp @@ -36,25 +36,25 @@ const char *allowedParameters = ":rb::t::c::a::f::d::s:"; float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f}; unsigned char volume = DEFAULT_INJECTOR_VOLUME; float triggerDistance = 0.0f; -float cubeSideLength = 0.0f; +float radius = 0.0f; void usage(void) { std::cout << "High Fidelity - Interface audio injector" << std::endl; - std::cout << " -r Random sleep mode. If not specified will default to constant loop." << std::endl; + std::cout << " -s Random sleep mode. If not specified will default to constant loop." << std::endl; std::cout << " -b FLOAT Min. number of seconds to sleep. Only valid in random sleep mode. Default 1.0" << std::endl; std::cout << " -t FLOAT Max. number of seconds to sleep. Only valid in random sleep mode. Default 2.0" << std::endl; std::cout << " -c FLOAT,FLOAT,FLOAT,FLOAT X,Y,Z,YAW position in universe where audio will be originating from and direction. Defaults to 0,0,0,0" << std::endl; std::cout << " -a 0-255 Attenuation curve modifier, defaults to 255" << std::endl; std::cout << " -f FILENAME Name of audio source file. Required - RAW format, 22050hz 16bit signed mono" << std::endl; std::cout << " -d FLOAT Trigger distance for injection. If not specified will loop constantly" << std::endl; - std::cout << " -s FLOAT Length of side of cube audio source. If not specified injected audio is point source" << std::endl; + std::cout << " -r FLOAT Radius for spherical source. If not specified injected audio is point source" << std::endl; } bool processParameters(int parameterCount, char* parameterData[]) { int p; while ((p = getopt(parameterCount, parameterData, allowedParameters)) != -1) { switch (p) { - case 'r': + case 's': ::loopAudio = false; std::cout << "[DEBUG] Random sleep mode enabled" << std::endl; break; @@ -94,9 +94,9 @@ bool processParameters(int parameterCount, char* parameterData[]) { ::triggerDistance = atof(optarg); std::cout << "[DEBUG] Trigger distance: " << optarg << std::endl; break; - case 's': - ::cubeSideLength = atof(optarg); - std::cout << "[DEBUG] Cube side length: " << optarg << std::endl; + case 'r': + ::radius = atof(optarg); + std::cout << "[DEBUG] Injector radius: " << optarg << std::endl; break; default: usage(); @@ -170,9 +170,9 @@ int main(int argc, char* argv[]) { injector.setBearing(*(::floatArguments + 3)); injector.setVolume(::volume); - if (::cubeSideLength > 0) { + if (::radius > 0) { // if we were passed a cube side length, give that to the injector - injector.setCubeSideLength(::cubeSideLength); + injector.setRadius(::radius); } // register the callback for agent data creation diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index e1de27ef6d..fbb55a84c3 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -19,7 +19,7 @@ const int MAX_INJECTOR_VOLUME = 0xFF; AudioInjector::AudioInjector(const char* filename) : _position(), - _cubeSideLength(0.0f), + _radius(0.0f), _bearing(0), _volume(MAX_INJECTOR_VOLUME), _indexOfNextSlot(0), @@ -49,7 +49,7 @@ AudioInjector::AudioInjector(const char* filename) : AudioInjector::AudioInjector(int maxNumSamples) : _numTotalSamples(maxNumSamples), _position(), - _cubeSideLength(0.0f), + _radius(0.0f), _bearing(0), _volume(MAX_INJECTOR_VOLUME), _indexOfNextSlot(0), @@ -72,13 +72,19 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination timeval startTime; // calculate the number of bytes required for additional data - int leadingBytes = sizeof(PACKET_HEADER) + sizeof(_streamIdentifier) + int leadingBytes = sizeof(PACKET_HEADER) + sizeof(INJECT_AUDIO_AT_POINT_COMMAND) + sizeof(_streamIdentifier) + sizeof(_position) + sizeof(_bearing) + sizeof(_volume); + + if (_radius > 0) { + // we'll need 4 extra bytes if the cube side length is being sent as well + leadingBytes += sizeof(_radius); + } + unsigned char dataPacket[BUFFER_LENGTH_BYTES + leadingBytes]; dataPacket[0] = PACKET_HEADER_INJECT_AUDIO; // add the correct command for point source or cube of sound - dataPacket[1] = (_cubeSideLength > 0) ? INJECT_AUDIO_AT_CUBE_COMMAND : INJECT_AUDIO_AT_POINT_COMMAND; + dataPacket[1] = (_radius > 0) ? INJECT_AUDIO_AT_CUBE_COMMAND : INJECT_AUDIO_AT_POINT_COMMAND; unsigned char *currentPacketPtr = dataPacket + sizeof(PACKET_HEADER) + sizeof(INJECT_AUDIO_AT_POINT_COMMAND); // copy the identifier for this injector @@ -88,11 +94,11 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination memcpy(currentPacketPtr, &_position, sizeof(_position)); currentPacketPtr += sizeof(_position); - if (_cubeSideLength > 0) { + if (_radius > 0) { // if we have a cube half height we need to send it here // this tells the mixer how much volume the injected audio will occupy - memcpy(currentPacketPtr, &_cubeSideLength, sizeof(_cubeSideLength)); - currentPacketPtr += sizeof(_cubeSideLength); + memcpy(currentPacketPtr, &_radius, sizeof(_radius)); + currentPacketPtr += sizeof(_radius); } *currentPacketPtr = _volume; diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 0269931568..d8b907d368 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -39,8 +39,8 @@ public: float getBearing() const { return _bearing; } void setBearing(float bearing) { _bearing = bearing; } - float getCubeSideLength() const { return _cubeSideLength; } - void setCubeSideLength(float cubeSideLength) { _cubeSideLength = cubeSideLength; } + float getRadius() const { return _radius; } + void setRadius(float radius) { _radius = radius; } void addSample(const int16_t sample); void addSamples(int16_t* sampleBuffer, int numSamples); @@ -49,7 +49,7 @@ private: int16_t* _audioSampleArray; int _numTotalSamples; glm::vec3 _position; - float _cubeSideLength; + float _radius; float _bearing; unsigned char _volume; int _indexOfNextSlot; diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 39cdc43287..fe93ece47f 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -17,7 +17,7 @@ AudioRingBuffer::AudioRingBuffer(int ringSamples, int bufferSamples) : AgentData(NULL), _ringBufferLengthSamples(ringSamples), _bufferLengthSamples(bufferSamples), - _cubeSideLength(0.0f), + _radius(0.0f), _endOfLastWrite(NULL), _started(false), _shouldBeAddedToMix(false), @@ -53,13 +53,13 @@ int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { } memcpy(&_position, dataBuffer, sizeof(_position)); - dataBuffer += (sizeof(_position)); + dataBuffer += sizeof(_position); if (sourceBuffer[0] == PACKET_HEADER_INJECT_AUDIO && sourceBuffer[1] == INJECT_AUDIO_AT_CUBE_COMMAND) { // this is audio that needs to be injected as a volume (cube) // parse out the cubeHalfHeight sent by the client - memcpy(&_cubeSideLength, dataBuffer, sizeof(_cubeSideLength)); - dataBuffer += (sizeof(_cubeSideLength)); + memcpy(&_radius, dataBuffer, sizeof(_radius)); + dataBuffer += sizeof(_radius); } unsigned int attenuationByte = *(dataBuffer++); diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 3554e13d46..0a2b8ed7db 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -26,6 +26,8 @@ public: ~AudioRingBuffer(); int parseData(unsigned char* sourceBuffer, int numBytes); + + float getRadius() const { return _radius; } int16_t* getNextOutput() const { return _nextOutput; } void setNextOutput(int16_t* nextOutput) { _nextOutput = nextOutput; } @@ -56,7 +58,7 @@ private: int _ringBufferLengthSamples; int _bufferLengthSamples; glm::vec3 _position; - float _cubeSideLength; + float _radius; float _attenuationRatio; float _bearing; int16_t* _nextOutput;