overte-HifiExperiments/interface/src/AudioReflector.cpp
2014-04-04 16:09:25 -07:00

322 lines
14 KiB
C++

//
// AudioReflector.cpp
// interface
//
// Created by Brad Hefta-Gaub on 4/2/2014
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
#include "AudioReflector.h"
void AudioReflector::render() {
if (!_myAvatar) {
return; // exit early if not set up correctly
}
/*
glm::vec3 position = _myAvatar->getHead()->getPosition();
const float radius = 0.25f;
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutWireSphere(radius, 15, 15);
glPopMatrix();
*/
drawRays();
}
// delay = 1ms per foot
// = 3ms per meter
// attenuation =
// BOUNCE_ATTENUATION_FACTOR [0.5] * (1/(1+distance))
int getDelayFromDistance(float distance) {
const int MS_DELAY_PER_METER = 3;
return MS_DELAY_PER_METER * distance;
}
const float BOUNCE_ATTENUATION_FACTOR = 0.125f;
float getDistanceAttenuationCoefficient(float distance) {
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);
float distanceSquareToSource = distance * distance;
// calculate the distance coefficient using the distance to this node
float distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
DISTANCE_SCALE_LOG +
(0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
distanceCoefficient = std::min(1.0f, distanceCoefficient);
return distanceCoefficient;
}
glm::vec3 getFaceNormal(BoxFace face) {
if (face == MIN_X_FACE) {
return glm::vec3(-1, 0, 0);
} else if (face == MAX_X_FACE) {
return glm::vec3(1, 0, 0);
} else if (face == MIN_Y_FACE) {
return glm::vec3(0, -1, 0);
} else if (face == MAX_Y_FACE) {
return glm::vec3(0, 1, 0);
} else if (face == MIN_Z_FACE) {
return glm::vec3(0, 0, -1);
} else if (face == MAX_Z_FACE) {
return glm::vec3(0, 0, 1);
}
return glm::vec3(0, 0, 0); //error case
}
void AudioReflector::drawReflections(const glm::vec3& origin, const glm::vec3& originalDirection,
int bounces, const glm::vec3& originalColor) {
glm::vec3 start = origin;
glm::vec3 direction = originalDirection;
glm::vec3 color = originalColor;
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
const float COLOR_ADJUST_PER_BOUNCE = 0.75f;
for (int i = 0; i < bounces; i++) {
if (_voxels->findRayIntersection(start, direction, elementHit, distance, face)) {
glm::vec3 end = start + (direction * (distance * SLIGHTLY_SHORT));
drawVector(start, end, color);
glm::vec3 faceNormal = getFaceNormal(face);
direction = glm::normalize(glm::reflect(direction,faceNormal));
start = end;
color = color * COLOR_ADJUST_PER_BOUNCE;
}
}
}
// set up our buffers for our attenuated and delayed samples
const int NUMBER_OF_CHANNELS = 2;
void AudioReflector::calculateReflections(const glm::vec3& origin, const glm::vec3& originalDirection,
int bounces, const QByteArray& originalSamples,
unsigned int sampleTime, int sampleRate) {
int samplesTouched = 0;
glm::vec3 rightEarPosition = _myAvatar->getHead()->getRightEarPosition();
glm::vec3 leftEarPosition = _myAvatar->getHead()->getLeftEarPosition();
glm::vec3 start = origin;
glm::vec3 direction = originalDirection;
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
int totalNumberOfSamples = originalSamples.size() / sizeof(int16_t);
int totalNumberOfStereoSamples = originalSamples.size() / (sizeof(int16_t) * NUMBER_OF_CHANNELS);
const int16_t* originalSamplesData = (const int16_t*)originalSamples.constData();
QByteArray attenuatedLeftSamples;
QByteArray attenuatedRightSamples;
attenuatedLeftSamples.resize(originalSamples.size());
attenuatedRightSamples.resize(originalSamples.size());
int16_t* attenuatedLeftSamplesData = (int16_t*)attenuatedLeftSamples.data();
int16_t* attenuatedRightSamplesData = (int16_t*)attenuatedRightSamples.data();
for (int bounceNumber = 1; bounceNumber <= bounces; bounceNumber++) {
if (_voxels->findRayIntersection(start, direction, elementHit, distance, face)) {
glm::vec3 end = start + (direction * (distance * SLIGHTLY_SHORT));
glm::vec3 faceNormal = getFaceNormal(face);
direction = glm::normalize(glm::reflect(direction,faceNormal));
start = end;
// calculate the distance to the ears
float rightEarDistance = glm::distance(end, rightEarPosition);
float leftEarDistance = glm::distance(end, leftEarPosition);
int rightEarDelayMsecs = getDelayFromDistance(rightEarDistance);
int leftEarDelayMsecs = getDelayFromDistance(leftEarDistance);
int rightEarDelay = rightEarDelayMsecs * sampleRate / MSECS_PER_SECOND;
int leftEarDelay = leftEarDelayMsecs * sampleRate / MSECS_PER_SECOND;
float rightEarAttenuation = getDistanceAttenuationCoefficient(rightEarDistance) * (bounceNumber * BOUNCE_ATTENUATION_FACTOR);
float leftEarAttenuation = getDistanceAttenuationCoefficient(leftEarDistance) * (bounceNumber * BOUNCE_ATTENUATION_FACTOR);
//qDebug() << "leftEarAttenuation=" << leftEarAttenuation << "rightEarAttenuation=" << rightEarAttenuation;
// run through the samples, and attenuate them
for (int sample = 0; sample < totalNumberOfStereoSamples; sample++) {
int16_t leftSample = originalSamplesData[sample * NUMBER_OF_CHANNELS];
int16_t rightSample = originalSamplesData[(sample * NUMBER_OF_CHANNELS) + 1];
//qDebug() << "leftSample=" << leftSample << "rightSample=" << rightSample;
attenuatedLeftSamplesData[sample * NUMBER_OF_CHANNELS] = leftSample * leftEarAttenuation;
attenuatedLeftSamplesData[sample * NUMBER_OF_CHANNELS + 1] = 0;
attenuatedRightSamplesData[sample * NUMBER_OF_CHANNELS] = 0;
attenuatedRightSamplesData[sample * NUMBER_OF_CHANNELS + 1] = rightSample * rightEarAttenuation;
//qDebug() << "attenuated... leftSample=" << (leftSample * leftEarAttenuation) << "rightSample=" << (rightSample * rightEarAttenuation);
samplesTouched++;
}
// now inject the attenuated array with the appropriate delay
unsigned int sampleTimeLeft = sampleTime + leftEarDelay;
unsigned int sampleTimeRight = sampleTime + rightEarDelay;
//qDebug() << "sampleTimeLeft=" << sampleTimeLeft << "sampleTimeRight=" << sampleTimeRight;
_audio->addSpatialAudioToBuffer(sampleTimeLeft, attenuatedLeftSamples, totalNumberOfSamples);
_audio->addSpatialAudioToBuffer(sampleTimeRight, attenuatedRightSamples, totalNumberOfSamples);
}
}
}
void AudioReflector::processSpatialAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format) {
//qDebug() << "AudioReflector::processSpatialAudio()...sampleTime=" << sampleTime << " threadID=" << QThread::currentThreadId();
int totalNumberOfSamples = samples.size() / (sizeof(int16_t));
int numFrameSamples = format.sampleRate() * format.channelCount();
qDebug() << " totalNumberOfSamples=" << totalNumberOfSamples;
qDebug() << " numFrameSamples=" << numFrameSamples;
qDebug() << " samples.size()=" << samples.size();
qDebug() << " sizeof(int16_t)=" << sizeof(int16_t);
/*
AudioRingBuffer samplesRingBuffer(totalNumberOfSamples);
qint64 bytesCopied = samplesRingBuffer.writeData(samples.constData(),samples.size());
for(int i = 0; i < totalNumberOfSamples; i++) {
samplesRingBuffer[i] = samplesRingBuffer[i] * 0.25f;
}
qDebug() << " bytesCopied=" << bytesCopied;
_audio->addSpatialAudioToBuffer(sampleTime + 12000, samples, totalNumberOfSamples);
return;
*/
quint64 start = usecTimestampNow();
glm::vec3 origin = _myAvatar->getHead()->getPosition();
glm::quat orientation = _myAvatar->getOrientation(); // _myAvatar->getHead()->getOrientation();
glm::vec3 right = glm::normalize(orientation * IDENTITY_RIGHT);
glm::vec3 up = glm::normalize(orientation * IDENTITY_UP);
glm::vec3 front = glm::normalize(orientation * IDENTITY_FRONT);
glm::vec3 left = -right;
glm::vec3 down = -up;
glm::vec3 back = -front;
glm::vec3 frontRightUp = glm::normalize(front + right + up);
glm::vec3 frontLeftUp = glm::normalize(front + left + up);
glm::vec3 backRightUp = glm::normalize(back + right + up);
glm::vec3 backLeftUp = glm::normalize(back + left + up);
glm::vec3 frontRightDown = glm::normalize(front + right + down);
glm::vec3 frontLeftDown = glm::normalize(front + left + down);
glm::vec3 backRightDown = glm::normalize(back + right + down);
glm::vec3 backLeftDown = glm::normalize(back + left + down);
const int BOUNCE_COUNT = 5;
calculateReflections(origin, frontRightUp, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, frontLeftUp, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, backRightUp, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, backLeftUp, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, frontRightDown, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, frontLeftDown, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, backRightDown, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, backLeftDown, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, front, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, back, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, left, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, right, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, up, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
calculateReflections(origin, down, BOUNCE_COUNT, samples, sampleTime, format.sampleRate());
quint64 end = usecTimestampNow();
//qDebug() << "AudioReflector::addSamples()... samples.size()=" << samples.size() << " elapsed=" << (end - start);
}
void AudioReflector::drawRays() {
glm::vec3 origin = _myAvatar->getHead()->getPosition();
//glm::vec3 origin = _myAvatar->getHead()->getRightEarPosition();
glm::quat orientation = _myAvatar->getOrientation(); // _myAvatar->getHead()->getOrientation();
glm::vec3 right = glm::normalize(orientation * IDENTITY_RIGHT);
glm::vec3 up = glm::normalize(orientation * IDENTITY_UP);
glm::vec3 front = glm::normalize(orientation * IDENTITY_FRONT);
glm::vec3 left = -right;
glm::vec3 down = -up;
glm::vec3 back = -front;
glm::vec3 frontRightUp = glm::normalize(front + right + up);
glm::vec3 frontLeftUp = glm::normalize(front + left + up);
glm::vec3 backRightUp = glm::normalize(back + right + up);
glm::vec3 backLeftUp = glm::normalize(back + left + up);
glm::vec3 frontRightDown = glm::normalize(front + right + down);
glm::vec3 frontLeftDown = glm::normalize(front + left + down);
glm::vec3 backRightDown = glm::normalize(back + right + down);
glm::vec3 backLeftDown = glm::normalize(back + left + down);
const glm::vec3 RED(1,0,0);
const glm::vec3 GREEN(0,1,0);
const glm::vec3 BLUE(0,0,1);
const glm::vec3 PURPLE(1,0,1);
const glm::vec3 YELLOW(1,1,0);
const glm::vec3 CYAN(0,1,1);
const glm::vec3 DARK_RED(0.8f,0.2f,0.2f);
const glm::vec3 DARK_GREEN(0.2f,0.8f,0.2f);
const glm::vec3 DARK_BLUE(0.2f,0.2f,0.8f);
const glm::vec3 DARK_PURPLE(0.8f,0.2f,0.8f);
const glm::vec3 DARK_YELLOW(0.8f,0.8f,0.2f);
const glm::vec3 DARK_CYAN(0.2f,0.8f,0.8f);
const glm::vec3 WHITE(1,1,1);
const glm::vec3 GRAY(0.5f,0.5f,0.5f);
const int BOUNCE_COUNT = 5;
drawReflections(origin, frontRightUp, BOUNCE_COUNT, RED);
drawReflections(origin, frontLeftUp, BOUNCE_COUNT, GREEN);
drawReflections(origin, backRightUp, BOUNCE_COUNT, BLUE);
drawReflections(origin, backLeftUp, BOUNCE_COUNT, CYAN);
drawReflections(origin, frontRightDown, BOUNCE_COUNT, PURPLE);
drawReflections(origin, frontLeftDown, BOUNCE_COUNT, YELLOW);
drawReflections(origin, backRightDown, BOUNCE_COUNT, WHITE);
drawReflections(origin, backLeftDown, BOUNCE_COUNT, DARK_RED);
drawReflections(origin, front, BOUNCE_COUNT, DARK_GREEN);
drawReflections(origin, back, BOUNCE_COUNT, DARK_BLUE);
drawReflections(origin, left, BOUNCE_COUNT, DARK_CYAN);
drawReflections(origin, right, BOUNCE_COUNT, DARK_PURPLE);
drawReflections(origin, up, BOUNCE_COUNT, DARK_YELLOW);
drawReflections(origin, down, BOUNCE_COUNT, GRAY);
}
void AudioReflector::drawVector(const glm::vec3& start, const glm::vec3& end, const glm::vec3& color) {
glDisable(GL_LIGHTING); // ??
glLineWidth(2.0);
// Draw the vector itself
glBegin(GL_LINES);
glColor3f(color.x,color.y,color.z);
glVertex3f(start.x, start.y, start.z);
glVertex3f(end.x, end.y, end.z);
glEnd();
glEnable(GL_LIGHTING); // ??
}