mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-14 08:16:03 +02:00
clean up AudioRingBuffer by subclassing
This commit is contained in:
parent
6353940bf7
commit
4cb00ad54b
12 changed files with 429 additions and 345 deletions
20
audio-mixer/src/AvatarAudioRingBuffer.cpp
Normal file
20
audio-mixer/src/AvatarAudioRingBuffer.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// AvatarAudioRingBuffer.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 6/5/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "AvatarAudioRingBuffer.h"
|
||||
|
||||
AvatarAudioRingBuffer::AvatarAudioRingBuffer() : _freeVerbs() {
|
||||
|
||||
}
|
||||
|
||||
AvatarAudioRingBuffer::~AvatarAudioRingBuffer() {
|
||||
// enumerate the freeVerbs map and delete the FreeVerb objects
|
||||
for (FreeVerbAgentMap::iterator verbIterator = _freeVerbs.begin(); verbIterator != _freeVerbs.end(); verbIterator++) {
|
||||
delete verbIterator->second;
|
||||
}
|
||||
}
|
33
audio-mixer/src/AvatarAudioRingBuffer.h
Normal file
33
audio-mixer/src/AvatarAudioRingBuffer.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// AvatarAudioRingBuffer.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 6/5/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AvatarAudioRingBuffer__
|
||||
#define __hifi__AvatarAudioRingBuffer__
|
||||
|
||||
#include <Stk.h>
|
||||
#include <FreeVerb.h>
|
||||
|
||||
#include "PositionalAudioRingBuffer.h"
|
||||
|
||||
typedef std::map<uint16_t, stk::FreeVerb*> FreeVerbAgentMap;
|
||||
|
||||
class AvatarAudioRingBuffer : public PositionalAudioRingBuffer {
|
||||
public:
|
||||
AvatarAudioRingBuffer();
|
||||
~AvatarAudioRingBuffer();
|
||||
|
||||
FreeVerbAgentMap& getFreeVerbs() { return _freeVerbs; }
|
||||
private:
|
||||
// disallow copying of AvatarAudioRingBuffer objects
|
||||
AvatarAudioRingBuffer(const AvatarAudioRingBuffer&);
|
||||
AvatarAudioRingBuffer& operator= (const AvatarAudioRingBuffer&);
|
||||
|
||||
FreeVerbAgentMap _freeVerbs;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AvatarAudioRingBuffer__) */
|
17
audio-mixer/src/InjectedAudioRingBuffer.cpp
Normal file
17
audio-mixer/src/InjectedAudioRingBuffer.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// InjectedAudioRingBuffer.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 6/5/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "InjectedAudioRingBuffer.h"
|
||||
|
||||
InjectedAudioRingBuffer::InjectedAudioRingBuffer() :
|
||||
_radius(0.0f),
|
||||
_attenuationRatio(0),
|
||||
_streamIdentifier()
|
||||
{
|
||||
|
||||
}
|
33
audio-mixer/src/InjectedAudioRingBuffer.h
Normal file
33
audio-mixer/src/InjectedAudioRingBuffer.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// InjectedAudioRingBuffer.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 6/5/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__InjectedAudioRingBuffer__
|
||||
#define __hifi__InjectedAudioRingBuffer__
|
||||
|
||||
#include <AudioInjector.h>
|
||||
|
||||
#include "PositionalAudioRingBuffer.h"
|
||||
|
||||
class InjectedAudioRingBuffer : public PositionalAudioRingBuffer {
|
||||
public:
|
||||
InjectedAudioRingBuffer();
|
||||
|
||||
float getRadius() const { return _radius; }
|
||||
float getAttenuationRatio() const { return _attenuationRatio; }
|
||||
const unsigned char* getStreamIdentifier() const { return _streamIdentifier; }
|
||||
private:
|
||||
// disallow copying of InjectedAudioRingBuffer objects
|
||||
InjectedAudioRingBuffer(const InjectedAudioRingBuffer&);
|
||||
InjectedAudioRingBuffer& operator= (const InjectedAudioRingBuffer&);
|
||||
|
||||
float _radius;
|
||||
float _attenuationRatio;
|
||||
unsigned char _streamIdentifier[STREAM_IDENTIFIER_NUM_BYTES];
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__InjectedAudioRingBuffer__) */
|
38
audio-mixer/src/PositionalAudioRingBuffer.cpp
Normal file
38
audio-mixer/src/PositionalAudioRingBuffer.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// PositionalAudioRingBuffer.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 6/5/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "PositionalAudioRingBuffer.h"
|
||||
|
||||
PositionalAudioRingBuffer::PositionalAudioRingBuffer() :
|
||||
AudioRingBuffer(false),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
_shouldLoopbackForAgent(false),
|
||||
_wasAddedToMix(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) {
|
||||
if (_endOfLastWrite) {
|
||||
if (!_isStarted && diffLastWriteNextOutput() <= BUFFER_LENGTH_SAMPLES_PER_CHANNEL + numJitterBufferSamples) {
|
||||
printf("Buffer held back\n");
|
||||
return false;
|
||||
} else if (diffLastWriteNextOutput() < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
printf("Buffer starved.\n");
|
||||
_isStarted = false;
|
||||
return false;
|
||||
} else {
|
||||
// good buffer, add this to the mix
|
||||
_isStarted = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
41
audio-mixer/src/PositionalAudioRingBuffer.h
Normal file
41
audio-mixer/src/PositionalAudioRingBuffer.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// PositionalAudioRingBuffer.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 6/5/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__PositionalAudioRingBuffer__
|
||||
#define __hifi__PositionalAudioRingBuffer__
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <AudioRingBuffer.h>
|
||||
|
||||
class PositionalAudioRingBuffer : public AudioRingBuffer {
|
||||
public:
|
||||
PositionalAudioRingBuffer();
|
||||
|
||||
bool shouldBeAddedToMix(int numJitterBufferSamples);
|
||||
|
||||
bool wasAddedToMix() const { return _wasAddedToMix; }
|
||||
void setWasAddedToMix(bool wasAddedToMix) { _wasAddedToMix = wasAddedToMix; }
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
|
||||
bool shouldLoopbackForAgent() const { return _shouldLoopbackForAgent; }
|
||||
|
||||
protected:
|
||||
// disallow copying of PositionalAudioRingBuffer objects
|
||||
PositionalAudioRingBuffer(const PositionalAudioRingBuffer&);
|
||||
PositionalAudioRingBuffer& operator= (const PositionalAudioRingBuffer&);
|
||||
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
bool _shouldLoopbackForAgent;
|
||||
bool _wasAddedToMix;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__PositionalAudioRingBuffer__) */
|
|
@ -27,7 +27,9 @@
|
|||
#include <Stk.h>
|
||||
#include <FreeVerb.h>
|
||||
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "InjectedAudioRingBuffer.h"
|
||||
#include "AvatarAudioRingBuffer.h"
|
||||
#include <AudioRingBuffer.h>
|
||||
#include "PacketHeaders.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -43,17 +45,9 @@
|
|||
|
||||
const unsigned short MIXER_LISTEN_PORT = 55443;
|
||||
|
||||
const float SAMPLE_RATE = 22050.0;
|
||||
|
||||
const short JITTER_BUFFER_MSECS = 12;
|
||||
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();
|
||||
|
@ -70,7 +64,11 @@ void plateauAdditionOfSamples(int16_t &mixSample, int16_t sampleToAdd) {
|
|||
|
||||
void attachNewBufferToAgent(Agent *newAgent) {
|
||||
if (!newAgent->getLinkedData()) {
|
||||
newAgent->setLinkedData(new AudioRingBuffer(RING_BUFFER_SAMPLES, BUFFER_LENGTH_SAMPLES_PER_CHANNEL));
|
||||
if (newAgent->getType() == AGENT_TYPE_AVATAR) {
|
||||
newAgent->setLinkedData(new AvatarAudioRingBuffer());
|
||||
} else {
|
||||
newAgent->setLinkedData(new InjectedAudioRingBuffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,203 +111,176 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
while (true) {
|
||||
// enumerate the agents, check if we can add audio from the agent to current mix
|
||||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
AudioRingBuffer* agentBuffer = (AudioRingBuffer*) agent->getLinkedData();
|
||||
|
||||
if (agentBuffer->getEndOfLastWrite()) {
|
||||
if (!agentBuffer->isStarted()
|
||||
&& agentBuffer->diffLastWriteNextOutput() <= BUFFER_LENGTH_SAMPLES_PER_CHANNEL + JITTER_BUFFER_SAMPLES) {
|
||||
printf("Held back buffer for agent with ID %d.\n", agent->getAgentID());
|
||||
agentBuffer->setShouldBeAddedToMix(false);
|
||||
} else if (agentBuffer->diffLastWriteNextOutput() < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
printf("Buffer from agent with ID %d starved.\n", agent->getAgentID());
|
||||
agentBuffer->setStarted(false);
|
||||
agentBuffer->setShouldBeAddedToMix(false);
|
||||
} else {
|
||||
// good buffer, add this to the mix
|
||||
agentBuffer->setStarted(true);
|
||||
agentBuffer->setShouldBeAddedToMix(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int numAgents = agentList->size();
|
||||
SharedAudioFactors audioFactors[numAgents][numAgents];
|
||||
memset(audioFactors, 0, sizeof(audioFactors));
|
||||
|
||||
while (true) {
|
||||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
if (agent->getType() == AGENT_TYPE_AVATAR) {
|
||||
AudioRingBuffer* agentRingBuffer = (AudioRingBuffer*) agent->getLinkedData();
|
||||
AvatarAudioRingBuffer* agentRingBuffer = (AvatarAudioRingBuffer*) agent->getLinkedData();
|
||||
|
||||
// zero out the client mix for this agent
|
||||
memset(clientSamples, 0, sizeof(clientSamples));
|
||||
|
||||
for (AgentList::iterator otherAgent = agentList->begin(); otherAgent != agentList->end(); otherAgent++) {
|
||||
if (otherAgent != agent || (otherAgent == agent && agentRingBuffer->shouldLoopbackForAgent())) {
|
||||
AudioRingBuffer* otherAgentBuffer = (AudioRingBuffer*) otherAgent->getLinkedData();
|
||||
if ((otherAgent != agent
|
||||
&& ((PositionalAudioRingBuffer*)otherAgent->getLinkedData())->shouldBeAddedToMix(JITTER_BUFFER_SAMPLES))
|
||||
|| (otherAgent == agent && agentRingBuffer->shouldLoopbackForAgent())) {
|
||||
|
||||
if (otherAgentBuffer->shouldBeAddedToMix()) {
|
||||
PositionalAudioRingBuffer* otherAgentBuffer = (PositionalAudioRingBuffer*) otherAgent->getLinkedData();
|
||||
otherAgentBuffer->setWasAddedToMix(true);
|
||||
|
||||
float bearingRelativeAngleToSource = 0.0f;
|
||||
float attenuationCoefficient = 1.0f;
|
||||
int numSamplesDelay = 0;
|
||||
float weakChannelAmplitudeRatio = 1.0f;
|
||||
|
||||
stk::FreeVerb* otherAgentFreeVerb = NULL;
|
||||
|
||||
if (otherAgent != agent) {
|
||||
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 bearingRelativeAngleToSource = 0.f;
|
||||
float attenuationCoefficient = 1.0f;
|
||||
int numSamplesDelay = 0;
|
||||
float weakChannelAmplitudeRatio = 1.0f;
|
||||
float distanceSquareToSource = glm::dot(relativePosition, relativePosition);
|
||||
float radius = 0.0f;
|
||||
|
||||
stk::FreeVerb* otherAgentFreeVerb = NULL;
|
||||
|
||||
if (otherAgent != agent) {
|
||||
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
|
||||
|
||||
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 {
|
||||
// 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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
FreeVerbAgentMap& agentFreeVerbs = agentRingBuffer->getFreeVerbs();
|
||||
FreeVerbAgentMap::iterator freeVerbIterator = agentFreeVerbs.find(otherAgent->getAgentID());
|
||||
|
||||
if (freeVerbIterator == agentFreeVerbs.end()) {
|
||||
// setup the freeVerb effect for this source for this client
|
||||
|
||||
printf("Creating a new FV object\n");
|
||||
|
||||
otherAgentFreeVerb = agentFreeVerbs[otherAgent->getAgentID()] = new stk::FreeVerb;
|
||||
|
||||
otherAgentFreeVerb->setDamping(DISTANCE_REVERB_DAMPING);
|
||||
otherAgentFreeVerb->setRoomSize(DISTANCE_REVERB_ROOM_SIZE);
|
||||
otherAgentFreeVerb->setWidth(DISTANCE_REVERB_WIDTH);
|
||||
|
||||
} else {
|
||||
otherAgentFreeVerb = freeVerbIterator->second;
|
||||
}
|
||||
|
||||
otherAgentFreeVerb->setEffectMix(audioFactors[lowAgentIndex][highAgentIndex].effectMix);
|
||||
if (otherAgent->getType() == AGENT_TYPE_AUDIO_INJECTOR) {
|
||||
radius = ((InjectedAudioRingBuffer*) otherAgentBuffer)->getRadius();
|
||||
}
|
||||
|
||||
int16_t* goodChannel = 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
|
||||
: 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]
|
||||
* attenuationCoefficient
|
||||
* weakChannelAmplitudeRatio;
|
||||
if (radius == 0 || (distanceSquareToSource > radius * radius)) {
|
||||
// this is either not a spherical source, or the listener is outside the sphere
|
||||
|
||||
if (radius > 0) {
|
||||
// this is a spherical source - the distance used for the coefficient
|
||||
// needs to be the closest point on the boundary to the source
|
||||
|
||||
// apply the STK FreeVerb effect
|
||||
if (otherAgentFreeVerb) {
|
||||
earlierSample = otherAgentFreeVerb->tick(earlierSample);
|
||||
}
|
||||
// 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
|
||||
|
||||
plateauAdditionOfSamples(delayedChannel[s], earlierSample);
|
||||
glm::vec3 closestPoint = glm::normalize(relativePosition) * radius;
|
||||
|
||||
// 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 {
|
||||
// calculate the angle delivery for off-axis attenuation
|
||||
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;
|
||||
|
||||
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
|
||||
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f));
|
||||
|
||||
// multiply the current attenuation coefficient by the calculated off axis coefficient
|
||||
attenuationCoefficient *= offAxisCoefficient;
|
||||
}
|
||||
|
||||
int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] * attenuationCoefficient);
|
||||
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
|
||||
float distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
|
||||
DISTANCE_SCALE_LOG +
|
||||
(0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
|
||||
distanceCoefficient = std::min(1.0f, distanceCoefficient);
|
||||
|
||||
// multiply the current attenuation coefficient by the distance coefficient
|
||||
attenuationCoefficient *= distanceCoefficient;
|
||||
|
||||
// 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;
|
||||
|
||||
// figure out the number of samples of delay and the ratio of the amplitude
|
||||
// in the weak channel for audio spatialization
|
||||
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
|
||||
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
|
||||
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
|
||||
}
|
||||
|
||||
FreeVerbAgentMap& agentFreeVerbs = agentRingBuffer->getFreeVerbs();
|
||||
FreeVerbAgentMap::iterator freeVerbIterator = agentFreeVerbs.find(otherAgent->getAgentID());
|
||||
|
||||
if (freeVerbIterator == agentFreeVerbs.end()) {
|
||||
// setup the freeVerb effect for this source for this client
|
||||
otherAgentFreeVerb = agentFreeVerbs[otherAgent->getAgentID()] = new stk::FreeVerb;
|
||||
|
||||
otherAgentFreeVerb->setDamping(DISTANCE_REVERB_DAMPING);
|
||||
otherAgentFreeVerb->setRoomSize(DISTANCE_REVERB_ROOM_SIZE);
|
||||
otherAgentFreeVerb->setWidth(DISTANCE_REVERB_WIDTH);
|
||||
|
||||
} else {
|
||||
otherAgentFreeVerb = freeVerbIterator->second;
|
||||
}
|
||||
|
||||
const float DISTANCE_REVERB_LOG_REMAINDER = 0.32f;
|
||||
const float DISTANCE_REVERB_MAX_WETNESS = 1.0f;
|
||||
|
||||
float effectMix = powf(2.0f, (0.5f * logf(distanceSquareToSource)
|
||||
/ logf(2.0f)) - DISTANCE_REVERB_LOG_REMAINDER)
|
||||
* DISTANCE_REVERB_MAX_WETNESS / 64.0f;;
|
||||
|
||||
otherAgentFreeVerb->setEffectMix(effectMix);
|
||||
}
|
||||
|
||||
int16_t* goodChannel = (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_LENGTH_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]
|
||||
* attenuationCoefficient
|
||||
* weakChannelAmplitudeRatio;
|
||||
|
||||
// apply the STK FreeVerb effect
|
||||
if (otherAgentFreeVerb) {
|
||||
currentSample = otherAgentFreeVerb->tick(currentSample);
|
||||
earlierSample = otherAgentFreeVerb->tick(earlierSample);
|
||||
}
|
||||
|
||||
plateauAdditionOfSamples(goodChannel[s], currentSample);
|
||||
|
||||
if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay],
|
||||
currentSample * weakChannelAmplitudeRatio);
|
||||
}
|
||||
plateauAdditionOfSamples(delayedChannel[s], earlierSample);
|
||||
}
|
||||
|
||||
int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] * attenuationCoefficient);
|
||||
|
||||
// apply the STK FreeVerb effect
|
||||
if (otherAgentFreeVerb) {
|
||||
currentSample = otherAgentFreeVerb->tick(currentSample);
|
||||
}
|
||||
|
||||
plateauAdditionOfSamples(goodChannel[s], currentSample);
|
||||
|
||||
if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay],
|
||||
currentSample * weakChannelAmplitudeRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -322,15 +293,15 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
// push forward the next output pointers for any audio buffers we used
|
||||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
AudioRingBuffer* agentBuffer = (AudioRingBuffer*) agent->getLinkedData();
|
||||
if (agentBuffer && agentBuffer->shouldBeAddedToMix()) {
|
||||
PositionalAudioRingBuffer* agentBuffer = (PositionalAudioRingBuffer*) agent->getLinkedData();
|
||||
if (agentBuffer && agentBuffer->wasAddedToMix()) {
|
||||
agentBuffer->setNextOutput(agentBuffer->getNextOutput() + BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
|
||||
if (agentBuffer->getNextOutput() >= agentBuffer->getBuffer() + RING_BUFFER_SAMPLES) {
|
||||
if (agentBuffer->getNextOutput() >= agentBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES) {
|
||||
agentBuffer->setNextOutput(agentBuffer->getBuffer());
|
||||
}
|
||||
|
||||
agentBuffer->setShouldBeAddedToMix(false);
|
||||
agentBuffer->setWasAddedToMix(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,7 +319,7 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
agentList->updateAgentWithData(agentAddress, packetData, receivedBytes);
|
||||
|
||||
if (std::isnan(((AudioRingBuffer *)avatarAgent->getLinkedData())->getOrientation().x)) {
|
||||
if (std::isnan(((PositionalAudioRingBuffer *)avatarAgent->getLinkedData())->getOrientation().x)) {
|
||||
// kill off this agent - temporary solution to mixer crash on mac sleep
|
||||
avatarAgent->setAlive(false);
|
||||
}
|
||||
|
@ -358,7 +329,7 @@ int main(int argc, const char* argv[]) {
|
|||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
if (agent->getLinkedData()) {
|
||||
|
||||
AudioRingBuffer* ringBuffer = (AudioRingBuffer*) agent->getLinkedData();
|
||||
InjectedAudioRingBuffer* ringBuffer = (InjectedAudioRingBuffer*) agent->getLinkedData();
|
||||
if (memcmp(ringBuffer->getStreamIdentifier(),
|
||||
packetData + 1,
|
||||
STREAM_IDENTIFIER_NUM_BYTES) == 0) {
|
||||
|
|
|
@ -32,12 +32,6 @@ 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;
|
||||
|
||||
|
@ -47,12 +41,11 @@ const float FLANGE_BASE_RATE = 4;
|
|||
const float MAX_FLANGE_SAMPLE_WEIGHT = 0.50;
|
||||
const float MIN_FLANGE_INTENSITY = 0.25;
|
||||
|
||||
const int SAMPLE_RATE = 22050;
|
||||
const float JITTER_BUFFER_LENGTH_MSECS = 12;
|
||||
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS *
|
||||
NUM_AUDIO_CHANNELS * (SAMPLE_RATE / 1000.0);
|
||||
|
||||
const float AUDIO_CALLBACK_MSECS = (float)BUFFER_LENGTH_SAMPLES / (float)SAMPLE_RATE * 1000.0;
|
||||
const float AUDIO_CALLBACK_MSECS = (float)BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0;
|
||||
|
||||
|
||||
const int AGENT_LOOPBACK_MODIFIER = 307;
|
||||
|
@ -92,7 +85,7 @@ int audioCallback (const void* inputBuffer,
|
|||
int16_t* outputRight = ((int16_t**) outputBuffer)[1];
|
||||
|
||||
// Add Procedural effects to input samples
|
||||
parentAudio->addProceduralSounds(inputLeft, BUFFER_LENGTH_SAMPLES);
|
||||
parentAudio->addProceduralSounds(inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
|
||||
// add output (@speakers) data to the scope
|
||||
parentAudio->_scope->addSamples(1, outputLeft, PACKET_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
|
@ -120,15 +113,15 @@ int audioCallback (const void* inputBuffer,
|
|||
|
||||
// Measure the loudness of the signal from the microphone and store in audio object
|
||||
float loudness = 0;
|
||||
for (int i = 0; i < BUFFER_LENGTH_SAMPLES; i++) {
|
||||
for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
||||
loudness += abs(inputLeft[i]);
|
||||
}
|
||||
|
||||
loudness /= BUFFER_LENGTH_SAMPLES;
|
||||
loudness /= BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
parentAudio->_lastInputLoudness = loudness;
|
||||
|
||||
// add input (@microphone) data to the scope
|
||||
parentAudio->_scope->addSamples(0, inputLeft, BUFFER_LENGTH_SAMPLES);
|
||||
parentAudio->_scope->addSamples(0, inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
|
||||
Agent* audioMixer = agentList->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER);
|
||||
|
||||
|
@ -172,11 +165,11 @@ int audioCallback (const void* inputBuffer,
|
|||
// if we've been reset, and there isn't any new packets yet
|
||||
// just play some silence
|
||||
|
||||
if (ringBuffer->getEndOfLastWrite() != NULL) {
|
||||
if (ringBuffer->getEndOfLastWrite()) {
|
||||
|
||||
if (!ringBuffer->isStarted() && ringBuffer->diffLastWriteNextOutput() < PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) {
|
||||
//printLog("Held back, buffer has %d of %d samples required.\n",
|
||||
// ringBuffer->diffLastWriteNextOutput(), PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES);
|
||||
// printLog("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);
|
||||
|
||||
|
@ -236,7 +229,7 @@ int audioCallback (const void* inputBuffer,
|
|||
// we need to grab the flange sample from earlier in the buffer
|
||||
flangeFrame = ringBuffer->getNextOutput() != ringBuffer->getBuffer()
|
||||
? ringBuffer->getNextOutput() - PACKET_LENGTH_SAMPLES
|
||||
: ringBuffer->getNextOutput() + RING_BUFFER_SAMPLES - PACKET_LENGTH_SAMPLES;
|
||||
: ringBuffer->getNextOutput() + RING_BUFFER_LENGTH_SAMPLES - PACKET_LENGTH_SAMPLES;
|
||||
|
||||
flangeIndex = PACKET_LENGTH_SAMPLES_PER_CHANNEL + (s - sampleFlangeDelay);
|
||||
}
|
||||
|
@ -260,7 +253,7 @@ int audioCallback (const void* inputBuffer,
|
|||
}
|
||||
ringBuffer->setNextOutput(ringBuffer->getNextOutput() + PACKET_LENGTH_SAMPLES);
|
||||
|
||||
if (ringBuffer->getNextOutput() == ringBuffer->getBuffer() + RING_BUFFER_SAMPLES) {
|
||||
if (ringBuffer->getNextOutput() == ringBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES) {
|
||||
ringBuffer->setNextOutput(ringBuffer->getBuffer());
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +283,7 @@ void outputPortAudioError(PaError error) {
|
|||
|
||||
Audio::Audio(Oscilloscope* scope) :
|
||||
_stream(NULL),
|
||||
_ringBuffer(RING_BUFFER_SAMPLES, PACKET_LENGTH_SAMPLES),
|
||||
_ringBuffer(true),
|
||||
_scope(scope),
|
||||
_averagedLatency(0.0),
|
||||
_measuredJitter(0),
|
||||
|
@ -315,7 +308,7 @@ Audio::Audio(Oscilloscope* scope) :
|
|||
2,
|
||||
(paInt16 | paNonInterleaved),
|
||||
SAMPLE_RATE,
|
||||
BUFFER_LENGTH_SAMPLES,
|
||||
BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
|
||||
audioCallback,
|
||||
(void*) this));
|
||||
|
||||
|
@ -324,8 +317,8 @@ Audio::Audio(Oscilloscope* scope) :
|
|||
|
||||
_echoInputSamples = new int16_t[BUFFER_LENGTH_BYTES];
|
||||
_echoOutputSamples = new int16_t[BUFFER_LENGTH_BYTES];
|
||||
memset(_echoInputSamples, 0, BUFFER_LENGTH_SAMPLES * sizeof(int));
|
||||
memset(_echoOutputSamples, 0, BUFFER_LENGTH_SAMPLES * sizeof(int));
|
||||
memset(_echoInputSamples, 0, BUFFER_LENGTH_SAMPLES_PER_CHANNEL * sizeof(int));
|
||||
memset(_echoOutputSamples, 0, BUFFER_LENGTH_SAMPLES_PER_CHANNEL * sizeof(int));
|
||||
|
||||
gettimeofday(&_lastReceiveTime, NULL);
|
||||
}
|
||||
|
@ -347,13 +340,13 @@ void Audio::renderEchoCompare() {
|
|||
glDisable(GL_LINE_SMOOTH);
|
||||
glColor3f(1,1,1);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (int i = 0; i < BUFFER_LENGTH_SAMPLES; i++) {
|
||||
for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
||||
glVertex2f(XPOS + i * XSCALE, YPOS + _echoInputSamples[i]/YSCALE);
|
||||
}
|
||||
glEnd();
|
||||
glColor3f(0,1,1);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (int i = 0; i < BUFFER_LENGTH_SAMPLES; i++) {
|
||||
for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
||||
glVertex2f(XPOS + i * XSCALE, YPOS + _echoOutputSamples[i]/YSCALE);
|
||||
}
|
||||
glEnd();
|
||||
|
@ -468,7 +461,7 @@ void Audio::render(int screenWidth, int screenHeight) {
|
|||
glVertex2f(currentX, topY);
|
||||
glVertex2f(currentX, bottomY);
|
||||
|
||||
for (int i = 0; i < RING_BUFFER_FRAMES; i++) {
|
||||
for (int i = 0; i < RING_BUFFER_LENGTH_FRAMES; i++) {
|
||||
glVertex2f(currentX, halfY);
|
||||
glVertex2f(currentX + frameWidth, halfY);
|
||||
currentX += frameWidth;
|
||||
|
|
|
@ -110,10 +110,10 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
|
|||
gettimeofday(&startTime, NULL);
|
||||
int nextFrame = 0;
|
||||
|
||||
for (int i = 0; i < _numTotalSamples; i += BUFFER_LENGTH_SAMPLES) {
|
||||
int numSamplesToCopy = BUFFER_LENGTH_SAMPLES;
|
||||
for (int i = 0; i < _numTotalSamples; i += BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
int numSamplesToCopy = BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
|
||||
if (_numTotalSamples - i < BUFFER_LENGTH_SAMPLES) {
|
||||
if (_numTotalSamples - i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
numSamplesToCopy = _numTotalSamples - i;
|
||||
memset(currentPacketPtr + numSamplesToCopy, 0, BUFFER_LENGTH_BYTES - (numSamplesToCopy * sizeof(int16_t)));
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
|
|||
|
||||
injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket));
|
||||
|
||||
double usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
double usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow();
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
|
|
@ -9,16 +9,18 @@
|
|||
#ifndef __hifi__AudioInjector__
|
||||
#define __hifi__AudioInjector__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <UDPSocket.h>
|
||||
|
||||
#include "AudioRingBuffer.h"
|
||||
|
||||
const int BUFFER_LENGTH_BYTES = 512;
|
||||
const int BUFFER_LENGTH_SAMPLES = BUFFER_LENGTH_BYTES / sizeof(int16_t);
|
||||
const float SAMPLE_RATE = 22050.0f;
|
||||
const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES / SAMPLE_RATE) * 1000000;
|
||||
const int STREAM_IDENTIFIER_NUM_BYTES = 8;
|
||||
const char INJECT_AUDIO_AT_POINT_COMMAND = 'P';
|
||||
const char INJECT_AUDIO_AT_CUBE_COMMAND = 'C';
|
||||
|
||||
const float INJECT_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000;
|
||||
|
||||
class AudioInjector {
|
||||
public:
|
||||
|
|
|
@ -13,103 +13,61 @@
|
|||
|
||||
#include "AudioRingBuffer.h"
|
||||
|
||||
AudioRingBuffer::AudioRingBuffer(int ringSamples, int bufferSamples) :
|
||||
AudioRingBuffer::AudioRingBuffer(bool isStereo) :
|
||||
AgentData(NULL),
|
||||
_ringBufferLengthSamples(ringSamples),
|
||||
_bufferLengthSamples(bufferSamples),
|
||||
_radius(0.0f),
|
||||
_endOfLastWrite(NULL),
|
||||
_started(false),
|
||||
_shouldBeAddedToMix(false),
|
||||
_shouldLoopbackForAgent(false),
|
||||
_streamIdentifier()
|
||||
_isStarted(false),
|
||||
_isStereo(isStereo)
|
||||
{
|
||||
_buffer = new int16_t[_ringBufferLengthSamples];
|
||||
_buffer = new int16_t[RING_BUFFER_LENGTH_SAMPLES];
|
||||
_nextOutput = _buffer;
|
||||
};
|
||||
|
||||
AudioRingBuffer::~AudioRingBuffer() {
|
||||
delete[] _buffer;
|
||||
};
|
||||
|
||||
const int AGENT_LOOPBACK_MODIFIER = 307;
|
||||
|
||||
int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
|
||||
unsigned char* dataBuffer = sourceBuffer + 1;
|
||||
|
||||
if (sourceBuffer[0] == PACKET_HEADER_INJECT_AUDIO ||
|
||||
sourceBuffer[0] == PACKET_HEADER_MICROPHONE_AUDIO) {
|
||||
// if this came from an injector or interface client
|
||||
// there's data required for spatialization to pull out
|
||||
|
||||
if (sourceBuffer[0] == PACKET_HEADER_INJECT_AUDIO) {
|
||||
// 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);
|
||||
|
||||
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(&_orientation, dataBuffer, sizeof(_orientation));
|
||||
dataBuffer += sizeof(_orientation);
|
||||
|
||||
// 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 {
|
||||
// currently no possiblity for loopback, need to add once quaternion audio is working again
|
||||
_shouldLoopbackForAgent = false;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we have enough bytes left for this to be the right amount of audio
|
||||
// otherwise we should not copy that data, and leave the buffer pointers where they are
|
||||
if (numBytes - (dataBuffer - sourceBuffer) == _bufferLengthSamples * sizeof(int16_t)) {
|
||||
if (!_endOfLastWrite) {
|
||||
_endOfLastWrite = _buffer;
|
||||
} else if (diffLastWriteNextOutput() > _ringBufferLengthSamples - _bufferLengthSamples) {
|
||||
_endOfLastWrite = _buffer;
|
||||
_nextOutput = _buffer;
|
||||
_started = false;
|
||||
}
|
||||
|
||||
memcpy(_endOfLastWrite, dataBuffer, _bufferLengthSamples * sizeof(int16_t));
|
||||
|
||||
_endOfLastWrite += _bufferLengthSamples;
|
||||
|
||||
if (_endOfLastWrite >= _buffer + _ringBufferLengthSamples) {
|
||||
_endOfLastWrite = _buffer;
|
||||
}
|
||||
}
|
||||
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
short AudioRingBuffer::diffLastWriteNextOutput() {
|
||||
int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
return parseAudioSamples(sourceBuffer + sizeof(PACKET_HEADER_MIXED_AUDIO), numBytes - sizeof(PACKET_HEADER_MIXED_AUDIO));
|
||||
}
|
||||
|
||||
int AudioRingBuffer::parseAudioSamples(unsigned char* sourceBuffer, int numBytes) {
|
||||
// make sure we have enough bytes left for this to be the right amount of audio
|
||||
// otherwise we should not copy that data, and leave the buffer pointers where they are
|
||||
int samplesToCopy = BUFFER_LENGTH_SAMPLES_PER_CHANNEL * (_isStereo ? 2 : 1);
|
||||
|
||||
if (numBytes == samplesToCopy * sizeof(int16_t)) {
|
||||
|
||||
if (!_endOfLastWrite) {
|
||||
_endOfLastWrite = _buffer;
|
||||
} else if (diffLastWriteNextOutput() > RING_BUFFER_LENGTH_SAMPLES - samplesToCopy) {
|
||||
_endOfLastWrite = _buffer;
|
||||
_nextOutput = _buffer;
|
||||
_isStarted = false;
|
||||
}
|
||||
|
||||
memcpy(_endOfLastWrite, sourceBuffer, numBytes);
|
||||
|
||||
_endOfLastWrite += samplesToCopy;
|
||||
|
||||
if (_endOfLastWrite >= _buffer + RING_BUFFER_LENGTH_SAMPLES) {
|
||||
_endOfLastWrite = _buffer;
|
||||
}
|
||||
|
||||
return numBytes;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int AudioRingBuffer::diffLastWriteNextOutput() const {
|
||||
if (!_endOfLastWrite) {
|
||||
return 0;
|
||||
} else {
|
||||
short sampleDifference = _endOfLastWrite - _nextOutput;
|
||||
int sampleDifference = _endOfLastWrite - _nextOutput;
|
||||
|
||||
if (sampleDifference < 0) {
|
||||
sampleDifference += _ringBufferLengthSamples;
|
||||
sampleDifference += RING_BUFFER_LENGTH_SAMPLES;
|
||||
}
|
||||
|
||||
return sampleDifference;
|
||||
|
|
|
@ -13,26 +13,25 @@
|
|||
#include <map>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <Stk.h>
|
||||
#include <FreeVerb.h>
|
||||
|
||||
#include "AgentData.h"
|
||||
|
||||
const int STREAM_IDENTIFIER_NUM_BYTES = 8;
|
||||
typedef std::map<uint16_t, stk::FreeVerb*> FreeVerbAgentMap;
|
||||
const float SAMPLE_RATE = 22050.0;
|
||||
|
||||
const char INJECT_AUDIO_AT_POINT_COMMAND = 'P';
|
||||
const char INJECT_AUDIO_AT_CUBE_COMMAND = 'C';
|
||||
const int BUFFER_LENGTH_BYTES = 1024;
|
||||
const int BUFFER_LENGTH_SAMPLES_PER_CHANNEL = (BUFFER_LENGTH_BYTES / 2) / sizeof(int16_t);
|
||||
|
||||
const short RING_BUFFER_LENGTH_FRAMES = 10;
|
||||
const short RING_BUFFER_LENGTH_SAMPLES = RING_BUFFER_LENGTH_FRAMES * BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
|
||||
class AudioRingBuffer : public AgentData {
|
||||
public:
|
||||
AudioRingBuffer(int ringSamples, int bufferSamples);
|
||||
AudioRingBuffer(bool isStereo);
|
||||
~AudioRingBuffer();
|
||||
|
||||
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
float getRadius() const { return _radius; }
|
||||
int parseAudioSamples(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
int16_t* getNextOutput() const { return _nextOutput; }
|
||||
void setNextOutput(int16_t* nextOutput) { _nextOutput = nextOutput; }
|
||||
|
@ -42,41 +41,20 @@ public:
|
|||
|
||||
int16_t* getBuffer() const { return _buffer; }
|
||||
|
||||
FreeVerbAgentMap& getFreeVerbs() { return _freeVerbs; }
|
||||
|
||||
bool isStarted() const { return _started; }
|
||||
void setStarted(bool started) { _started = started; }
|
||||
|
||||
bool shouldBeAddedToMix() const { return _shouldBeAddedToMix; }
|
||||
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; }
|
||||
|
||||
bool shouldLoopbackForAgent() const { return _shouldLoopbackForAgent; }
|
||||
const unsigned char* getStreamIdentifier() const { return _streamIdentifier; }
|
||||
bool isStarted() const { return _isStarted; }
|
||||
void setStarted(bool isStarted) { _isStarted = isStarted; }
|
||||
|
||||
short diffLastWriteNextOutput();
|
||||
private:
|
||||
int diffLastWriteNextOutput() const;
|
||||
protected:
|
||||
// disallow copying of AudioRingBuffer objects
|
||||
AudioRingBuffer(const AudioRingBuffer&);
|
||||
AudioRingBuffer& operator= (const AudioRingBuffer&);
|
||||
|
||||
int _ringBufferLengthSamples;
|
||||
int _bufferLengthSamples;
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
float _radius;
|
||||
float _attenuationRatio;
|
||||
int16_t* _nextOutput;
|
||||
int16_t* _endOfLastWrite;
|
||||
int16_t* _buffer;
|
||||
bool _started;
|
||||
bool _shouldBeAddedToMix;
|
||||
bool _shouldLoopbackForAgent;
|
||||
unsigned char _streamIdentifier[STREAM_IDENTIFIER_NUM_BYTES];
|
||||
FreeVerbAgentMap _freeVerbs;
|
||||
bool _isStarted;
|
||||
bool _isStereo;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__AudioRingBuffer__) */
|
||||
|
|
Loading…
Reference in a new issue