add hooks for local audio echo, first cut at diffusion

This commit is contained in:
ZappoMan 2014-04-06 10:11:11 -07:00
parent d198b04daf
commit dfd6411a4f
5 changed files with 142 additions and 22 deletions

View file

@ -1603,7 +1603,8 @@ void Application::init() {
_audioReflector.setMyAvatar(getAvatar());
_audioReflector.setVoxels(_voxels.getTree());
_audioReflector.setAudio(getAudio());
connect(getAudio(), &Audio::processSpatialAudio, &_audioReflector, &AudioReflector::processSpatialAudio,Qt::DirectConnection);
connect(getAudio(), &Audio::processInboundAudio, &_audioReflector, &AudioReflector::processInboundAudio,Qt::DirectConnection);
connect(getAudio(), &Audio::processLocalAudio, &_audioReflector, &AudioReflector::processLocalAudio,Qt::DirectConnection);
}
void Application::closeMirrorView() {

View file

@ -360,6 +360,11 @@ void Audio::handleAudioInput() {
QByteArray inputByteArray = _inputDevice->readAll();
// send our local loopback to any interested parties
if (_processSpatialAudio && !_muted && _audioOutput) {
emit processLocalAudio(_spatialAudioStart, inputByteArray, _inputFormat);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) {
// if this person wants local loopback add that to the locally injected audio
@ -367,7 +372,7 @@ void Audio::handleAudioInput() {
// we didn't have the loopback output device going so set that up now
_loopbackOutputDevice = _loopbackAudioOutput->start();
}
if (_inputFormat == _outputFormat) {
if (_loopbackOutputDevice) {
_loopbackOutputDevice->write(inputByteArray);
@ -756,7 +761,7 @@ void Audio::processReceivedAudio(AudioRingBuffer& ringBuffer) {
}
// Send audio off for spatial processing
emit processSpatialAudio(sampleTime, buffer, _desiredOutputFormat);
emit processInboundAudio(sampleTime, buffer, _desiredOutputFormat);
// copy the samples we'll resample from the spatial audio ring buffer - this also
// pushes the read pointer of the spatial audio ring buffer forwards

View file

@ -102,7 +102,8 @@ public slots:
signals:
bool muteToggled();
void processSpatialAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
void processInboundAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
void processLocalAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
private:

View file

@ -16,13 +16,16 @@ const float DEFAULT_PRE_DELAY = 20.0f; // this delay in msecs will always be add
const float DEFAULT_MS_DELAY_PER_METER = 3.0f;
const float MINIMUM_ATTENUATION_TO_REFLECT = 1.0f / 256.0f;
const float DEFAULT_DISTANCE_SCALING_FACTOR = 2.0f;
const float MAXIMUM_DELAY_MS = 1000.0 * 20.0f; // stop reflecting after path is this long
const int DEFAULT_DIFFUSION_FANOUT = 2;
const int ABSOLUTE_MAXIMUM_BOUNCE_COUNT = 10;
AudioReflector::AudioReflector(QObject* parent) :
QObject(parent),
_preDelay(DEFAULT_PRE_DELAY),
_soundMsPerMeter(DEFAULT_MS_DELAY_PER_METER),
_distanceAttenuationScalingFactor(DEFAULT_DISTANCE_SCALING_FACTOR)
_distanceAttenuationScalingFactor(DEFAULT_DISTANCE_SCALING_FACTOR),
_diffusionFanout(DEFAULT_DIFFUSION_FANOUT)
{
reset();
}
@ -90,22 +93,27 @@ float getBounceAttenuationCoefficient(int bounceCount) {
}
glm::vec3 getFaceNormal(BoxFace face) {
float surfaceRandomness = randFloatInRange(0.99,1.0);
glm::vec3 slightlyRandomFaceNormal;
float surfaceRandomness = randFloatInRange(0.99f,1.0f);
float surfaceRemainder = (1.0f - surfaceRandomness)/2.0f;
float altRemainderSignA = (randFloatInRange(-1.0f,1.0f) < 0.0f) ? -1.0 : 1.0;
float altRemainderSignB = (randFloatInRange(-1.0f,1.0f) < 0.0f) ? -1.0 : 1.0;
if (face == MIN_X_FACE) {
return glm::vec3(-surfaceRandomness, surfaceRemainder, surfaceRemainder);
slightlyRandomFaceNormal = glm::vec3(-surfaceRandomness, surfaceRemainder * altRemainderSignA, surfaceRemainder * altRemainderSignB);
} else if (face == MAX_X_FACE) {
return glm::vec3(surfaceRandomness, surfaceRemainder, surfaceRemainder);
slightlyRandomFaceNormal = glm::vec3(surfaceRandomness, surfaceRemainder * altRemainderSignA, surfaceRemainder * altRemainderSignB);
} else if (face == MIN_Y_FACE) {
return glm::vec3(surfaceRemainder, -surfaceRandomness, surfaceRemainder);
slightlyRandomFaceNormal = glm::vec3(surfaceRemainder * altRemainderSignA, -surfaceRandomness, surfaceRemainder * altRemainderSignB);
} else if (face == MAX_Y_FACE) {
return glm::vec3(surfaceRemainder, surfaceRandomness, surfaceRemainder);
slightlyRandomFaceNormal = glm::vec3(surfaceRemainder * altRemainderSignA, surfaceRandomness, surfaceRemainder * altRemainderSignB);
} else if (face == MIN_Z_FACE) {
return glm::vec3(surfaceRemainder, surfaceRemainder, -surfaceRandomness);
slightlyRandomFaceNormal = glm::vec3(surfaceRemainder * altRemainderSignA, surfaceRemainder * altRemainderSignB, -surfaceRandomness);
} else if (face == MAX_Z_FACE) {
return glm::vec3(surfaceRemainder, surfaceRemainder, surfaceRandomness);
slightlyRandomFaceNormal = glm::vec3(surfaceRemainder * altRemainderSignA, surfaceRemainder * altRemainderSignB, surfaceRandomness);
}
return glm::vec3(0, 0, 0); //error case
return slightlyRandomFaceNormal;
}
void AudioReflector::reset() {
@ -214,13 +222,96 @@ void AudioReflector::calculateAllReflections() {
quint64 end = usecTimestampNow();
reset();
//qDebug() << "Reflections recalculated in " << (end - start) << "usecs";
}
}
QVector<glm::vec3> AudioReflector::calculateReflections(const glm::vec3& earPosition, const glm::vec3& origin, const glm::vec3& originalDirection) {
// TODO: add diffusion ratio. percentage of echo energy that diffuses
// so say that 50% of the energy that hits the echo point diffuses in fanout directions
void AudioReflector::calculateDiffusions(const glm::vec3& earPosition, const glm::vec3& origin,
const glm::vec3& thisReflection, float thisDistance, float thisAttenuation, int thisBounceCount,
BoxFace thisReflectionFace, QVector<glm::vec3> reflectionPoints) {
//return; // do nothing
QVector<glm::vec3> diffusionDirections;
// diffusions fan out from random places on the semisphere of the collision point
for(int i = 0; i < _diffusionFanout; i++) {
glm::vec3 randomDirection;
float surfaceRandomness = randFloatInRange(0.5f,1.0f);
float surfaceRemainder = (1.0f - surfaceRandomness)/2.0f;
float altRemainderSignA = (randFloatInRange(-1.0f,1.0f) < 0.0f) ? -1.0 : 1.0;
float altRemainderSignB = (randFloatInRange(-1.0f,1.0f) < 0.0f) ? -1.0 : 1.0;
if (thisReflectionFace == MIN_X_FACE) {
randomDirection = glm::vec3(-surfaceRandomness, surfaceRemainder * altRemainderSignA, surfaceRemainder * altRemainderSignB);
} else if (thisReflectionFace == MAX_X_FACE) {
randomDirection = glm::vec3(surfaceRandomness, surfaceRemainder * altRemainderSignA, surfaceRemainder * altRemainderSignB);
} else if (thisReflectionFace == MIN_Y_FACE) {
randomDirection = glm::vec3(surfaceRemainder * altRemainderSignA, -surfaceRandomness, surfaceRemainder * altRemainderSignB);
} else if (thisReflectionFace == MAX_Y_FACE) {
randomDirection = glm::vec3(surfaceRemainder * altRemainderSignA, surfaceRandomness, surfaceRemainder * altRemainderSignB);
} else if (thisReflectionFace == MIN_Z_FACE) {
randomDirection = glm::vec3(surfaceRemainder * altRemainderSignA, surfaceRemainder * altRemainderSignB, -surfaceRandomness);
} else if (thisReflectionFace == MAX_Z_FACE) {
randomDirection = glm::vec3(surfaceRemainder * altRemainderSignA, surfaceRemainder * altRemainderSignB, surfaceRandomness);
}
diffusionDirections.push_back(randomDirection);
}
foreach(glm::vec3 direction, diffusionDirections) {
glm::vec3 start = thisReflection;
OctreeElement* elementHit;
float distance;
BoxFace face;
const float SLIGHTLY_SHORT = 0.999f; // slightly inside the distance so we're on the inside of the reflection point
float currentAttenuation = thisAttenuation;
float totalDistance = thisDistance;
float totalDelay = getDelayFromDistance(totalDistance);
int bounceCount = thisBounceCount;
while (currentAttenuation > MINIMUM_ATTENUATION_TO_REFLECT && totalDelay < MAXIMUM_DELAY_MS && bounceCount < ABSOLUTE_MAXIMUM_BOUNCE_COUNT) {
if (_voxels->findRayIntersection(start, direction, elementHit, distance, face)) {
glm::vec3 end = start + (direction * (distance * SLIGHTLY_SHORT));
totalDistance += glm::distance(start, end);
float earDistance = glm::distance(end, earPosition);
float totalDistanceToEar = earDistance + distance;
totalDelay = getDelayFromDistance(totalDistanceToEar);
currentAttenuation = getDistanceAttenuationCoefficient(totalDistanceToEar) * getBounceAttenuationCoefficient(bounceCount);
if (currentAttenuation > MINIMUM_ATTENUATION_TO_REFLECT && totalDelay < MAXIMUM_DELAY_MS) {
reflectionPoints.push_back(end);
glm::vec3 faceNormal = getFaceNormal(face);
direction = glm::normalize(glm::reflect(direction,faceNormal));
start = end;
bounceCount++;
/*
// handle diffusion here
if (_diffusionFanout > 0 && bounceCount < ABSOLUTE_MAXIMUM_BOUNCE_COUNT) {
glm::vec3 thisReflection = end;
calculateDiffusions(earPosition, origin, end, totalDistance,
currentAttenuation, bounceCount, face, reflectionPoints);
}
*/
}
} else {
currentAttenuation = 0.0f;
}
}
}
}
QVector<glm::vec3> AudioReflector::calculateReflections(const glm::vec3& earPosition,
const glm::vec3& origin, const glm::vec3& originalDirection) {
QVector<glm::vec3> reflectionPoints;
glm::vec3 start = origin;
glm::vec3 direction = originalDirection;
@ -230,23 +321,32 @@ QVector<glm::vec3> AudioReflector::calculateReflections(const glm::vec3& earPosi
const float SLIGHTLY_SHORT = 0.999f; // slightly inside the distance so we're on the inside of the reflection point
float currentAttenuation = 1.0f;
float totalDistance = 0.0f;
float totalDelay = 0.0f;
int bounceCount = 1;
while (currentAttenuation > MINIMUM_ATTENUATION_TO_REFLECT) {
while (currentAttenuation > MINIMUM_ATTENUATION_TO_REFLECT && totalDelay < MAXIMUM_DELAY_MS && bounceCount < ABSOLUTE_MAXIMUM_BOUNCE_COUNT) {
if (_voxels->findRayIntersection(start, direction, elementHit, distance, face)) {
glm::vec3 end = start + (direction * (distance * SLIGHTLY_SHORT));
totalDistance += glm::distance(start, end);
float earDistance = glm::distance(end, earPosition);
float totalDistance = earDistance + distance;
totalDelay = getDelayFromDistance(totalDistance);
currentAttenuation = getDistanceAttenuationCoefficient(totalDistance) * getBounceAttenuationCoefficient(bounceCount);
if (currentAttenuation > MINIMUM_ATTENUATION_TO_REFLECT) {
if (currentAttenuation > MINIMUM_ATTENUATION_TO_REFLECT && totalDelay < MAXIMUM_DELAY_MS) {
reflectionPoints.push_back(end);
glm::vec3 faceNormal = getFaceNormal(face);
direction = glm::normalize(glm::reflect(direction,faceNormal));
start = end;
bounceCount++;
// handle diffusion here
if (_diffusionFanout > 0) {
glm::vec3 thisReflection = end;
calculateDiffusions(earPosition, origin, end, totalDistance,
currentAttenuation, bounceCount, face, reflectionPoints);
}
}
} else {
currentAttenuation = 0.0f;
@ -373,7 +473,11 @@ void AudioReflector::echoReflections(const glm::vec3& origin, const QVector<glm:
}
}
void AudioReflector::processSpatialAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format) {
void AudioReflector::processLocalAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format) {
// nothing yet, but will do local reflections too...
}
void AudioReflector::processInboundAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format) {
//quint64 start = usecTimestampNow();
_maxDelay = 0;

View file

@ -36,7 +36,8 @@ public:
float getMinAttenuation() const { return _minAttenuation; }
float getDelayFromDistance(float distance);
void processSpatialAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
void processInboundAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
void processLocalAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
public slots:
@ -46,8 +47,9 @@ public slots:
void setSoundMsPerMeter(float soundMsPerMeter) { _soundMsPerMeter = soundMsPerMeter; }
float getDistanceAttenuationScalingFactor() const { return _distanceAttenuationScalingFactor; } /// ms per meter, larger means slower
void setDistanceAttenuationScalingFactor(float factor) { _distanceAttenuationScalingFactor = factor; }
int getDiffusionFanout() const { return _diffusionFanout; } /// number of points of diffusion from each reflection point
void setDiffusionFanout(int fanout) { _diffusionFanout = fanout; } /// number of points of diffusion from each reflection point
signals:
@ -63,6 +65,11 @@ private:
unsigned int sampleTime, int sampleRate);
QVector<glm::vec3> calculateReflections(const glm::vec3& earPosition, const glm::vec3& origin, const glm::vec3& originalDirection);
void calculateDiffusions(const glm::vec3& earPosition, const glm::vec3& origin,
const glm::vec3& thisReflection, float thisDistance, float thisAttenuation, int thisBounceCount,
BoxFace thisReflectionFace, QVector<glm::vec3> reflectionPoints);
void drawReflections(const glm::vec3& origin, const glm::vec3& originalColor, const QVector<glm::vec3>& reflections);
void calculateAllReflections();
@ -105,6 +112,8 @@ private:
float _preDelay;
float _soundMsPerMeter;
float _distanceAttenuationScalingFactor;
int _diffusionFanout; // number of points of diffusion from each reflection point
};