// // 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 #include #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 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 _inboundAudioPaths; /// audio paths we're processing for inbound audio QVector _inboundAudiblePoints; /// the audible points that have been calculated from the inbound audio paths QMap _inboundAudioDelays; /// delay times for currently injected audio points QVector _inboundEchoesSuppressed; /// delay times for currently injected audio points int _inboundEchoesCount; int _inboundEchoesSuppressedCount; QVector _localAudioPaths; /// audio paths we're processing for local audio QVector _localAudiblePoints; /// the audible points that have been calculated from the local audio paths QMap _localAudioDelays; /// delay times for currently injected audio points QVector _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