From 688bd0f34f53170a4af6c84e3cfa833971bb7c10 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 5 Nov 2014 16:58:58 -0800 Subject: [PATCH] first pass audio mixer automatic muting of noisy streams, tour guide improvements, new lightExample.js, hair hangs over cone 'body' --- assignment-client/src/audio/AudioMixer.cpp | 16 +++++-- .../resources/describe-settings.json | 8 ++++ examples/guidedTour.js | 11 ++--- examples/lightExample.js | 46 +++++++++++++++++++ interface/src/Audio.cpp | 3 +- interface/src/Hair.cpp | 21 ++++++--- interface/src/entities/EntityTreeRenderer.cpp | 2 +- libraries/audio/src/AudioInjector.cpp | 19 +++++++- libraries/audio/src/AudioInjector.h | 3 ++ .../audio/src/AudioScriptingInterface.cpp | 10 +++- libraries/audio/src/AudioScriptingInterface.h | 1 + libraries/audio/src/PositionalAudioStream.cpp | 7 +-- 12 files changed, 121 insertions(+), 26 deletions(-) create mode 100644 examples/lightExample.js diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 25e21b5d3a..61ad111574 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -61,8 +61,7 @@ const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18; -const float DEFAULT_NOISE_MUTING_THRESHOLD = 100.0f; - +const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f; const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; const QString AUDIO_ENV_GROUP_KEY = "audio_env"; const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; @@ -81,7 +80,6 @@ bool AudioMixer::_enableFilter = true; bool AudioMixer::shouldMute(float quietestFrame, float loudestFrame) { return (quietestFrame > _noiseMutingThreshold); - qDebug() << "Muting, quiestest frame = " << quietestFrame; } AudioMixer::AudioMixer(const QByteArray& packet) : @@ -1015,7 +1013,17 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { qDebug() << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance; } } - + + const QString NOISE_MUTING_THRESHOLD = "noise_muting_threshold"; + if (audioEnvGroupObject[NOISE_MUTING_THRESHOLD].isString()) { + bool ok = false; + float noiseMutingThreshold = audioEnvGroupObject[NOISE_MUTING_THRESHOLD].toString().toFloat(&ok); + if (ok) { + _noiseMutingThreshold = noiseMutingThreshold; + qDebug() << "Noise muting threshold changed to" << _noiseMutingThreshold; + } + } + const QString FILTER_KEY = "enable_filter"; if (audioEnvGroupObject[FILTER_KEY].isBool()) { _enableFilter = audioEnvGroupObject[FILTER_KEY].toBool(); diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 026fe252b2..c044126a80 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -89,6 +89,14 @@ "default": "0.18", "advanced": false }, + { + "name": "noise_muting_threshold", + "label": "Noise Muting Threshold", + "help": "Loudness value for noise background between 0 and 1.0 (0: mute everyone, 1.0: never mute)", + "placeholder": "0.003", + "default": "0.003", + "advanced": false + }, { "name": "enable_filter", "type": "checkbox", diff --git a/examples/guidedTour.js b/examples/guidedTour.js index 1882b527d7..8729850927 100644 --- a/examples/guidedTour.js +++ b/examples/guidedTour.js @@ -12,6 +12,8 @@ var MIN_CHANGE = 2.0; var LANDING_DISTANCE = 2.0; var LANDING_RANDOM = 0.2; +var relativePosition; + function update(deltaTime) { if (Math.random() < deltaTime) { @@ -26,20 +28,15 @@ function update(deltaTime) { } if (guide) { + relativePosition = Vec3.subtract(MyAvatar.position, lastGuidePosition); // Check whether guide has moved, update if so if (Vec3.length(lastGuidePosition) == 0.0) { lastGuidePosition = guide.position; } else { if (Vec3.length(Vec3.subtract(lastGuidePosition, guide.position)) > MIN_CHANGE) { - var meToGuide = Vec3.multiply(Vec3.normalize(Vec3.subtract(guide.position, MyAvatar.position)), LANDING_DISTANCE); - var newPosition = Vec3.subtract(guide.position, meToGuide); - newPosition = Vec3.sum(newPosition, { x: Math.random() * LANDING_RANDOM - LANDING_RANDOM / 2.0, - y: 0, - z: Math.random() * LANDING_RANDOM - LANDING_RANDOM / 2.0 }); + var newPosition = Vec3.sum(guide.position, relativePosition); MyAvatar.position = newPosition; - lastGuidePosition = guide.position; - MyAvatar.orientation = guide.orientation; } } } diff --git a/examples/lightExample.js b/examples/lightExample.js new file mode 100644 index 0000000000..3781a2bc7e --- /dev/null +++ b/examples/lightExample.js @@ -0,0 +1,46 @@ +// +// spotlightExample.js +// examples +// +// Created by Brad Hefta-Gaub on 10/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script that demonstrates creating and editing a particle +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var position = Vec3.sum(MyAvatar.position, Quat.getFront(Camera.getOrientation())); + +var sphereID = Entities.addEntity({ + type: "Sphere", + position: position, + dimensions: { x: 0.1, y: 0.1, z: 0.1 }, + color: { red: 255, green: 255, blue: 0 } + }); + +var lightID = Entities.addEntity({ + type: "Light", + position: position, + dimensions: { x: 1, y: 1, z: 1 }, + angularVelocity: { x: 0, y: 0, z: 0 }, + angularDamping: 0, + + isSpotlight: false, + diffuseColor: { red: 255, green: 255, blue: 0 }, + ambientColor: { red: 0, green: 0, blue: 0 }, + specularColor: { red: 255, green: 255, blue: 255 }, + + constantAttenuation: 0, + linearAttenuation: 1, + quadraticAttenuation: 0, + exponent: 0, + cutoff: 180, // in degrees +}); + +Script.scriptEnding.connect(function() { + print("Deleted sphere and light"); + Entities.deleteEntity(sphereID); + Entities.deleteEntity(lightID); +}); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 0c28f0a943..8c207c3d21 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -728,9 +728,8 @@ void Audio::handleAudioInput() { _loudestFrame = _lastInputLoudness; } - const int FRAMES_FOR_NOISE_DETECTION = 300; + const int FRAMES_FOR_NOISE_DETECTION = 400; if (_inputFrameCounter++ > FRAMES_FOR_NOISE_DETECTION) { - qDebug() << "Quietest/loudest frame: " << _quietestFrame << " / " << _loudestFrame << " NGfloor: " << _noiseGateMeasuredFloor; _quietestFrame = std::numeric_limits::max(); _loudestFrame = 0.0f; _inputFrameCounter = 0; diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index acc00c14b0..637e00a8bc 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -52,12 +52,10 @@ Hair::Hair(int strands, glm::vec3 thisVertex; for (int strand = 0; strand < _strands; strand++) { float strandAngle = randFloat() * PI; - float azimuth; - float elevation = - (randFloat() * PI); - azimuth = PI_OVER_TWO; - if (randFloat() < 0.5f) { - azimuth *= -1.0f; - } + + float azimuth = (float)strand / (float)_strands * PI * 2.0f; + float elevation = 0.0f; + glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); thisStrand *= _radius; @@ -115,11 +113,22 @@ void Hair::simulate(float deltaTime) { glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; _hairPosition[vertexIndex] += diff * HAIR_DAMPING; + /* // Resolve collisions with sphere if (glm::length(_hairPosition[vertexIndex]) < _radius) { _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * (_radius - glm::length(_hairPosition[vertexIndex])); + } */ + + // Collide with a conical body descending from the root of the hair + glm::vec3 thisVertex = _hairPosition[vertexIndex]; + float depth = -thisVertex.y; + thisVertex.y = 0.0f; + const float BODY_CONE_ANGLE = 0.30; + if (glm::length(thisVertex) < depth * BODY_CONE_ANGLE) { + _hairPosition[vertexIndex] += glm::normalize(thisVertex) * (depth * BODY_CONE_ANGLE - glm::length(thisVertex)); } + // Add random thing driven by loudness float loudnessFactor = (_loudness > SOUND_THRESHOLD) ? logf(_loudness - SOUND_THRESHOLD) / 2000.0f : 0.0f; diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index e447c703fb..3b31fef0ee 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -1,4 +1,4 @@ -// + // // EntityTreeRenderer.cpp // interface/src // diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 31c135fd58..fe58de578f 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -26,7 +26,8 @@ AudioInjector::AudioInjector(QObject* parent) : _sound(NULL), _options(), _shouldStop(false), - _currentSendPosition(0) + _currentSendPosition(0), + _loudness(0.0f) { } @@ -34,7 +35,8 @@ AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorO _sound(sound), _options(injectorOptions), _shouldStop(false), - _currentSendPosition(0) + _currentSendPosition(0), + _loudness(0.0f) { } @@ -42,6 +44,10 @@ void AudioInjector::setOptions(AudioInjectorOptions& options) { _options = options; } +float AudioInjector::getLoudness() { + return _loudness; +} + const uchar MAX_INJECTOR_VOLUME = 0xFF; void AudioInjector::injectAudio() { @@ -113,6 +119,15 @@ void AudioInjector::injectAudio() { int bytesToCopy = std::min(((_options.isStereo()) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, soundByteArray.size() - _currentSendPosition); + + // Measure the loudness of this frame + _loudness = 0.0f; + for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { + _loudness += abs(*reinterpret_cast(soundByteArray.data() + _currentSendPosition + i)) / + (MAX_SAMPLE_VALUE / 2.0f); + } + _loudness /= (float)(bytesToCopy / sizeof(int16_t)); + memcpy(injectAudioPacket.data() + positionOptionOffset, &_options.getPosition(), sizeof(_options.getPosition())); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index af9b5e55d1..6465337a47 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -33,6 +33,8 @@ public slots: void stop() { _shouldStop = true; } void setOptions(AudioInjectorOptions& options); void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } + float getLoudness(); + signals: void finished(); private: @@ -40,6 +42,7 @@ private: AudioInjectorOptions _options; bool _shouldStop; int _currentSendPosition; + float _loudness; }; Q_DECLARE_METATYPE(AudioInjector*) diff --git a/libraries/audio/src/AudioScriptingInterface.cpp b/libraries/audio/src/AudioScriptingInterface.cpp index 43c7d35c1d..b1499b2e01 100644 --- a/libraries/audio/src/AudioScriptingInterface.cpp +++ b/libraries/audio/src/AudioScriptingInterface.cpp @@ -45,7 +45,15 @@ bool AudioScriptingInterface::isInjectorPlaying(AudioInjector* injector) { return (injector != NULL); } -void AudioScriptingInterface::startDrumSound(float volume, float frequency, float duration, float decay, +float AudioScriptingInterface::getLoudness(AudioInjector* injector) { + if (injector) { + return injector->getLoudness(); + } else { + return 0.0f; + } +} + +void AudioScriptingInterface::startDrumSound(float volume, float frequency, float duration, float decay, const AudioInjectorOptions* injectorOptions) { Sound* sound = new Sound(volume, frequency, duration, decay); diff --git a/libraries/audio/src/AudioScriptingInterface.h b/libraries/audio/src/AudioScriptingInterface.h index 343eac304c..4f9f87ea21 100644 --- a/libraries/audio/src/AudioScriptingInterface.h +++ b/libraries/audio/src/AudioScriptingInterface.h @@ -23,6 +23,7 @@ public slots: static AudioInjector* playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL); static void stopInjector(AudioInjector* injector); static bool isInjectorPlaying(AudioInjector* injector); + static float getLoudness(AudioInjector* injector); static void startDrumSound(float volume, float frequency, float duration, float decay, const AudioInjectorOptions* injectorOptions = NULL); diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index 49129659b6..8a6bcb629f 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -45,8 +45,9 @@ void PositionalAudioStream::resetStats() { void PositionalAudioStream::updateLastPopOutputLoudnessAndTrailingLoudness() { _lastPopOutputLoudness = _ringBuffer.getFrameLoudness(_lastPopOutput); - const int TRAILING_AVERAGE_FRAMES = 100; - const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; + const int TRAILING_MUTE_THRESHOLD_FRAMES = 400; + const int TRAILING_LOUDNESS_FRAMES = 200; + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_LOUDNESS_FRAMES; const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; const float LOUDNESS_EPSILON = 0.000001f; @@ -59,7 +60,7 @@ void PositionalAudioStream::updateLastPopOutputLoudnessAndTrailingLoudness() { _lastPopOutputTrailingLoudness = 0; } } - if (_frameCounter++ == TRAILING_AVERAGE_FRAMES) { + if (_frameCounter++ == TRAILING_MUTE_THRESHOLD_FRAMES) { _frameCounter = 0; _quietestTrailingFrameLoudness = std::numeric_limits::max(); _loudestTrailingFrameLoudness = 0.0f;