overte/interface/src/AudioReflector.h
2014-04-22 11:27:50 -07:00

254 lines
10 KiB
C++

//
// AudioReflector.h
// interface
//
// Created by Brad Hefta-Gaub on 4/2/2014
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef interface_AudioReflector_h
#define interface_AudioReflector_h
#include <QMutex>
#include <VoxelTree.h>
#include "Audio.h"
#include "avatar/MyAvatar.h"
#include "avatar/AvatarManager.h"
enum AudioSource {
LOCAL_AUDIO,
INBOUND_AUDIO
};
class AudioPath {
public:
AudioPath(AudioSource source = INBOUND_AUDIO, const glm::vec3& origin = glm::vec3(0.0f),
const glm::vec3& direction = glm::vec3(0.0f), float attenuation = 1.0f,
float delay = 0.0f, float distance = 0.0f, bool isDiffusion = false, int bounceCount = 0);
AudioSource source;
bool isDiffusion;
glm::vec3 startPoint;
glm::vec3 startDirection;
float startDelay;
float startAttenuation;
glm::vec3 lastPoint;
glm::vec3 lastDirection;
float lastDistance;
float lastDelay;
float lastAttenuation;
unsigned int bounceCount;
bool finalized;
QVector<glm::vec3> reflections;
};
class AudiblePoint {
public:
glm::vec3 location; /// location of the audible point
float delay; /// includes total delay including pre delay to the point of the audible location, not to the listener's ears
float attenuation; /// only the reflective & diffusive portion of attenuation, doesn't include distance attenuation
float distance; /// includes total distance to the point of the audible location, not to the listener's ears
};
class SurfaceCharacteristics {
public:
float reflectiveRatio;
float absorptionRatio;
float diffusionRatio;
};
class AudioReflector : public QObject {
Q_OBJECT
public:
AudioReflector(QObject* parent = NULL);
// setup functions to configure the resources used by the AudioReflector
void setVoxels(VoxelTree* voxels) { _voxels = voxels; }
void setMyAvatar(MyAvatar* myAvatar) { _myAvatar = myAvatar; }
void setAudio(Audio* audio) { _audio = audio; }
void setAvatarManager(AvatarManager* avatarManager) { _avatarManager = avatarManager; }
void render(); /// must be called in the application render loop
void preProcessOriginalInboundAudio(unsigned int sampleTime, 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:
// statistics
int getReflections() const { return _reflections; }
float getAverageDelayMsecs() const { return _officialAverageDelay; }
float getAverageAttenuation() const { return _officialAverageAttenuation; }
float getMaxDelayMsecs() const { return _officialMaxDelay; }
float getMaxAttenuation() const { return _officialMaxAttenuation; }
float getMinDelayMsecs() const { return _officialMinDelay; }
float getMinAttenuation() const { return _officialMinAttenuation; }
float getDelayFromDistance(float distance);
int getDiffusionPathCount() const { return _diffusionPathCount; }
int getEchoesInjected() const { return _inboundEchoesCount + _localEchoesCount; }
int getEchoesSuppressed() const { return _inboundEchoesSuppressedCount + _localEchoesSuppressedCount; }
/// ms of delay added to all echos
float getPreDelay() const { return _preDelay; }
void setPreDelay(float preDelay) { _preDelay = preDelay; }
/// ms per meter that sound travels, larger means slower, which sounds bigger
float getSoundMsPerMeter() const { return _soundMsPerMeter; }
void setSoundMsPerMeter(float soundMsPerMeter) { _soundMsPerMeter = soundMsPerMeter; }
/// scales attenuation to be louder or softer than the default distance attenuation
float getDistanceAttenuationScalingFactor() const { return _distanceAttenuationScalingFactor; }
void setDistanceAttenuationScalingFactor(float factor) { _distanceAttenuationScalingFactor = factor; }
/// scales attenuation of local audio to be louder or softer than the default attenuation
float getLocalAudioAttenuationFactor() const { return _localAudioAttenuationFactor; }
void setLocalAudioAttenuationFactor(float factor) { _localAudioAttenuationFactor = factor; }
/// ms window in which we will suppress echoes to reduce comb filter effects
float getCombFilterWindow() const { return _combFilterWindow; }
void setCombFilterWindow(float value) { _combFilterWindow = value; }
/// number of points of diffusion from each reflection point, as fanout increases there are more chances for secondary
/// echoes, but each diffusion ray is quieter and therefore more likely to be below the sound floor
int getDiffusionFanout() const { return _diffusionFanout; }
void setDiffusionFanout(int fanout) { _diffusionFanout = fanout; }
/// ratio 0.0 - 1.0 of amount of each ray that is absorbed upon hitting a surface
float getAbsorptionRatio() const { return _absorptionRatio; }
void setAbsorptionRatio(float ratio);
// ratio 0.0 - 1.0 of amount of each ray that is diffused upon hitting a surface
float getDiffusionRatio() const { return _diffusionRatio; }
void setDiffusionRatio(float ratio);
// remaining ratio 0.0 - 1.0 of amount of each ray that is cleanly reflected upon hitting a surface
float getReflectiveRatio() const { return (1.0f - (_absorptionRatio + _diffusionRatio)); }
void setReflectiveRatio(float ratio);
// wet/dry mix - these don't affect any reflection calculations, only the final mix volumes
float getOriginalSourceAttenuation() const { return _originalSourceAttenuation; }
void setOriginalSourceAttenuation(float value) { _originalSourceAttenuation = value; }
float getEchoesAttenuation() const { return _allEchoesAttenuation; }
void setEchoesAttenuation(float value) { _allEchoesAttenuation = value; }
signals:
private:
VoxelTree* _voxels; // used to access voxel scene
MyAvatar* _myAvatar; // access to listener
Audio* _audio; // access to audio API
AvatarManager* _avatarManager; // access to avatar manager API
// Helpers for drawing
void drawVector(const glm::vec3& start, const glm::vec3& end, const glm::vec3& color);
// helper for generically calculating attenuation based on distance
float getDistanceAttenuationCoefficient(float distance);
// statistics
int _reflections;
int _diffusionPathCount;
int _delayCount;
float _totalDelay;
float _averageDelay;
float _maxDelay;
float _minDelay;
float _officialAverageDelay;
float _officialMaxDelay;
float _officialMinDelay;
int _attenuationCount;
float _totalAttenuation;
float _averageAttenuation;
float _maxAttenuation;
float _minAttenuation;
float _officialAverageAttenuation;
float _officialMaxAttenuation;
float _officialMinAttenuation;
glm::vec3 _listenerPosition;
glm::vec3 _origin;
glm::quat _orientation;
QVector<AudioPath*> _inboundAudioPaths; /// audio paths we're processing for inbound audio
QVector<AudiblePoint> _inboundAudiblePoints; /// the audible points that have been calculated from the inbound audio paths
QMap<float, float> _inboundAudioDelays; /// delay times for currently injected audio points
QVector<float> _inboundEchoesSuppressed; /// delay times for currently injected audio points
int _inboundEchoesCount;
int _inboundEchoesSuppressedCount;
QVector<AudioPath*> _localAudioPaths; /// audio paths we're processing for local audio
QVector<AudiblePoint> _localAudiblePoints; /// the audible points that have been calculated from the local audio paths
QMap<float, float> _localAudioDelays; /// delay times for currently injected audio points
QVector<float> _localEchoesSuppressed; /// delay times for currently injected audio points
int _localEchoesCount;
int _localEchoesSuppressedCount;
// adds a sound source to begin an audio path trace, these can be the initial sound sources with their directional properties,
// as well as diffusion sound sources
void addAudioPath(AudioSource source, const glm::vec3& origin, const glm::vec3& initialDirection, float initialAttenuation,
float initialDelay, float initialDistance = 0.0f, bool isDiffusion = false);
// helper that handles audioPath analysis
int analyzePathsSingleStep();
void handlePathPoint(AudioPath* path, float distance, OctreeElement* elementHit, BoxFace face);
void clearPaths();
void analyzePaths();
void drawRays();
void drawPath(AudioPath* path, const glm::vec3& originalColor);
void calculateAllReflections();
int countDiffusionPaths();
glm::vec3 getFaceNormal(BoxFace face);
void identifyAudioSources();
void injectAudiblePoint(AudioSource source, const AudiblePoint& audiblePoint, const QByteArray& samples, unsigned int sampleTime, int sampleRate);
void echoAudio(AudioSource source, unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
// return the surface characteristics of the element we hit
SurfaceCharacteristics getSurfaceCharacteristics(OctreeElement* elementHit = NULL);
QMutex _mutex;
float _preDelay;
float _soundMsPerMeter;
float _distanceAttenuationScalingFactor;
float _localAudioAttenuationFactor;
float _combFilterWindow;
int _diffusionFanout; // number of points of diffusion from each reflection point
// all elements have the same material for now...
float _absorptionRatio;
float _diffusionRatio;
float _reflectiveRatio;
// wet/dry mix - these don't affect any reflection calculations, only the final mix volumes
float _originalSourceAttenuation; /// each sample of original signal will be multiplied by this
float _allEchoesAttenuation; /// each sample of all echo signals will be multiplied by this
// remember the last known values at calculation
bool haveAttributesChanged();
bool _withDiffusion;
float _lastPreDelay;
float _lastSoundMsPerMeter;
float _lastDistanceAttenuationScalingFactor;
float _lastLocalAudioAttenuationFactor;
int _lastDiffusionFanout;
float _lastAbsorptionRatio;
float _lastDiffusionRatio;
bool _lastDontDistanceAttenuate;
bool _lastAlternateDistanceAttenuate;
int _injectedEchoes;
};
#endif // interface_AudioReflector_h