mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge pull request #485 from birarda/cube-injector
spherical audio injector and quaternion rotations for audio
This commit is contained in:
commit
9310876531
10 changed files with 188 additions and 148 deletions
|
@ -17,6 +17,8 @@
|
|||
#include <limits>
|
||||
#include <signal.h>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <AgentList.h>
|
||||
#include <Agent.h>
|
||||
#include <AgentTypes.h>
|
||||
|
@ -55,13 +57,6 @@ const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SA
|
|||
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_SCALE = 2.5f;
|
||||
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
|
||||
const int PHASE_DELAY_AT_90 = 20;
|
||||
|
||||
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
|
||||
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
|
||||
|
||||
void plateauAdditionOfSamples(int16_t &mixSample, int16_t sampleToAdd) {
|
||||
long sumSample = sampleToAdd + mixSample;
|
||||
|
||||
|
@ -128,10 +123,6 @@ int main(int argc, const char* argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
int numAgents = agentList->size();
|
||||
float distanceCoefficients[numAgents][numAgents];
|
||||
memset(distanceCoefficients, 0, sizeof(distanceCoefficients));
|
||||
|
||||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
if (agent->getType() == AGENT_TYPE_AVATAR) {
|
||||
AudioRingBuffer* agentRingBuffer = (AudioRingBuffer*) agent->getLinkedData();
|
||||
|
@ -146,90 +137,101 @@ int main(int argc, const char* argv[]) {
|
|||
if (otherAgentBuffer->shouldBeAddedToMix()) {
|
||||
|
||||
float bearingRelativeAngleToSource = 0.f;
|
||||
float attenuationCoefficient = 1.f;
|
||||
float attenuationCoefficient = 1.0f;
|
||||
int numSamplesDelay = 0;
|
||||
float weakChannelAmplitudeRatio = 1.f;
|
||||
float weakChannelAmplitudeRatio = 1.0f;
|
||||
|
||||
if (otherAgent != agent) {
|
||||
glm::vec3 agentPosition = agentRingBuffer->getPosition();
|
||||
glm::vec3 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(agent.getAgentIndex(), otherAgent.getAgentIndex());
|
||||
int highAgentIndex = std::max(agent.getAgentIndex(), otherAgent.getAgentIndex());
|
||||
|
||||
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));
|
||||
glm::vec3 listenerPosition = agentRingBuffer->getPosition();
|
||||
glm::vec3 relativePosition = otherAgentBuffer->getPosition() - agentRingBuffer->getPosition();
|
||||
glm::quat inverseOrientation = glm::inverse(agentRingBuffer->getOrientation());
|
||||
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
|
||||
|
||||
float distanceSquareToSource = glm::dot(relativePosition, relativePosition);
|
||||
|
||||
float distanceCoefficient = 1.0f;
|
||||
float offAxisCoefficient = 1.0f;
|
||||
|
||||
if (otherAgentBuffer->getRadius() == 0
|
||||
|| (distanceSquareToSource > (otherAgentBuffer->getRadius()
|
||||
* otherAgentBuffer->getRadius()))) {
|
||||
// this is either not a spherical source, or the listener is outside the sphere
|
||||
|
||||
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 (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 closestPoint = glm::normalize(relativePosition) * otherAgentBuffer->getRadius();
|
||||
|
||||
// for the other calculations the agent position is the closest point on the sphere
|
||||
rotatedSourcePosition = inverseOrientation * closestPoint;
|
||||
|
||||
// ovveride the distance to the agent with the distance to the point on the
|
||||
// boundary of the sphere
|
||||
distanceSquareToSource = glm::distance2(listenerPosition, -closestPoint);
|
||||
|
||||
} else {
|
||||
absoluteAngleToSource = -90 - triangleAngle;
|
||||
}
|
||||
} else {
|
||||
if (otherAgentPosition.z > agentPosition.z) {
|
||||
absoluteAngleToSource = 90 - triangleAngle;
|
||||
} else {
|
||||
absoluteAngleToSource = 90 + triangleAngle;
|
||||
// calculate the angle delivery
|
||||
glm::vec3 rotatedListenerPosition = glm::inverse(otherAgentBuffer->getOrientation())
|
||||
* relativePosition;
|
||||
|
||||
float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f),
|
||||
glm::normalize(rotatedListenerPosition));
|
||||
|
||||
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
|
||||
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
|
||||
|
||||
offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
|
||||
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f));
|
||||
}
|
||||
|
||||
const float DISTANCE_SCALE = 2.5f;
|
||||
const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f;
|
||||
const float DISTANCE_LOG_BASE = 2.5f;
|
||||
const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE);
|
||||
|
||||
// calculate the distance coefficient using the distance to this agent
|
||||
distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
|
||||
DISTANCE_SCALE_LOG +
|
||||
(logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
|
||||
distanceCoefficient = std::min(1.0f, distanceCoefficient);
|
||||
|
||||
// off-axis attenuation and spatialization of audio
|
||||
// not performed if listener is inside spherical injector
|
||||
|
||||
// calculate the angle from the source to the listener
|
||||
|
||||
// project the rotated source position vector onto the XZ plane
|
||||
rotatedSourcePosition.y = 0.0f;
|
||||
|
||||
// produce an oriented angle about the y-axis
|
||||
bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f),
|
||||
glm::normalize(rotatedSourcePosition),
|
||||
glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
|
||||
const int PHASE_DELAY_AT_90 = 20;
|
||||
|
||||
float sinRatio = fabsf(sinf(glm::radians(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]
|
||||
attenuationCoefficient = distanceCoefficient
|
||||
* 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
|
||||
? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL
|
||||
: clientSamples;
|
||||
int16_t* delayedChannel = bearingRelativeAngleToSource > 0.0f
|
||||
? clientSamples
|
||||
: clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
int16_t* delayedChannel = bearingRelativeAngleToSource > 0.0f
|
||||
? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL
|
||||
: clientSamples;
|
||||
|
||||
int16_t* delaySamplePointer = otherAgentBuffer->getNextOutput() == otherAgentBuffer->getBuffer()
|
||||
? otherAgentBuffer->getBuffer() + RING_BUFFER_SAMPLES - numSamplesDelay
|
||||
|
@ -288,7 +290,7 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
agentList->updateAgentWithData(agentAddress, packetData, receivedBytes);
|
||||
|
||||
if (std::isnan(((AudioRingBuffer *)avatarAgent->getLinkedData())->getBearing())) {
|
||||
if (std::isnan(((AudioRingBuffer *)avatarAgent->getLinkedData())->getOrientation().x)) {
|
||||
// kill off this agent - temporary solution to mixer crash on mac sleep
|
||||
avatarAgent->setAlive(false);
|
||||
}
|
||||
|
|
|
@ -27,32 +27,41 @@ const int AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS = 15;
|
|||
|
||||
const int DEFAULT_INJECTOR_VOLUME = 0xFF;
|
||||
|
||||
enum {
|
||||
INJECTOR_POSITION_X,
|
||||
INJECTOR_POSITION_Y,
|
||||
INJECTOR_POSITION_Z,
|
||||
INJECTOR_YAW
|
||||
};
|
||||
|
||||
// Command line parameter defaults
|
||||
bool loopAudio = true;
|
||||
float sleepIntervalMin = 1.00;
|
||||
float sleepIntervalMax = 2.00;
|
||||
char *sourceAudioFile = NULL;
|
||||
const char *allowedParameters = ":rb::t::c::a::f::d:";
|
||||
const char *allowedParameters = ":sb::t::c::a::f::d::r:";
|
||||
float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
unsigned char volume = DEFAULT_INJECTOR_VOLUME;
|
||||
float triggerDistance = 0;
|
||||
float triggerDistance = 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 << " -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;
|
||||
|
@ -92,6 +101,10 @@ bool processParameters(int parameterCount, char* parameterData[]) {
|
|||
::triggerDistance = atof(optarg);
|
||||
std::cout << "[DEBUG] Trigger distance: " << optarg << std::endl;
|
||||
break;
|
||||
case 'r':
|
||||
::radius = atof(optarg);
|
||||
std::cout << "[DEBUG] Injector radius: " << optarg << std::endl;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return false;
|
||||
|
@ -160,9 +173,16 @@ int main(int argc, char* argv[]) {
|
|||
// start the agent list thread that will kill off agents when they stop talking
|
||||
agentList->startSilentAgentRemovalThread();
|
||||
|
||||
injector.setPosition(glm::vec3(::floatArguments[0], ::floatArguments[1], ::floatArguments[2]));
|
||||
injector.setBearing(*(::floatArguments + 3));
|
||||
injector.setPosition(glm::vec3(::floatArguments[INJECTOR_POSITION_X],
|
||||
::floatArguments[INJECTOR_POSITION_Y],
|
||||
::floatArguments[INJECTOR_POSITION_Z]));
|
||||
injector.setOrientation(glm::quat(glm::vec3(0.0f, ::floatArguments[INJECTOR_YAW], 0.0f)));
|
||||
injector.setVolume(::volume);
|
||||
|
||||
if (::radius > 0) {
|
||||
// if we were passed a cube side length, give that to the injector
|
||||
injector.setRadius(::radius);
|
||||
}
|
||||
|
||||
// register the callback for agent data creation
|
||||
agentList->linkedDataCreateCallback = createAvatarDataForAgent;
|
||||
|
|
|
@ -2232,7 +2232,7 @@ void Application::maybeEditVoxelUnderCursor() {
|
|||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
//_myAvatar.getPosition()
|
||||
voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw());
|
||||
// voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw());
|
||||
voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value
|
||||
|
||||
/* for (int i = 0; i
|
||||
|
@ -2295,7 +2295,7 @@ void Application::deleteVoxelUnderCursor() {
|
|||
sendVoxelEditMessage(PACKET_HEADER_ERASE_VOXEL, _mouseVoxel);
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000);
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
voxelInjector->setBearing(0); //straight down the z axis
|
||||
// voxelInjector->setBearing(0); //straight down the z axis
|
||||
voxelInjector->setVolume (255); //255 is max, and also default value
|
||||
|
||||
|
||||
|
|
|
@ -133,7 +133,10 @@ int audioCallback (const void* inputBuffer,
|
|||
Agent* audioMixer = agentList->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER);
|
||||
|
||||
if (audioMixer) {
|
||||
int leadingBytes = 2 + (sizeof(float) * 4);
|
||||
glm::vec3 headPosition = interfaceAvatar->getHeadJointPosition();
|
||||
glm::quat headOrientation = interfaceAvatar->getHead().getOrientation();
|
||||
|
||||
int leadingBytes = 1 + sizeof(headPosition) + sizeof(headOrientation) + sizeof(unsigned char);
|
||||
|
||||
// we need the amount of bytes in the buffer + 1 for type
|
||||
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte
|
||||
|
@ -143,29 +146,15 @@ int audioCallback (const void* inputBuffer,
|
|||
unsigned char *currentPacketPtr = dataPacket + 1;
|
||||
|
||||
// memcpy the three float positions
|
||||
memcpy(currentPacketPtr, &interfaceAvatar->getHeadJointPosition(), sizeof(float) * 3);
|
||||
currentPacketPtr += (sizeof(float) * 3);
|
||||
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
|
||||
currentPacketPtr += (sizeof(headPosition));
|
||||
|
||||
// tell the mixer not to add additional attenuation to our source
|
||||
*(currentPacketPtr++) = 255;
|
||||
|
||||
// memcpy the corrected render yaw
|
||||
float correctedYaw = fmodf(-1 * interfaceAvatar->getAbsoluteHeadYaw(), 360);
|
||||
|
||||
if (correctedYaw > 180) {
|
||||
correctedYaw -= 360;
|
||||
} else if (correctedYaw < -180) {
|
||||
correctedYaw += 360;
|
||||
}
|
||||
|
||||
if (Application::getInstance()->shouldEchoAudio()) {
|
||||
correctedYaw = correctedYaw > 0
|
||||
? correctedYaw + AGENT_LOOPBACK_MODIFIER
|
||||
: correctedYaw - AGENT_LOOPBACK_MODIFIER;
|
||||
}
|
||||
|
||||
memcpy(currentPacketPtr, &correctedYaw, sizeof(float));
|
||||
currentPacketPtr += sizeof(float);
|
||||
// memcpy our orientation
|
||||
memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation));
|
||||
currentPacketPtr += sizeof(headOrientation);
|
||||
|
||||
// copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet
|
||||
memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES);
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
const int MAX_INJECTOR_VOLUME = 0xFF;
|
||||
|
||||
AudioInjector::AudioInjector(const char* filename) :
|
||||
_position(),
|
||||
_bearing(0),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
_radius(0.0f),
|
||||
_volume(MAX_INJECTOR_VOLUME),
|
||||
_indexOfNextSlot(0),
|
||||
_isInjectingAudio(false)
|
||||
|
@ -47,8 +48,9 @@ AudioInjector::AudioInjector(const char* filename) :
|
|||
|
||||
AudioInjector::AudioInjector(int maxNumSamples) :
|
||||
_numTotalSamples(maxNumSamples),
|
||||
_position(),
|
||||
_bearing(0),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
_radius(0.0f),
|
||||
_volume(MAX_INJECTOR_VOLUME),
|
||||
_indexOfNextSlot(0),
|
||||
_isInjectingAudio(false)
|
||||
|
@ -70,12 +72,20 @@ 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)
|
||||
+ sizeof(_position) + sizeof(_bearing) + sizeof(_volume);
|
||||
int leadingBytes = sizeof(PACKET_HEADER) + sizeof(INJECT_AUDIO_AT_POINT_COMMAND) + sizeof(_streamIdentifier)
|
||||
+ sizeof(_position) + sizeof(_orientation) + 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;
|
||||
unsigned char *currentPacketPtr = dataPacket + 1;
|
||||
// add the correct command for point source or cube of sound
|
||||
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
|
||||
memcpy(currentPacketPtr, &_streamIdentifier, sizeof(_streamIdentifier));
|
||||
|
@ -84,11 +94,18 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
|
|||
memcpy(currentPacketPtr, &_position, sizeof(_position));
|
||||
currentPacketPtr += sizeof(_position);
|
||||
|
||||
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, &_radius, sizeof(_radius));
|
||||
currentPacketPtr += sizeof(_radius);
|
||||
}
|
||||
|
||||
*currentPacketPtr = _volume;
|
||||
currentPacketPtr++;
|
||||
|
||||
memcpy(currentPacketPtr, &_bearing, sizeof(_bearing));
|
||||
currentPacketPtr += sizeof(_bearing);
|
||||
memcpy(currentPacketPtr, &_orientation, sizeof(_orientation));
|
||||
currentPacketPtr += sizeof(_orientation);
|
||||
|
||||
gettimeofday(&startTime, NULL);
|
||||
int nextFrame = 0;
|
||||
|
|
|
@ -36,8 +36,11 @@ public:
|
|||
const glm::vec3& getPosition() const { return _position; }
|
||||
void setPosition(const glm::vec3& position) { _position = position; }
|
||||
|
||||
float getBearing() const { return _bearing; }
|
||||
void setBearing(float bearing) { _bearing = bearing; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
|
||||
|
||||
float getRadius() const { return _radius; }
|
||||
void setRadius(float radius) { _radius = radius; }
|
||||
|
||||
void addSample(const int16_t sample);
|
||||
void addSamples(int16_t* sampleBuffer, int numSamples);
|
||||
|
@ -46,7 +49,8 @@ private:
|
|||
int16_t* _audioSampleArray;
|
||||
int _numTotalSamples;
|
||||
glm::vec3 _position;
|
||||
float _bearing;
|
||||
glm::quat _orientation;
|
||||
float _radius;
|
||||
unsigned char _volume;
|
||||
int _indexOfNextSlot;
|
||||
bool _isInjectingAudio;
|
||||
|
|
|
@ -17,6 +17,7 @@ AudioRingBuffer::AudioRingBuffer(int ringSamples, int bufferSamples) :
|
|||
AgentData(NULL),
|
||||
_ringBufferLengthSamples(ringSamples),
|
||||
_bufferLengthSamples(bufferSamples),
|
||||
_radius(0.0f),
|
||||
_endOfLastWrite(NULL),
|
||||
_started(false),
|
||||
_shouldBeAddedToMix(false),
|
||||
|
@ -46,31 +47,34 @@ int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
// we've got a stream identifier to pull from the packet
|
||||
memcpy(&_streamIdentifier, dataBuffer, sizeof(_streamIdentifier));
|
||||
dataBuffer += sizeof(_streamIdentifier);
|
||||
|
||||
// push past the injection command
|
||||
dataBuffer += sizeof(INJECT_AUDIO_AT_POINT_COMMAND);
|
||||
}
|
||||
|
||||
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(&_radius, dataBuffer, sizeof(_radius));
|
||||
dataBuffer += sizeof(_radius);
|
||||
}
|
||||
|
||||
unsigned int attenuationByte = *(dataBuffer++);
|
||||
_attenuationRatio = attenuationByte / 255.0f;
|
||||
|
||||
memcpy(&_bearing, dataBuffer, sizeof(float));
|
||||
dataBuffer += sizeof(_bearing);
|
||||
memcpy(&_orientation, dataBuffer, sizeof(_orientation));
|
||||
dataBuffer += sizeof(_orientation);
|
||||
|
||||
// if this agent sent us a NaN bearing then don't consider this good audio and bail
|
||||
if (std::isnan(_bearing)) {
|
||||
// if this agent sent us a NaN for first float in orientation then don't consider this good audio and bail
|
||||
if (std::isnan(_orientation.x)) {
|
||||
_endOfLastWrite = _nextOutput = _buffer;
|
||||
_started = false;
|
||||
return 0;
|
||||
} else if (_bearing > 180 || _bearing < -180) {
|
||||
// we were passed an invalid bearing because this agent wants loopback (pressed the H key)
|
||||
_shouldLoopbackForAgent = true;
|
||||
|
||||
// correct the bearing
|
||||
_bearing = _bearing > 0
|
||||
? _bearing - AGENT_LOOPBACK_MODIFIER
|
||||
: _bearing + AGENT_LOOPBACK_MODIFIER;
|
||||
} else {
|
||||
// currently no possiblity for loopback, need to add once quaternion audio is working again
|
||||
_shouldLoopbackForAgent = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,17 +12,23 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include "AgentData.h"
|
||||
|
||||
const int STREAM_IDENTIFIER_NUM_BYTES = 8;
|
||||
|
||||
const char INJECT_AUDIO_AT_POINT_COMMAND = 'P';
|
||||
const char INJECT_AUDIO_AT_CUBE_COMMAND = 'C';
|
||||
|
||||
class AudioRingBuffer : public AgentData {
|
||||
public:
|
||||
AudioRingBuffer(int ringSamples, int bufferSamples);
|
||||
~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; }
|
||||
|
@ -39,8 +45,9 @@ public:
|
|||
void setShouldBeAddedToMix(bool shouldBeAddedToMix) { _shouldBeAddedToMix = shouldBeAddedToMix; }
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
float getAttenuationRatio() const { return _attenuationRatio; }
|
||||
float getBearing() const { return _bearing; }
|
||||
|
||||
bool shouldLoopbackForAgent() const { return _shouldLoopbackForAgent; }
|
||||
const unsigned char* getStreamIdentifier() const { return _streamIdentifier; }
|
||||
|
||||
|
@ -53,8 +60,9 @@ private:
|
|||
int _ringBufferLengthSamples;
|
||||
int _bufferLengthSamples;
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
float _radius;
|
||||
float _attenuationRatio;
|
||||
float _bearing;
|
||||
int16_t* _nextOutput;
|
||||
int16_t* _endOfLastWrite;
|
||||
int16_t* _buffer;
|
||||
|
|
|
@ -40,7 +40,7 @@ bool pingUnknownAgentThreadStopFlag = false;
|
|||
AgentList* AgentList::_sharedInstance = NULL;
|
||||
|
||||
AgentList* AgentList::createInstance(char ownerType, unsigned int socketListenPort) {
|
||||
if (_sharedInstance == NULL) {
|
||||
if (!_sharedInstance) {
|
||||
_sharedInstance = new AgentList(ownerType, socketListenPort);
|
||||
} else {
|
||||
printLog("AgentList createInstance called with existing instance.\n");
|
||||
|
@ -50,7 +50,7 @@ AgentList* AgentList::createInstance(char ownerType, unsigned int socketListenPo
|
|||
}
|
||||
|
||||
AgentList* AgentList::getInstance() {
|
||||
if (_sharedInstance == NULL) {
|
||||
if (!_sharedInstance) {
|
||||
printLog("AgentList getInstance called before call to createInstance. Returning NULL pointer.\n");
|
||||
}
|
||||
|
||||
|
@ -150,14 +150,12 @@ int AgentList::updateAgentWithData(sockaddr *senderAddress, unsigned char *packe
|
|||
int AgentList::updateAgentWithData(Agent *agent, unsigned char *packetData, int dataBytes) {
|
||||
agent->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
if (agent->getActiveSocket() != NULL) {
|
||||
if (agent->getActiveSocket()) {
|
||||
agent->recordBytesReceived(dataBytes);
|
||||
}
|
||||
|
||||
if (agent->getLinkedData() == NULL) {
|
||||
if (linkedDataCreateCallback != NULL) {
|
||||
linkedDataCreateCallback(agent);
|
||||
}
|
||||
if (!agent->getLinkedData() && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(agent);
|
||||
}
|
||||
|
||||
return agent->getLinkedData()->parseData(packetData, dataBytes);
|
||||
|
@ -165,7 +163,7 @@ int AgentList::updateAgentWithData(Agent *agent, unsigned char *packetData, int
|
|||
|
||||
Agent* AgentList::agentWithAddress(sockaddr *senderAddress) {
|
||||
for(AgentList::iterator agent = begin(); agent != end(); agent++) {
|
||||
if (agent->getActiveSocket() != NULL && socketMatch(agent->getActiveSocket(), senderAddress)) {
|
||||
if (agent->getActiveSocket() && socketMatch(agent->getActiveSocket(), senderAddress)) {
|
||||
return &(*agent);
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +214,7 @@ int AgentList::processDomainServerList(unsigned char *packetData, size_t dataByt
|
|||
Agent* AgentList::addOrUpdateAgent(sockaddr* publicSocket, sockaddr* localSocket, char agentType, uint16_t agentId) {
|
||||
AgentList::iterator agent = end();
|
||||
|
||||
if (publicSocket != NULL) {
|
||||
if (publicSocket) {
|
||||
for (agent = begin(); agent != end(); agent++) {
|
||||
if (agent->matches(publicSocket, localSocket, agentType)) {
|
||||
// we already have this agent, stop checking
|
||||
|
@ -327,8 +325,7 @@ void *pingUnknownAgents(void *args) {
|
|||
for(AgentList::iterator agent = agentList->begin();
|
||||
agent != agentList->end();
|
||||
agent++) {
|
||||
if (agent->getActiveSocket() == NULL
|
||||
&& (agent->getPublicSocket() != NULL && agent->getLocalSocket() != NULL)) {
|
||||
if (!agent->getActiveSocket() && agent->getPublicSocket() && agent->getLocalSocket()) {
|
||||
// ping both of the sockets for the agent so we can figure out
|
||||
// which socket we can use
|
||||
agentList->getAgentSocket()->send(agent->getPublicSocket(), &PACKET_HEADER_PING, 1);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
//
|
||||
// PacketHeaders.h
|
||||
// hifi
|
||||
|
|
Loading…
Reference in a new issue