From f10aeaffab496e2b75e6acc2467e70f8583d0678 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 28 Oct 2014 20:22:33 -0700 Subject: [PATCH 1/6] working on audio mixer muting --- assignment-client/src/audio/AudioMixer.cpp | 12 ++++++++++++ assignment-client/src/audio/AudioMixer.h | 3 +++ interface/src/Audio.cpp | 18 ++++++++++++++++++ interface/src/Audio.h | 3 +++ libraries/audio/src/PositionalAudioStream.cpp | 16 +++++++++++++++- libraries/audio/src/PositionalAudioStream.h | 6 ++++++ 6 files changed, 57 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 8d87638434..d581bbc624 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -61,6 +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 QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; const QString AUDIO_ENV_GROUP_KEY = "audio_env"; @@ -78,12 +79,18 @@ bool AudioMixer::_printStreamStats = false; bool AudioMixer::_enableFilter = true; +bool AudioMixer::shouldMute(float quietestFrame, float loudestFrame) { + return (quietestFrame > _noiseMutingThreshold); + qDebug() << "Muting, quiestest frame = " << quietestFrame; +} + AudioMixer::AudioMixer(const QByteArray& packet) : ThreadedAssignment(packet), _trailingSleepRatio(1.0f), _minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f), _performanceThrottlingRatio(0.0f), _attenuationPerDoublingInDistance(DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE), + _noiseMutingThreshold(DEFAULT_NOISE_MUTING_THRESHOLD), _numStatFrames(0), _sumListeners(0), _sumMixes(0), @@ -136,6 +143,11 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l return 0; } + // if the stream should be muted, bail + if (shouldMute(streamToAdd->getQuietestTrailingFrameLoudness(), streamToAdd->getLoudestTrailingFrameLoudness())) { + return 0; + } + float bearingRelativeAngleToSource = 0.0f; float attenuationCoefficient = 1.0f; int numSamplesDelay = 0; diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index ff976dec61..836554c44a 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -59,6 +59,8 @@ private: int16_t _mixSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; void perSecondActions(); + + bool shouldMute(float quietestFrame, float loudestFrame); QString getReadPendingDatagramsCallsPerSecondsStatsString() const; QString getReadPendingDatagramsPacketsPerCallStatsString() const; @@ -71,6 +73,7 @@ private: float _minAudibilityThreshold; float _performanceThrottlingRatio; float _attenuationPerDoublingInDistance; + float _noiseMutingThreshold; int _numStatFrames; int _sumListeners; int _sumMixes; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index dd84eb3211..b8e881bc48 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -77,6 +77,9 @@ Audio::Audio(QObject* parent) : _isStereoInput(false), _averagedLatency(0.0), _lastInputLoudness(0), + _inputFrameCounter(0), + _quietestFrame(std::numeric_limits::max()), + _loudestFrame(0.0f), _timeSinceLastClip(-1.0), _dcOffset(0), _noiseGateMeasuredFloor(0), @@ -717,6 +720,21 @@ void Audio::handleAudioInput() { } _lastInputLoudness = fabs(loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + + if (_quietestFrame > _lastInputLoudness) { + _quietestFrame = _lastInputLoudness; + } + if (_loudestFrame < _lastInputLoudness) { + _loudestFrame = _lastInputLoudness; + } + + const int FRAMES_FOR_NOISE_DETECTION = 300; + if (_inputFrameCounter++ > FRAMES_FOR_NOISE_DETECTION) { + qDebug() << "Quietest/loudest frame: " << _quietestFrame << " / " << _loudestFrame << " NGfloor: " << _noiseGateMeasuredFloor; + _quietestFrame = std::numeric_limits::max(); + _loudestFrame = 0.0f; + _inputFrameCounter = 0; + } // If Noise Gate is enabled, check and turn the gate on and off if (!_audioSourceInjectEnabled && _noiseGateEnabled) { diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 900b6ce0d6..fcbfb0d34a 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -212,6 +212,9 @@ private: QElapsedTimer _timeSinceLastReceived; float _averagedLatency; float _lastInputLoudness; + int _inputFrameCounter; + float _quietestFrame; + float _loudestFrame; float _timeSinceLastClip; float _dcOffset; float _noiseGateMeasuredFloor; diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index 6334ac0efb..49129659b6 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -30,7 +30,10 @@ PositionalAudioStream::PositionalAudioStream(PositionalAudioStream::Type type, b _shouldLoopbackForNode(false), _isStereo(isStereo), _lastPopOutputTrailingLoudness(0.0f), - _lastPopOutputLoudness(0.0f) + _lastPopOutputLoudness(0.0f), + _quietestTrailingFrameLoudness(std::numeric_limits::max()), + _loudestTrailingFrameLoudness(0.0f), + _frameCounter(0) { } @@ -56,6 +59,17 @@ void PositionalAudioStream::updateLastPopOutputLoudnessAndTrailingLoudness() { _lastPopOutputTrailingLoudness = 0; } } + if (_frameCounter++ == TRAILING_AVERAGE_FRAMES) { + _frameCounter = 0; + _quietestTrailingFrameLoudness = std::numeric_limits::max(); + _loudestTrailingFrameLoudness = 0.0f; + } + if (_lastPopOutputLoudness < _quietestTrailingFrameLoudness) { + _quietestTrailingFrameLoudness = _lastPopOutputLoudness; + } + if (_lastPopOutputLoudness > _loudestTrailingFrameLoudness) { + _loudestTrailingFrameLoudness = _lastPopOutputLoudness; + } } int PositionalAudioStream::parsePositionalData(const QByteArray& positionalByteArray) { diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index 6ac189352b..f8028cba3d 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -36,12 +36,15 @@ public: void updateLastPopOutputLoudnessAndTrailingLoudness(); float getLastPopOutputTrailingLoudness() const { return _lastPopOutputTrailingLoudness; } float getLastPopOutputLoudness() const { return _lastPopOutputLoudness; } + float getQuietestTrailingFrameLoudness() const { return _quietestTrailingFrameLoudness; } + float getLoudestTrailingFrameLoudness() const { return _loudestTrailingFrameLoudness; } bool shouldLoopbackForNode() const { return _shouldLoopbackForNode; } bool isStereo() const { return _isStereo; } PositionalAudioStream::Type getType() const { return _type; } const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } + protected: // disallow copying of PositionalAudioStream objects @@ -60,6 +63,9 @@ protected: float _lastPopOutputTrailingLoudness; float _lastPopOutputLoudness; + float _quietestTrailingFrameLoudness; + float _loudestTrailingFrameLoudness; + int _frameCounter; }; #endif // hifi_PositionalAudioStream_h From 53c0c4074a15abc06a7e1dfe442115a05351fdb5 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 29 Oct 2014 12:47:27 -0700 Subject: [PATCH 2/6] see yourself before/after moving --- examples/headMove.js | 61 +++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/examples/headMove.js b/examples/headMove.js index f377409cb8..2c49847864 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -18,10 +18,11 @@ var warpActive = false; var warpPosition = { x: 0, y: 0, z: 0 }; var hipsToEyes; +var restoreCountdownTimer; // Overlays to show target location -var WARP_SPHERE_SIZE = 0.15; +var WARP_SPHERE_SIZE = 0.085; var warpSphere = Overlays.addOverlay("sphere", { position: { x: 0, y: 0, z: 0 }, size: WARP_SPHERE_SIZE, @@ -67,10 +68,7 @@ function activateWarp() { updateWarp(); } -var TRIGGER_PULLBACK_DISTANCE = 0.04; -var WATCH_AVATAR_DISTANCE = 1.5; -var MAX_PULLBACK_YAW = 5.0; -var MAX_PULLBACK_PITCH = 5.0; +var WATCH_AVATAR_DISTANCE = 2.5; var sound = new Sound("http://public.highfidelity.io/sounds/Footsteps/FootstepW2Right-12db.wav"); function playSound() { @@ -80,9 +78,20 @@ function playSound() { options.volume = 1.0; Audio.playSound(sound, options); } + + + +function pullBack() { + saveCameraState(); + cameraPosition = Vec3.subtract(MyAvatar.position, Vec3.multiplyQbyV(Camera.getOrientation(), { x: 0, y: -hipsToEyes, z: -hipsToEyes * WATCH_AVATAR_DISTANCE })); + Camera.setPosition(cameraPosition); + cameraPosition = Camera.getPosition(); + startPullbackPosition = cameraPosition; +} + var WARP_SMOOTHING = 0.90; -var WARP_START_TIME = 0.50; -var WARP_START_DISTANCE = 1.5; +var WARP_START_TIME = 0.25; +var WARP_START_DISTANCE = 2.5; var WARP_SENSITIVITY = 0.15; var fixedHeight = true; @@ -106,18 +115,10 @@ function updateWarp() { warpPosition = Vec3.mix(Vec3.sum(startPosition, Vec3.multiply(warpDirection, distance)), warpPosition, WARP_SMOOTHING); } - var height = MyAvatar.getEyePosition().y - MyAvatar.position.y; var cameraPosition; - if (!watchAvatar && - (Math.abs(deltaYaw) < MAX_PULLBACK_YAW) && - (Math.abs(deltaPitch) < MAX_PULLBACK_PITCH) && - (Vec3.length(deltaPosition) > TRIGGER_PULLBACK_DISTANCE)) { - saveCameraState(); - cameraPosition = Vec3.subtract(MyAvatar.position, Vec3.multiplyQbyV(Camera.getOrientation(), { x: 0, y: -height, z: -height * WATCH_AVATAR_DISTANCE })); - Camera.setPosition(cameraPosition); - cameraPosition = Camera.getPosition(); - startPullbackPosition = cameraPosition; + if (!watchAvatar && willMove) { + pullBack(); watchAvatar = true; } @@ -143,8 +144,15 @@ function finishWarp() { visible: false, }); if (willMove) { - warpPosition.y -= hipsToEyes; + if (fixedHeight) { + warpPosition.y = MyAvatar.position.y; + } else { + warpPosition.y -= hipsToEyes; + } + MyAvatar.position = warpPosition; + cameraPosition = Vec3.subtract(MyAvatar.position, Vec3.multiplyQbyV(Camera.getOrientation(), { x: 0, y: -hipsToEyes, z: -hipsToEyes * WATCH_AVATAR_DISTANCE })); + Camera.setPosition(cameraPosition); playSound(); } } @@ -155,6 +163,14 @@ function update(deltaTime) { keyDownTime += deltaTime; updateWarp(); } + if (restoreCountdownTimer > 0.0) { + restoreCountdownTimer -= deltaTime; + if (restoreCountdownTimer <= 0.0) { + restoreCameraState(); + watchAvatar = false; + restoreCountDownTimer = 0.0; + } + } } Controller.keyPressEvent.connect(function(event) { @@ -173,10 +189,10 @@ Controller.keyPressEvent.connect(function(event) { activateWarp(); } }); - -var TIME_FOR_TURN = 0.25; + var DOUBLE_CLICK_TIME = 0.50; var TURN_AROUND = 180.0; +var RESTORE_TIME = 0.50; Controller.keyReleaseEvent.connect(function(event) { if (event.text == "SPACE" && !event.isAutoRepeat) { @@ -187,7 +203,7 @@ Controller.keyReleaseEvent.connect(function(event) { lastYawTurned = 0.0; MyAvatar.orientation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, TURN_AROUND, 0), MyAvatar.orientation); playSound(); - } else if (keyDownTime < TIME_FOR_TURN) { + } else if (keyDownTime < WARP_START_TIME) { var currentYaw = MyAvatar.getHeadFinalYaw(); lastYawTurned = currentYaw; MyAvatar.orientation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, currentYaw, 0), MyAvatar.orientation); @@ -196,8 +212,7 @@ Controller.keyReleaseEvent.connect(function(event) { timeSinceLastUp = 0.0; finishWarp(); if (watchAvatar) { - restoreCameraState(); - watchAvatar = false; + restoreCountdownTimer = RESTORE_TIME; } } }); From 688bd0f34f53170a4af6c84e3cfa833971bb7c10 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 5 Nov 2014 16:58:58 -0800 Subject: [PATCH 3/6] 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; From 0b205f3f1619d91a6ead9e98d6db01dc8014f1fb Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 5 Nov 2014 17:06:00 -0800 Subject: [PATCH 4/6] fix bad spaces --- interface/src/entities/EntityTreeRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 3b31fef0ee..e447c703fb 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -1,4 +1,4 @@ - // +// // EntityTreeRenderer.cpp // interface/src // From 291767fd9e3dc93e6b35db5f710f3df827bd970c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 5 Nov 2014 17:09:26 -0800 Subject: [PATCH 5/6] updated header --- examples/lightExample.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/lightExample.js b/examples/lightExample.js index 3781a2bc7e..7a90eb8714 100644 --- a/examples/lightExample.js +++ b/examples/lightExample.js @@ -1,11 +1,11 @@ // -// spotlightExample.js +// lightExample.js // examples // -// Created by Brad Hefta-Gaub on 10/28/14. +// Created by Philip Rosedale on November 5, 2014 // Copyright 2014 High Fidelity, Inc. // -// This is an example script that demonstrates creating and editing a particle +// Makes a light right in front of your avatar, as well as a sphere at that location. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html From b103c62df99fb45f0f9891a0d076cc0bed12815b Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 6 Nov 2014 08:30:33 -0800 Subject: [PATCH 6/6] add new birdSongs.js --- examples/birdSongs.js | 164 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 examples/birdSongs.js diff --git a/examples/birdSongs.js b/examples/birdSongs.js new file mode 100644 index 0000000000..94e013d92b --- /dev/null +++ b/examples/birdSongs.js @@ -0,0 +1,164 @@ +// +// birdSongs.js +// examples +// +// Copyright 2014 High Fidelity, Inc. +// Plays a sample audio file at the avatar's current location +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +// First, load a sample sound from a URL +var birds = []; +var playing = []; + +var lowerCorner = { x: 0, y: 8, z: 0 }; +var upperCorner = { x: 10, y: 10, z: 10 }; + +var RATE = 0.035; +var numPlaying = 0; +var BIRD_SIZE = 0.1; +var BIRD_VELOCITY = 2.0; +var LIGHT_RADIUS = 10.0; +var BIRD_MASTER_VOLUME = 0.5; + +var useLights = true; + +function randomVector(scale) { + return { x: Math.random() * scale - scale / 2.0, y: Math.random() * scale - scale / 2.0, z: Math.random() * scale - scale / 2.0 }; +} + +function maybePlaySound(deltaTime) { + if (Math.random() < RATE) { + // Set the location and other info for the sound to play + var whichBird = Math.floor(Math.random() * birds.length); + //print("playing sound # " + whichBird); + var options = new AudioInjectionOptions(); + var position = { x: lowerCorner.x + Math.random() * (upperCorner.x - lowerCorner.x), + y: lowerCorner.y + Math.random() * (upperCorner.y - lowerCorner.y), + z: lowerCorner.z + Math.random() * (upperCorner.z - lowerCorner.z) }; + options.position = position; + options.volume = BIRD_MASTER_VOLUME; + // + var entityId = Entities.addEntity({ + type: "Sphere", + position: position, + dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }, + color: birds[whichBird].color, + lifetime: 10 + }); + + if (useLights) { + var lightId = Entities.addEntity({ + type: "Light", + position: position, + dimensions: { x: LIGHT_RADIUS, y: LIGHT_RADIUS, z: LIGHT_RADIUS }, + + isSpotlight: false, + diffuseColor: birds[whichBird].color, + ambientColor: { red: 0, green: 0, blue: 0 }, + specularColor: { red: 255, green: 255, blue: 255 }, + + constantAttenuation: 0, + linearAttenuation: 4.0, + quadraticAttenuation: 2.0, + lifetime: 10 + }); + } + + playing.push({ audioId: Audio.playSound(birds[whichBird].sound, options), entityId: entityId, lightId: lightId, color: birds[whichBird].color }); + } + if (playing.length != numPlaying) { + numPlaying = playing.length; + //print("number playing = " + numPlaying); + } + for (var i = 0; i < playing.length; i++) { + if (!Audio.isInjectorPlaying(playing[i].audioId)) { + Entities.deleteEntity(playing[i].entityId); + if (useLights) { + Entities.deleteEntity(playing[i].lightId); + } + playing.splice(i, 1); + } else { + var loudness = Audio.getLoudness(playing[i].audioId); + var newColor = { red: playing[i].color.red, green: playing[i].color.green, blue: playing[i].color.blue }; + if (loudness > 0.05) { + newColor.red *= (1.0 - loudness); + newColor.green *= (1.0 - loudness); + newColor.blue *= (1.0 - loudness); + } + var properties = Entities.getEntityProperties(playing[i].entityId); + var newPosition = Vec3.sum(properties.position, randomVector(BIRD_VELOCITY * deltaTime)); + if (properties) { + properties.position = newPosition; + Entities.editEntity(playing[i].entityId, { position: properties.position, color: newColor }); + } + if (useLights) { + var lightProperties = Entities.getEntityProperties(playing[i].lightId); + if (lightProperties) { + Entities.editEntity(playing[i].lightId, { position: newPosition, diffuseColor: newColor }); + } + } + } + } +} + +loadBirds(); +// Connect a call back that happens every frame +Script.update.connect(maybePlaySound); + +// Delete our little friends if script is stopped +Script.scriptEnding.connect(function() { + for (var i = 0; i < playing.length; i++) { + Entities.deleteEntity(playing[i].entityId); + if (useLights) { + Entities.deleteEntity(playing[i].lightId); + } + } +}); + +function loadBirds() { + var sound_filenames = ["bushtit_1.raw", "bushtit_2.raw", "bushtit_3.raw", "mexicanWhipoorwill.raw", + "rosyfacedlovebird.raw", "saysphoebe.raw", "westernscreechowl.raw", "bandtailedpigeon.wav", "bridledtitmouse.wav", + "browncrestedflycatcher.wav", "commonnighthawk.wav", "commonpoorwill.wav", "doublecrestedcormorant.wav", + "gambelsquail.wav", "goldcrownedkinglet.wav", "greaterroadrunner.wav","groovebilledani.wav","hairywoodpecker.wav", + "housewren.wav","hummingbird.wav", "mountainchickadee.wav", "nightjar.wav", "piebilledgrieb.wav", "pygmynuthatch.wav", + "whistlingduck.wav", "woodpecker.wav"]; + + var colors = [ + { red: 242, green: 207, blue: 013 }, + { red: 238, green: 94, blue: 11 }, + { red: 81, green: 30, blue: 7 }, + { red: 195, green: 176, blue: 81 }, + { red: 235, green: 190, blue: 152 }, + { red: 167, green: 99, blue: 52 }, + { red: 199, green: 122, blue: 108 }, + { red: 246, green: 220, blue: 189 }, + { red: 208, green: 145, blue: 65 }, + { red: 173, green: 120 , blue: 71 }, + { red: 132, green: 147, blue: 174 }, + { red: 164, green: 74, blue: 40 }, + { red: 131, green: 127, blue: 134 }, + { red: 209, green: 157, blue: 117 }, + { red: 205, green: 191, blue: 193 }, + { red: 193, green: 154, blue: 118 }, + { red: 205, green: 190, blue: 169 }, + { red: 199, green: 111, blue: 69 }, + { red: 221, green: 223, blue: 228 }, + { red: 115, green: 92, blue: 87 }, + { red: 214, green: 165, blue: 137 }, + { red: 160, green: 124, blue: 33 }, + { red: 117, green: 91, blue: 86 }, + { red: 113, green: 104, blue: 107 }, + { red: 216, green: 153, blue: 99 }, + { red: 242, green: 226, blue: 64 } + ]; + + var SOUND_BASE_URL = "http://public.highfidelity.io/sounds/Animals/"; + + for (var i = 0; i < sound_filenames.length; i++) { + birds.push({ sound: new Sound(SOUND_BASE_URL + sound_filenames[i]), + color: colors[i] + } ); + } +} \ No newline at end of file