From 7b485829c446e4d0b643e78b9c2002f1afc0845c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 15:52:00 -0800 Subject: [PATCH 01/38] cleanup AudioInjector memory management --- interface/src/Application.cpp | 3 - libraries/audio/src/AudioInjector.cpp | 17 +++++ libraries/audio/src/AudioInjector.h | 2 + .../src/AudioScriptingInterface.cpp | 67 +++++-------------- .../src/AudioScriptingInterface.h | 31 ++++----- libraries/script-engine/src/ScriptEngine.h | 2 +- 6 files changed, 49 insertions(+), 73 deletions(-) rename libraries/{audio => script-engine}/src/AudioScriptingInterface.cpp (54%) rename libraries/{audio => script-engine}/src/AudioScriptingInterface.h (60%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aa24711509..69d43eac1e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -542,9 +542,6 @@ Application::~Application() { _nodeThread->quit(); _nodeThread->wait(); - // kill any audio injectors that are still around - AudioScriptingInterface::getInstance().stopAllInjectors(); - auto audioIO = DependencyManager::get(); // stop the audio process diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 5568a7e372..aa4b99c30d 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include @@ -22,6 +23,9 @@ #include "AudioInjector.h" QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in) { + // when the script goes down we want to cleanup the injector + QObject::connect(engine, &QScriptEngine::destroyed, in, &AudioInjector::stopAndDeleteLater); + return engine->newQObject(in); } @@ -236,6 +240,14 @@ void AudioInjector::injectToMixer() { // send two packets before the first sleep so the mixer can start playback right away if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) { + + // process events in case we have been told to stop and be deleted + QCoreApplication::processEvents(); + + if (_shouldStop) { + break; + } + // not the first packet and not done // sleep for the appropriate time int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; @@ -264,3 +276,8 @@ void AudioInjector::stop() { emit finished(); } } + +void AudioInjector::stopAndDeleteLater() { + stop(); + QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); +} diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 257b538c11..05850c4f47 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -13,6 +13,7 @@ #define hifi_AudioInjector_h #include +#include #include #include @@ -42,6 +43,7 @@ public: public slots: void injectAudio(); void stop(); + void stopAndDeleteLater(); void setOptions(AudioInjectorOptions& options); void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } float getLoudness(); diff --git a/libraries/audio/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp similarity index 54% rename from libraries/audio/src/AudioScriptingInterface.cpp rename to libraries/script-engine/src/AudioScriptingInterface.cpp index 58b72f3fcd..6182e6926d 100644 --- a/libraries/audio/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -28,80 +28,47 @@ AudioScriptingInterface::AudioScriptingInterface() : } -void AudioScriptingInterface::stopAllInjectors() { - QList >::iterator injector = _activeInjectors.begin(); - while (injector != _activeInjectors.end()) { - if (!injector->isNull()) { - injector->data()->stop(); - - while (injector->data() && !injector->data()->isFinished()) { - // wait for this injector to go down - } - } - - injector = _activeInjectors.erase(injector); - } +AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { + AudioInjector* injector = NULL; + QMetaObject::invokeMethod(this, "invokedPlaySound", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(AudioInjector*, injector), + Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions)); + return injector; } -AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { +AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions) { if (sound) { // stereo option isn't set from script, this comes from sound metadata or filename AudioInjectorOptions optionsCopy = injectorOptions; optionsCopy.stereo = sound->isStereo(); - AudioInjector* injector = new AudioInjector(sound, optionsCopy); - injector->setLocalAudioInterface(_localAudioInterface); - QThread* injectorThread = new QThread(); injectorThread->setObjectName("Audio Injector Thread"); + AudioInjector* injector = new AudioInjector(sound, optionsCopy); + injector->setLocalAudioInterface(_localAudioInterface); + injector->moveToThread(injectorThread); // start injecting when the injector thread starts connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); - // connect the right slots and signals so that the AudioInjector is killed once the injection is complete - connect(injector, &AudioInjector::finished, injector, &AudioInjector::deleteLater); - connect(injector, &AudioInjector::finished, injectorThread, &QThread::quit); - connect(injector, &AudioInjector::finished, this, &AudioScriptingInterface::injectorStopped); + + // connect the right slots and signals for AudioInjector and thread cleanup + connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); + connect(injectorThread, &QThread::destroyed, this, &AudioScriptingInterface::threadDead); injectorThread->start(); - _activeInjectors.append(QPointer(injector)); - return injector; + } else { qDebug() << "AudioScriptingInterface::playSound called with null Sound object."; return NULL; } } -void AudioScriptingInterface::stopInjector(AudioInjector* injector) { - if (injector) { - injector->stop(); - } -} - -bool AudioScriptingInterface::isInjectorPlaying(AudioInjector* injector) { - return (injector != NULL); -} - -void AudioScriptingInterface::setInjectorOptions(AudioInjector* injector, const AudioInjectorOptions& injectorOptions) { - AudioInjectorOptions optionsCopy = injectorOptions; - if (injector) { - injector->setOptions(optionsCopy); - } -} - -float AudioScriptingInterface::getLoudness(AudioInjector* injector) { - if (injector) { - return injector->getLoudness(); - } else { - return 0.0f; - } -} - -void AudioScriptingInterface::injectorStopped() { - _activeInjectors.removeAll(QPointer(reinterpret_cast(sender()))); +void AudioScriptingInterface::threadDead() { + qDebug() << "The audio thread has gone down"; } diff --git a/libraries/audio/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h similarity index 60% rename from libraries/audio/src/AudioScriptingInterface.h rename to libraries/script-engine/src/AudioScriptingInterface.h index b437286ecf..8cbb515d7f 100644 --- a/libraries/audio/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -12,40 +12,33 @@ #ifndef hifi_AudioScriptingInterface_h #define hifi_AudioScriptingInterface_h -#include - -#include "AbstractAudioInterface.h" -#include "AudioInjector.h" -#include "Sound.h" +#include +#include +#include class AudioScriptingInterface : public QObject { Q_OBJECT public: static AudioScriptingInterface& getInstance(); - void stopAllInjectors(); - void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } -public slots: - - static float getLoudness(AudioInjector* injector); - - AudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); - void stopInjector(AudioInjector* injector); - bool isInjectorPlaying(AudioInjector* injector); - - void setInjectorOptions(AudioInjector* injector, const AudioInjectorOptions& injectorOptions); - - void injectorStopped(); +protected: + // this method is protected to stop C++ callers from calling, but invokable from script + Q_INVOKABLE AudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); signals: void mutedByMixer(); void environmentMuted(); +public slots: + void threadDead(); + +private slots: + AudioInjector* invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions); + private: AudioScriptingInterface(); - QList< QPointer > _activeInjectors; AbstractAudioInterface* _localAudioInterface; }; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index f78a14bffa..6cc9f64d4c 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -19,13 +19,13 @@ #include #include -#include #include #include #include #include "AbstractControllerScriptingInterface.h" #include "ArrayBufferClass.h" +#include "AudioScriptingInterface.h" #include "Quat.h" #include "ScriptUUID.h" #include "Vec3.h" From 0498c5c708238ad0f74667646d02b3ef40ed3cfd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 15:57:32 -0800 Subject: [PATCH 02/38] provide old ASI APIs in AudioInjector --- libraries/audio/src/AudioInjector.cpp | 53 +++++++++++---------------- libraries/audio/src/AudioInjector.h | 22 +++++++---- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index aa4b99c30d..445bf0770b 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -34,35 +34,20 @@ void injectorFromScriptValue(const QScriptValue& object, AudioInjector*& out) { } AudioInjector::AudioInjector(QObject* parent) : - QObject(parent), - _options(), - _shouldStop(false), - _loudness(0.0f), - _isFinished(false), - _currentSendPosition(0), - _localBuffer(NULL) + QObject(parent) { + } AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) : _audioData(sound->getByteArray()), - _options(injectorOptions), - _shouldStop(false), - _loudness(0.0f), - _isFinished(false), - _currentSendPosition(0), - _localBuffer(NULL) + _options(injectorOptions) { } AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions) : _audioData(audioData), - _options(injectorOptions), - _shouldStop(false), - _loudness(0.0f), - _isFinished(false), - _currentSendPosition(0), - _localBuffer(NULL) + _options(injectorOptions) { } @@ -83,21 +68,25 @@ float AudioInjector::getLoudness() { void AudioInjector::injectAudio() { - // check if we need to offset the sound by some number of seconds - if (_options.secondOffset > 0.0f) { + if (!_isStarted) { + // check if we need to offset the sound by some number of seconds + if (_options.secondOffset > 0.0f) { + + // convert the offset into a number of bytes + int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); + byteOffset *= sizeof(int16_t); + + _currentSendPosition = byteOffset; + } - // convert the offset into a number of bytes - int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); - byteOffset *= sizeof(int16_t); - - _currentSendPosition = byteOffset; - } - - if (_options.localOnly) { - injectLocally(); + if (_options.localOnly) { + injectLocally(); + } else { + injectToMixer(); + } } else { - injectToMixer(); - } + qDebug() << "AudioInjector::injectAudio called but already started."; + } } void AudioInjector::injectLocally() { diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 05850c4f47..7f414effae 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -43,25 +43,31 @@ public: public slots: void injectAudio(); void stop(); - void stopAndDeleteLater(); void setOptions(AudioInjectorOptions& options); void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } - float getLoudness(); + float getLoudness() const; + bool isPlaying() const { return !_isFinished; } signals: void finished(); + +private slots: + void stopAndDeleteLater(); private: void injectToMixer(); void injectLocally(); QByteArray _audioData; AudioInjectorOptions _options; - bool _shouldStop; - float _loudness; - bool _isFinished; - int _currentSendPosition; - AbstractAudioInterface* _localAudioInterface; - AudioInjectorLocalBuffer* _localBuffer; + bool _shouldStop = false; + float _loudness = 0.0f; + bool _isStarted = false; + bool _isFinished = false; + int _currentSendPosition = 0; + AbstractAudioInterface* _localAudioInterface = NULL; + AudioInjectorLocalBuffer* _localBuffer = NULL; + + friend QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in); }; Q_DECLARE_METATYPE(AudioInjector*) From 4894a5b4141a351bf09b07bc349c0bbdb2b617dd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 16:11:55 -0800 Subject: [PATCH 03/38] better handling of cleanup for local injector --- libraries/audio-client/src/AudioClient.cpp | 3 -- libraries/audio/src/AudioInjector.cpp | 29 +++++++++---------- libraries/audio/src/AudioInjector.h | 8 +++-- .../src/AudioScriptingInterface.cpp | 5 ---- .../src/AudioScriptingInterface.h | 3 -- 5 files changed, 19 insertions(+), 29 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 7f6c509626..3d43cb3cf8 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -980,9 +980,6 @@ bool AudioClient::outputLocalInjector(bool isStereo, qreal volume, AudioInjector // move the localOutput to the same thread as the local injector buffer localOutput->moveToThread(injector->getLocalBuffer()->thread()); - // have it be cleaned up when that injector is done - connect(injector, &AudioInjector::finished, localOutput, &QAudioOutput::stop); - qDebug() << "Starting QAudioOutput for local injector" << localOutput; localOutput->start(injector->getLocalBuffer()); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 445bf0770b..eea61db1fb 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -52,20 +52,21 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt } -AudioInjector::~AudioInjector() { - if (_localBuffer) { - _localBuffer->stop(); +void AudioInjector::setIsFinished(bool isFinished) { + _isFinished = isFinished; + + if (_isFinished) { + emit finished(); + + if (_localBuffer) { + _localBuffer->stop(); + } + + // cleanup the audio data so we aren't holding that memory unecessarily + _audioData.resize(0); } } -void AudioInjector::setOptions(AudioInjectorOptions& options) { - _options = options; -} - -float AudioInjector::getLoudness() { - return _loudness; -} - void AudioInjector::injectAudio() { if (!_isStarted) { @@ -252,8 +253,7 @@ void AudioInjector::injectToMixer() { } } - _isFinished = true; - emit finished(); + setIsFinished(true); } void AudioInjector::stop() { @@ -261,8 +261,7 @@ void AudioInjector::stop() { if (_options.localOnly) { // we're only a local injector, so we can say we are finished right away too - _isFinished = true; - emit finished(); + setIsFinished(true); } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 7f414effae..4252eaf2ef 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -31,9 +31,9 @@ public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions); - ~AudioInjector(); bool isFinished() const { return _isFinished; } + int getCurrentSendPosition() const { return _currentSendPosition; } AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; } @@ -43,9 +43,9 @@ public: public slots: void injectAudio(); void stop(); - void setOptions(AudioInjectorOptions& options); + void setOptions(AudioInjectorOptions& options) { _options = options; } void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } - float getLoudness() const; + float getLoudness() const { return _loudness; } bool isPlaying() const { return !_isFinished; } signals: @@ -57,6 +57,8 @@ private: void injectToMixer(); void injectLocally(); + void setIsFinished(bool isFinished); + QByteArray _audioData; AudioInjectorOptions _options; bool _shouldStop = false; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 6182e6926d..3deceffb09 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -57,7 +57,6 @@ AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const Aud // connect the right slots and signals for AudioInjector and thread cleanup connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); - connect(injectorThread, &QThread::destroyed, this, &AudioScriptingInterface::threadDead); injectorThread->start(); @@ -68,7 +67,3 @@ AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const Aud return NULL; } } - -void AudioScriptingInterface::threadDead() { - qDebug() << "The audio thread has gone down"; -} diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 8cbb515d7f..59d610b4e3 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -31,9 +31,6 @@ signals: void mutedByMixer(); void environmentMuted(); -public slots: - void threadDead(); - private slots: AudioInjector* invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions); From 6a5c0fd9f270dcd9b185a10021d27d16246140f0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 16:16:01 -0800 Subject: [PATCH 04/38] repairs for new injector API in lobby script --- examples/lobby.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/lobby.js b/examples/lobby.js index 1936a4e531..9c253ffaf7 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -227,10 +227,10 @@ function cleanupLobby() { panelWall = false; orbShell = false; - Audio.stopInjector(currentDrone); + currentDrone.stop(); currentDrone = null; - Audio.stopInjector(currentMuzakInjector); + currentMuzakInjector.stop(); currentMuzakInjector = null; places = {}; @@ -354,7 +354,7 @@ function update(deltaTime) { Overlays.editOverlay(descriptionText, { position: textOverlayPosition() }); // if the reticle is up then we may need to play the next muzak - if (currentMuzakInjector && !Audio.isInjectorPlaying(currentMuzakInjector)) { + if (currentMuzakInjector && !currentMuzakInjector.isPlaying()) { playNextMuzak(); } } From d8646f6ea05a23fd09796f0943582d9781b88811 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 16:17:10 -0800 Subject: [PATCH 05/38] repairs for new injector API in air guitar script --- examples/controllers/hydra/airGuitar.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/controllers/hydra/airGuitar.js b/examples/controllers/hydra/airGuitar.js index 29ab2f9c44..6f5a259ecd 100644 --- a/examples/controllers/hydra/airGuitar.js +++ b/examples/controllers/hydra/airGuitar.js @@ -85,10 +85,10 @@ function checkHands(deltaTime) { var chord = Controller.getTriggerValue(chordHand); if (volume > 1.0) volume = 1.0; - if ((chord > 0.1) && Audio.isInjectorPlaying(soundPlaying)) { + if ((chord > 0.1) && soundPlaying.isPlaying()) { // If chord finger trigger pulled, stop current chord print("stopped sound"); - Audio.stopInjector(soundPlaying); + soundPlaying.stop(); } var BUTTON_COUNT = 6; @@ -132,9 +132,9 @@ function checkHands(deltaTime) { } function playChord(position, volume) { - if (Audio.isInjectorPlaying(soundPlaying)) { + if (soundPlaying.isPlaying()) { print("stopped sound"); - Audio.stopInjector(soundPlaying); + soundPlaying.stop(); } print("Played sound: " + whichChord + " at volume " + options.volume); From f9174366dc5840e3a825e3d2ce65bf8a3cce1e16 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 16:20:48 -0800 Subject: [PATCH 06/38] fixes for new injector API in other scripts --- examples/entityScripts/movable.js | 2 +- examples/example/audio/birdSongs.js | 4 ++-- examples/utilities/diagnostics/inWorldTestTone.js | 4 ++-- examples/utilities/diagnostics/orbitingSound.js | 6 +++--- examples/utilities/diagnostics/playSoundLoop.js | 4 ++-- libraries/audio/src/AudioInjector.h | 2 ++ 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/entityScripts/movable.js b/examples/entityScripts/movable.js index 21e6261179..8b51ee2b90 100644 --- a/examples/entityScripts/movable.js +++ b/examples/entityScripts/movable.js @@ -117,7 +117,7 @@ print("stopSound()"); } if (this.injector) { - Audio.stopInjector(this.injector); + this.injector.stop(); this.injector = null; } } diff --git a/examples/example/audio/birdSongs.js b/examples/example/audio/birdSongs.js index 267fa20b49..876f942dbc 100644 --- a/examples/example/audio/birdSongs.js +++ b/examples/example/audio/birdSongs.js @@ -75,14 +75,14 @@ function maybePlaySound(deltaTime) { //print("number playing = " + numPlaying); } for (var i = 0; i < playing.length; i++) { - if (!Audio.isInjectorPlaying(playing[i].audioId)) { + if (!playing[i].audioId.isPlaying) { 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 loudness = playing[i].audioId.loudness; 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); diff --git a/examples/utilities/diagnostics/inWorldTestTone.js b/examples/utilities/diagnostics/inWorldTestTone.js index 77ec7ba3b2..ffafd62f97 100644 --- a/examples/utilities/diagnostics/inWorldTestTone.js +++ b/examples/utilities/diagnostics/inWorldTestTone.js @@ -21,7 +21,7 @@ var offset = Vec3.normalize(Quat.getFront(MyAvatar.orientation)); var position = Vec3.sum(MyAvatar.position, offset); function update(deltaTime) { - if (!Audio.isInjectorPlaying(soundPlaying)) { + if (!soundPlaying.isPlaying) { soundPlaying = Audio.playSound(sound, { position: position, loop: true @@ -31,7 +31,7 @@ function update(deltaTime) { } function scriptEnding() { - if (Audio.isInjectorPlaying(soundPlaying)) { + if (soundPlaying.isPlaying) { Audio.stopInjector(soundPlaying); print("Stopped sound loop"); } diff --git a/examples/utilities/diagnostics/orbitingSound.js b/examples/utilities/diagnostics/orbitingSound.js index 54e319faaa..3ff73e9d35 100644 --- a/examples/utilities/diagnostics/orbitingSound.js +++ b/examples/utilities/diagnostics/orbitingSound.js @@ -32,14 +32,14 @@ var sound = Audio.playSound(soundClip, { position: orbitCenter, loop: true, volu function update(deltaTime) { time += deltaTime; currentPosition = { x: orbitCenter.x + Math.cos(time * SPEED) * RADIUS, y: orbitCenter.y, z: orbitCenter.z + Math.sin(time * SPEED) * RADIUS }; - trailingLoudness = 0.9 * trailingLoudness + 0.1 * Audio.getLoudness(sound); + trailingLoudness = 0.9 * trailingLoudness + 0.1 * sound.loudness; Entities.editEntity( objectId, { position: currentPosition, color: { red: Math.min(trailingLoudness * 2000, 255), green: 0, blue: 0 } } ); - Audio.setInjectorOptions(sound, { position: currentPosition }); + sound.setOptions({ position: currentPosition }); } Script.scriptEnding.connect(function() { Entities.deleteEntity(objectId); - Audio.stopInjector(sound); + sound.stop(); }); Script.update.connect(update); \ No newline at end of file diff --git a/examples/utilities/diagnostics/playSoundLoop.js b/examples/utilities/diagnostics/playSoundLoop.js index faf23761b4..f54ed88e28 100644 --- a/examples/utilities/diagnostics/playSoundLoop.js +++ b/examples/utilities/diagnostics/playSoundLoop.js @@ -45,8 +45,8 @@ function maybePlaySound(deltaTime) { } function scriptEnding() { - if (Audio.isInjectorPlaying(soundPlaying)) { - Audio.stopInjector(soundPlaying); + if (soundPlaying.isPlaying) { + soundPlaying.stop(); Entities.deleteEntity(ball); print("Stopped sound."); } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 4252eaf2ef..d01294dc36 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -27,6 +27,8 @@ class AbstractAudioInterface; class AudioInjector : public QObject { Q_OBJECT + Q_PROPERTY(bool isPlaying READ isPlaying) + Q_PROPERTY(float loudness READ getLoudness) public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); From 05b7cd819e4cb03445ed0629b0a71a50ccb82818 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 16:21:45 -0800 Subject: [PATCH 07/38] use isPlaying property instead of getter --- examples/controllers/hydra/airGuitar.js | 4 ++-- examples/lobby.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/controllers/hydra/airGuitar.js b/examples/controllers/hydra/airGuitar.js index 6f5a259ecd..8f16bc0e23 100644 --- a/examples/controllers/hydra/airGuitar.js +++ b/examples/controllers/hydra/airGuitar.js @@ -85,7 +85,7 @@ function checkHands(deltaTime) { var chord = Controller.getTriggerValue(chordHand); if (volume > 1.0) volume = 1.0; - if ((chord > 0.1) && soundPlaying.isPlaying()) { + if ((chord > 0.1) && soundPlaying.isPlaying) { // If chord finger trigger pulled, stop current chord print("stopped sound"); soundPlaying.stop(); @@ -132,7 +132,7 @@ function checkHands(deltaTime) { } function playChord(position, volume) { - if (soundPlaying.isPlaying()) { + if (soundPlaying.isPlaying) { print("stopped sound"); soundPlaying.stop(); } diff --git a/examples/lobby.js b/examples/lobby.js index 9c253ffaf7..ded287cc10 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -354,7 +354,7 @@ function update(deltaTime) { Overlays.editOverlay(descriptionText, { position: textOverlayPosition() }); // if the reticle is up then we may need to play the next muzak - if (currentMuzakInjector && !currentMuzakInjector.isPlaying()) { + if (currentMuzakInjector && !currentMuzakInjector.isPlaying) { playNextMuzak(); } } From e3c4ead32323619d3a5682224bb5caf93fbca4f9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 16:23:57 -0800 Subject: [PATCH 08/38] remove unecessary injector stops --- examples/acScripts/ambiance.js | 6 ------ examples/example/audio/radio.js | 3 --- examples/utilities/diagnostics/inWorldTestTone.js | 7 ------- examples/utilities/diagnostics/playSoundLoop.js | 8 +++----- 4 files changed, 3 insertions(+), 21 deletions(-) diff --git a/examples/acScripts/ambiance.js b/examples/acScripts/ambiance.js index fcc3ba0d80..f4f023a473 100644 --- a/examples/acScripts/ambiance.js +++ b/examples/acScripts/ambiance.js @@ -32,10 +32,4 @@ Script.update.connect(function() { injector = Audio.playSound(sound, audioOptions); print("Playing: " + injector); } -}); - -Script.scriptEnding.connect(function() { - if (injector !== null) { - injector.stop(); - } }); \ No newline at end of file diff --git a/examples/example/audio/radio.js b/examples/example/audio/radio.js index 7ac33675a1..5a8b4cbb88 100644 --- a/examples/example/audio/radio.js +++ b/examples/example/audio/radio.js @@ -76,9 +76,6 @@ function scriptEnding() { if (entity != null) { Entities.deleteEntity(entity); } - if (injector != null) { - injector.stop(); - } } Script.update.connect(update); diff --git a/examples/utilities/diagnostics/inWorldTestTone.js b/examples/utilities/diagnostics/inWorldTestTone.js index ffafd62f97..0229654f15 100644 --- a/examples/utilities/diagnostics/inWorldTestTone.js +++ b/examples/utilities/diagnostics/inWorldTestTone.js @@ -30,13 +30,6 @@ function update(deltaTime) { } } -function scriptEnding() { - if (soundPlaying.isPlaying) { - Audio.stopInjector(soundPlaying); - print("Stopped sound loop"); - } -} - Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); diff --git a/examples/utilities/diagnostics/playSoundLoop.js b/examples/utilities/diagnostics/playSoundLoop.js index f54ed88e28..d6a99ceea9 100644 --- a/examples/utilities/diagnostics/playSoundLoop.js +++ b/examples/utilities/diagnostics/playSoundLoop.js @@ -45,11 +45,9 @@ function maybePlaySound(deltaTime) { } function scriptEnding() { - if (soundPlaying.isPlaying) { - soundPlaying.stop(); - Entities.deleteEntity(ball); - print("Stopped sound."); - } + if (ball) { + Entities.deleteEntity(ball); + } } // Connect a call back that happens every frame From 449719bc7cedd7a819160f70b1c1ec84afa05f62 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 16:58:30 -0800 Subject: [PATCH 09/38] add option to restart a local audio injector --- libraries/audio-client/src/AudioClient.cpp | 3 +++ libraries/audio/src/AudioInjector.cpp | 14 ++++++++++++-- libraries/audio/src/AudioInjector.h | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 3d43cb3cf8..7f6c509626 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -980,6 +980,9 @@ bool AudioClient::outputLocalInjector(bool isStereo, qreal volume, AudioInjector // move the localOutput to the same thread as the local injector buffer localOutput->moveToThread(injector->getLocalBuffer()->thread()); + // have it be cleaned up when that injector is done + connect(injector, &AudioInjector::finished, localOutput, &QAudioOutput::stop); + qDebug() << "Starting QAudioOutput for local injector" << localOutput; localOutput->start(injector->getLocalBuffer()); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index eea61db1fb..7ef00f6091 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -60,16 +60,19 @@ void AudioInjector::setIsFinished(bool isFinished) { if (_localBuffer) { _localBuffer->stop(); + _localBuffer->deleteLater(); + _localBuffer = NULL; } - // cleanup the audio data so we aren't holding that memory unecessarily - _audioData.resize(0); + _isStarted = false; + _shouldStop = false; } } void AudioInjector::injectAudio() { if (!_isStarted) { + // check if we need to offset the sound by some number of seconds if (_options.secondOffset > 0.0f) { @@ -78,6 +81,8 @@ void AudioInjector::injectAudio() { byteOffset *= sizeof(int16_t); _currentSendPosition = byteOffset; + } else { + _currentSendPosition = 0; } if (_options.localOnly) { @@ -90,6 +95,11 @@ void AudioInjector::injectAudio() { } } +void AudioInjector::restart() { + stop(); + QMetaObject::invokeMethod(this, "injectAudio", Qt::QueuedConnection); +} + void AudioInjector::injectLocally() { bool success = false; if (_localAudioInterface) { diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index d01294dc36..61e42d8a11 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -25,6 +25,9 @@ class AbstractAudioInterface; +// In order to make scripting cleaner for the AudioInjector, the script now holds on to the AudioInjector object +// until it dies. + class AudioInjector : public QObject { Q_OBJECT Q_PROPERTY(bool isPlaying READ isPlaying) @@ -44,6 +47,7 @@ public: void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; } public slots: void injectAudio(); + void restart(); void stop(); void setOptions(AudioInjectorOptions& options) { _options = options; } void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } From 79674fb94f975fcd289e691c6b4f0b8804f1e767 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 17:20:00 -0800 Subject: [PATCH 10/38] use a wrapper on audio injector for script ownership --- libraries/audio/src/AudioInjector.cpp | 11 ---- libraries/audio/src/AudioInjector.h | 12 +---- .../src/AudioScriptingInterface.cpp | 13 ++++- .../src/AudioScriptingInterface.h | 4 +- .../script-engine/src/ScriptAudioInjector.cpp | 36 +++++++++++++ .../script-engine/src/ScriptAudioInjector.h | 52 +++++++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 2 +- 7 files changed, 105 insertions(+), 25 deletions(-) create mode 100644 libraries/script-engine/src/ScriptAudioInjector.cpp create mode 100644 libraries/script-engine/src/ScriptAudioInjector.h diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 7ef00f6091..ddd96563a5 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -22,17 +22,6 @@ #include "AudioInjector.h" -QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in) { - // when the script goes down we want to cleanup the injector - QObject::connect(engine, &QScriptEngine::destroyed, in, &AudioInjector::stopAndDeleteLater); - - return engine->newQObject(in); -} - -void injectorFromScriptValue(const QScriptValue& object, AudioInjector*& out) { - out = qobject_cast(object.toQObject()); -} - AudioInjector::AudioInjector(QObject* parent) : QObject(parent) { diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 61e42d8a11..2380df7e73 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -30,8 +30,6 @@ class AbstractAudioInterface; class AudioInjector : public QObject { Q_OBJECT - Q_PROPERTY(bool isPlaying READ isPlaying) - Q_PROPERTY(float loudness READ getLoudness) public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); @@ -49,6 +47,8 @@ public slots: void injectAudio(); void restart(); void stop(); + void stopAndDeleteLater(); + void setOptions(AudioInjectorOptions& options) { _options = options; } void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } float getLoudness() const { return _loudness; } @@ -57,8 +57,6 @@ public slots: signals: void finished(); -private slots: - void stopAndDeleteLater(); private: void injectToMixer(); void injectLocally(); @@ -74,13 +72,7 @@ private: int _currentSendPosition = 0; AbstractAudioInterface* _localAudioInterface = NULL; AudioInjectorLocalBuffer* _localBuffer = NULL; - - friend QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in); }; -Q_DECLARE_METATYPE(AudioInjector*) - -QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in); -void injectorFromScriptValue(const QScriptValue& object, AudioInjector*& out); #endif // hifi_AudioInjector_h diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 3deceffb09..ea994ea32f 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ScriptAudioInjector.h" + #include "AudioScriptingInterface.h" void registerAudioMetaTypes(QScriptEngine* engine) { @@ -28,12 +30,16 @@ AudioScriptingInterface::AudioScriptingInterface() : } -AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { +ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { AudioInjector* injector = NULL; QMetaObject::invokeMethod(this, "invokedPlaySound", Qt::BlockingQueuedConnection, Q_RETURN_ARG(AudioInjector*, injector), Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions)); - return injector; + if (injector) { + return new ScriptAudioInjector(injector); + } else { + return NULL; + } } AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions) { @@ -43,6 +49,7 @@ AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const Aud optionsCopy.stereo = sound->isStereo(); QThread* injectorThread = new QThread(); + qDebug() << "the injector thread is" << injectorThread; injectorThread->setObjectName("Audio Injector Thread"); AudioInjector* injector = new AudioInjector(sound, optionsCopy); @@ -50,6 +57,8 @@ AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const Aud injector->moveToThread(injectorThread); + qDebug() << "the injector is" << injector; + // start injecting when the injector thread starts connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 59d610b4e3..ed52d951ad 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -16,6 +16,8 @@ #include #include +class ScriptAudioInjector; + class AudioScriptingInterface : public QObject { Q_OBJECT public: @@ -25,7 +27,7 @@ public: protected: // this method is protected to stop C++ callers from calling, but invokable from script - Q_INVOKABLE AudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); + Q_INVOKABLE ScriptAudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); signals: void mutedByMixer(); diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp new file mode 100644 index 0000000000..422697441b --- /dev/null +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -0,0 +1,36 @@ +// +// ScriptAudioInjector.cpp +// libraries/script-engine/src +// +// Created by Stephen Birarda on 2015-02-11. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScriptAudioInjector.h" + +QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in) { + // when the script goes down we want to cleanup the injector + QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately); + + return engine->newQObject(in, QScriptEngine::ScriptOwnership); +} + +void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& out) { + out = qobject_cast(object.toQObject()); +} + +ScriptAudioInjector::ScriptAudioInjector(AudioInjector* injector) : + _injector(injector) +{ + +} + +ScriptAudioInjector::~ScriptAudioInjector() { +} + +void ScriptAudioInjector::stopInjectorImmediately() { + _injector->stopAndDeleteLater(); +} diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h new file mode 100644 index 0000000000..407bcef788 --- /dev/null +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -0,0 +1,52 @@ +// +// ScriptAudioInjector.h +// libraries/script-engine/src +// +// Created by Stephen Birarda on 2015-02-11. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptAudioInjector_h +#define hifi_ScriptAudioInjector_h + +#include + +#include + +class ScriptAudioInjector : public QObject { + Q_OBJECT + + Q_PROPERTY(bool isPlaying READ isPlaying) + Q_PROPERTY(float loudness READ getLoudness) +public: + ScriptAudioInjector(AudioInjector* injector); + ~ScriptAudioInjector(); +public slots: + void restart() { _injector->restart(); } + void stop() { _injector->stop(); } + + void setOptions(AudioInjectorOptions& options) { _injector->setOptions(options); } + + float getLoudness() const { return _injector->getLoudness(); } + bool isPlaying() const { return _injector->isPlaying(); } + +signals: + void finished(); + +protected slots: + void stopInjectorImmediately(); +private: + AudioInjector* _injector; + + friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in); +}; + +Q_DECLARE_METATYPE(ScriptAudioInjector*) + +QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in); +void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& out); + +#endif // hifi_ScriptAudioInjector_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 353e215561..062bb0ca06 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -35,6 +34,7 @@ #include "DataViewClass.h" #include "EventTypes.h" #include "MenuItemProperties.h" +#include "ScriptAudioInjector.h" #include "ScriptEngine.h" #include "TypedArrays.h" #include "XMLHttpRequestClass.h" From 43e777163ea0a5c3b9b5810f63fccfaeee876d74 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Feb 2015 19:15:33 -0800 Subject: [PATCH 11/38] use a QPointer to trigger delayed delete --- libraries/audio/src/AudioInjector.cpp | 6 ++++++ libraries/audio/src/AudioInjector.h | 3 +++ libraries/script-engine/src/ScriptAudioInjector.cpp | 5 +++++ libraries/script-engine/src/ScriptAudioInjector.h | 2 +- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index ddd96563a5..31258a28dc 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -55,6 +55,12 @@ void AudioInjector::setIsFinished(bool isFinished) { _isStarted = false; _shouldStop = false; + + if (_shouldDeleteAfterFinish) { + // we've been asked to delete after finishing, trigger a queued deleteLater here + qDebug() << "triggering the delete since we are finished"; + QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); + } } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 2380df7e73..25d0c1699d 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -46,7 +46,9 @@ public: public slots: void injectAudio(); void restart(); + void stop(); + void triggerDeleteAfterFinish() { _shouldDeleteAfterFinish = true; } void stopAndDeleteLater(); void setOptions(AudioInjectorOptions& options) { _options = options; } @@ -69,6 +71,7 @@ private: float _loudness = 0.0f; bool _isStarted = false; bool _isFinished = false; + bool _shouldDeleteAfterFinish = false; int _currentSendPosition = 0; AbstractAudioInterface* _localAudioInterface = NULL; AudioInjectorLocalBuffer* _localBuffer = NULL; diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp index 422697441b..7b78ed3cc3 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.cpp +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -29,6 +29,11 @@ ScriptAudioInjector::ScriptAudioInjector(AudioInjector* injector) : } ScriptAudioInjector::~ScriptAudioInjector() { + if (!_injector.isNull()) { + // we've been asked to delete after finishing, trigger a queued deleteLater here + QMetaObject::invokeMethod(_injector.data(), "triggerDeleteAfterFinish", Qt::QueuedConnection); + } + } void ScriptAudioInjector::stopInjectorImmediately() { diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h index 407bcef788..b89a274af1 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.h +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -39,7 +39,7 @@ signals: protected slots: void stopInjectorImmediately(); private: - AudioInjector* _injector; + QPointer _injector; friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in); }; From ea52cea5dd280a91d65c972a99e714a9ff03c483 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 10:56:40 -0800 Subject: [PATCH 12/38] more cleanup for AudioInjector mem management --- examples/lobby.js | 30 ++++++++++++++++--- libraries/audio-client/src/AudioClient.cpp | 7 +++-- libraries/audio/src/AudioInjector.cpp | 2 ++ .../src/AudioScriptingInterface.cpp | 4 --- .../script-engine/src/ScriptAudioInjector.cpp | 3 +- 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/examples/lobby.js b/examples/lobby.js index ded287cc10..6e85ef501b 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -55,7 +55,9 @@ var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.st var currentDrone = null; var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.stereo.raw") +var latinInjector = null; var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw") +var elevatorInjector = null; var currentMuzakInjector = null; var currentSound = null; @@ -140,7 +142,11 @@ function drawLobby() { if (droneSound.downloaded) { // start the drone sound - currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true, volume: DRONE_VOLUME }); + if (!currentDrone) { + currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true, volume: DRONE_VOLUME }); + } else { + currentDrone.restart(); + } } // start one of our muzak sounds @@ -203,7 +209,24 @@ function playRandomMuzak() { if (currentSound) { // pick a random number of seconds from 0-10 to offset the muzak var secondOffset = Math.random() * 10; - currentMuzakInjector = Audio.playSound(currentSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); + + if (currentSound == latinSound) { + if (!latinInjector) { + latinInjector = Audio.playSound(latinSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); + } else { + latinInjector.restart(); + } + + currentMuzakInjector = latinInjector; + } else if (currentSound == elevatorSound) { + if (!elevatorInjector) { + elevatorInjector = Audio.playSound(elevatorSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); + } else { + elevatorInjector.restart(); + } + + currentMuzakInjector = elevatorInjector; + } } else { currentMuzakInjector = null; } @@ -228,9 +251,8 @@ function cleanupLobby() { orbShell = false; currentDrone.stop(); - currentDrone = null; - currentMuzakInjector.stop(); + currentMuzakInjector = null; places = {}; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 7f6c509626..a16b0e1779 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -974,14 +974,15 @@ bool AudioClient::outputLocalInjector(bool isStereo, qreal volume, AudioInjector QAudioOutput* localOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName), localFormat, - injector); + injector->getLocalBuffer()); + localOutput->setVolume(volume); // move the localOutput to the same thread as the local injector buffer localOutput->moveToThread(injector->getLocalBuffer()->thread()); - // have it be cleaned up when that injector is done - connect(injector, &AudioInjector::finished, localOutput, &QAudioOutput::stop); + // have it be stopped when that local buffer is about to close + connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop); qDebug() << "Starting QAudioOutput for local injector" << localOutput; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 31258a28dc..b90aefb47b 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -91,6 +91,7 @@ void AudioInjector::injectAudio() { } void AudioInjector::restart() { + qDebug() << "Restarting an AudioInjector by stopping and starting over."; stop(); QMetaObject::invokeMethod(this, "injectAudio", Qt::QueuedConnection); } @@ -101,6 +102,7 @@ void AudioInjector::injectLocally() { if (_audioData.size() > 0) { _localBuffer = new AudioInjectorLocalBuffer(_audioData, this); + _localBuffer->open(QIODevice::ReadOnly); _localBuffer->setShouldLoop(_options.loop); diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index ea994ea32f..32b9eb23e2 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -49,7 +49,6 @@ AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const Aud optionsCopy.stereo = sound->isStereo(); QThread* injectorThread = new QThread(); - qDebug() << "the injector thread is" << injectorThread; injectorThread->setObjectName("Audio Injector Thread"); AudioInjector* injector = new AudioInjector(sound, optionsCopy); @@ -57,12 +56,9 @@ AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const Aud injector->moveToThread(injectorThread); - qDebug() << "the injector is" << injector; - // start injecting when the injector thread starts connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); - // connect the right slots and signals for AudioInjector and thread cleanup connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp index 7b78ed3cc3..beb056468f 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.cpp +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -31,9 +31,8 @@ ScriptAudioInjector::ScriptAudioInjector(AudioInjector* injector) : ScriptAudioInjector::~ScriptAudioInjector() { if (!_injector.isNull()) { // we've been asked to delete after finishing, trigger a queued deleteLater here - QMetaObject::invokeMethod(_injector.data(), "triggerDeleteAfterFinish", Qt::QueuedConnection); + _injector->triggerDeleteAfterFinish(); } - } void ScriptAudioInjector::stopInjectorImmediately() { From 060b0bbfd606b6f9ff3c6974b8f64cda3f523337 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 11:13:03 -0800 Subject: [PATCH 13/38] tell injector it is not finished once restarted --- libraries/audio/src/AudioInjector.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index b90aefb47b..1e66ad77cf 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -65,9 +65,7 @@ void AudioInjector::setIsFinished(bool isFinished) { } void AudioInjector::injectAudio() { - if (!_isStarted) { - // check if we need to offset the sound by some number of seconds if (_options.secondOffset > 0.0f) { @@ -93,6 +91,7 @@ void AudioInjector::injectAudio() { void AudioInjector::restart() { qDebug() << "Restarting an AudioInjector by stopping and starting over."; stop(); + setIsFinished(false); QMetaObject::invokeMethod(this, "injectAudio", Qt::QueuedConnection); } From 5715642e04e19be1c8690085a0588a98f8f0960b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 11:13:30 -0800 Subject: [PATCH 14/38] fix for restart API in lobby script --- examples/lobby.js | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/examples/lobby.js b/examples/lobby.js index 6e85ef501b..5d687dc07a 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -179,6 +179,26 @@ function changeLobbyTextures() { var MUZAK_VOLUME = 0.1; +function playCurrentSound(secondOffset) { + if (currentSound == latinSound) { + if (!latinInjector) { + latinInjector = Audio.playSound(latinSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); + } else { + latinInjector.restart(); + } + + currentMuzakInjector = latinInjector; + } else if (currentSound == elevatorSound) { + if (!elevatorInjector) { + elevatorInjector = Audio.playSound(elevatorSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); + } else { + elevatorInjector.restart(); + } + + currentMuzakInjector = elevatorInjector; + } +} + function playNextMuzak() { if (panelWall) { if (currentSound == latinSound) { @@ -190,8 +210,8 @@ function playNextMuzak() { currentSound = latinSound; } } - - currentMuzakInjector = Audio.playSound(currentSound, { localOnly: true, volume: MUZAK_VOLUME }); + + playCurrentSound(0); } } @@ -206,27 +226,11 @@ function playRandomMuzak() { currentSound = elevatorSound; } - if (currentSound) { + if (currentSound) { // pick a random number of seconds from 0-10 to offset the muzak var secondOffset = Math.random() * 10; - if (currentSound == latinSound) { - if (!latinInjector) { - latinInjector = Audio.playSound(latinSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); - } else { - latinInjector.restart(); - } - - currentMuzakInjector = latinInjector; - } else if (currentSound == elevatorSound) { - if (!elevatorInjector) { - elevatorInjector = Audio.playSound(elevatorSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); - } else { - elevatorInjector.restart(); - } - - currentMuzakInjector = elevatorInjector; - } + playCurrentSound(secondOffset); } else { currentMuzakInjector = null; } From 5f4e3528c2d8236a8abb2f800c43d200052034a4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 11:29:55 -0800 Subject: [PATCH 15/38] override seek to handle warning once stopped --- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/audio/src/AudioInjectorLocalBuffer.cpp | 9 +++++++++ libraries/audio/src/AudioInjectorLocalBuffer.h | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index a16b0e1779..1919f2bbe5 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -982,6 +982,7 @@ bool AudioClient::outputLocalInjector(bool isStereo, qreal volume, AudioInjector localOutput->moveToThread(injector->getLocalBuffer()->thread()); // have it be stopped when that local buffer is about to close + connect(injector->getLocalBuffer(), &AudioInjectorLocalBuffer::bufferEmpty, localOutput, &QAudioOutput::stop); connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop); qDebug() << "Starting QAudioOutput for local injector" << localOutput; @@ -993,7 +994,6 @@ bool AudioClient::outputLocalInjector(bool isStereo, qreal volume, AudioInjector return false; } - void AudioClient::outputFormatChanged() { int outputFormatChannelCountTimesSampleRate = _outputFormat.channelCount() * _outputFormat.sampleRate(); _outputFrameSize = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * outputFormatChannelCountTimesSampleRate / _desiredOutputFormat.sampleRate(); diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.cpp b/libraries/audio/src/AudioInjectorLocalBuffer.cpp index a58d686498..6cbe56adb7 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.cpp +++ b/libraries/audio/src/AudioInjectorLocalBuffer.cpp @@ -23,9 +23,18 @@ AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArr void AudioInjectorLocalBuffer::stop() { _isStopped = true; + QIODevice::close(); } +bool AudioInjectorLocalBuffer::seek(qint64 pos) { + if (_isStopped) { + return false; + } else { + return QIODevice::seek(pos); + } +} + qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { if (!_isStopped) { diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 399c7515ec..ce41052730 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -21,6 +21,8 @@ public: void stop(); + bool seek(qint64 pos); + qint64 readData(char* data, qint64 maxSize); qint64 writeData(const char* data, qint64 maxSize) { return 0; } From 70e085009b59b3c72fb18535cd55d269d69bd8de Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 11:44:14 -0800 Subject: [PATCH 16/38] use injector restart in airGuitar script --- examples/controllers/hydra/airGuitar.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/controllers/hydra/airGuitar.js b/examples/controllers/hydra/airGuitar.js index 8f16bc0e23..750bfb5bc7 100644 --- a/examples/controllers/hydra/airGuitar.js +++ b/examples/controllers/hydra/airGuitar.js @@ -138,10 +138,15 @@ function playChord(position, volume) { } print("Played sound: " + whichChord + " at volume " + options.volume); - soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], { - position: position, - volume: volume - }); + if (!soundPlaying) { + soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], { + position: position, + volume: volume + }); + } else { + soundPlaying.restart(); + } + } function keyPressEvent(event) { From eda7d8ffb1dfe90214670f99b09e8f32b692dde2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 11:49:01 -0800 Subject: [PATCH 17/38] use new injector APIs in movable script --- examples/entityScripts/movable.js | 32 +++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/examples/entityScripts/movable.js b/examples/entityScripts/movable.js index 8b51ee2b90..066f258f2e 100644 --- a/examples/entityScripts/movable.js +++ b/examples/entityScripts/movable.js @@ -48,7 +48,8 @@ this.turnSounds = new Array(); this.moveSound = null; this.turnSound = null; - this.injector = null; + this.moveInjector = null; + this.turnInjector = null; var debug = false; var displayRotateTargets = true; // change to false if you don't want the rotate targets @@ -92,9 +93,14 @@ } if (this.moveSound && this.moveSound.downloaded) { if (debug) { - print("playMoveSound() --- calling this.injector = Audio.playSound(this.moveSound...)"); + print("playMoveSound() --- calling this.moveInjector = Audio.playSound(this.moveSound...)"); + } + + if (!this.moveInjector) { + this.moveInjector = Audio.playSound(this.moveSound, { position: this.properties.position, loop: true, volume: 0.1 }); + } else { + this.moveInjector.restart(); } - this.injector = Audio.playSound(this.moveSound, { position: this.properties.position, loop: true, volume: 0.1 }); } } @@ -105,9 +111,13 @@ } if (this.turnSound && this.turnSound.downloaded) { if (debug) { - print("playTurnSound() --- calling this.injector = Audio.playSound(this.turnSound...)"); + print("playTurnSound() --- calling this.turnInjector = Audio.playSound(this.turnSound...)"); + } + if (!this.turnInjector) { + this.turnInjector = Audio.playSound(this.turnSound, { position: this.properties.position, loop: true, volume: 0.1 }); + } else { + this.turnInjector.restart(); } - this.injector = Audio.playSound(this.turnSound, { position: this.properties.position, loop: true, volume: 0.1 }); } } @@ -116,9 +126,11 @@ if (debug) { print("stopSound()"); } - if (this.injector) { - this.injector.stop(); - this.injector = null; + if (this.turnInjector) { + this.turnInjector.stop(); + } + if (this.moveInjector) { + this.moveInjector.stop(); } } @@ -174,7 +186,7 @@ this.move = function(mouseEvent) { this.updatePosition(mouseEvent); - if (this.injector === null) { + if (this.moveInjector === null || !this.moveInjector.isPlaying) { this.playMoveSound(); } }; @@ -233,7 +245,7 @@ } } - if (this.injector === null) { + if (this.turnInjector === null || !this.turnInjector.isPlaying) { this.playTurnSound(); } }; From 1120a5f963d1d2c896de59c3f6790fab2536ee98 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 11:50:48 -0800 Subject: [PATCH 18/38] use new injector API in inWorldTestTone --- examples/utilities/diagnostics/inWorldTestTone.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/utilities/diagnostics/inWorldTestTone.js b/examples/utilities/diagnostics/inWorldTestTone.js index 0229654f15..4596bfe2ad 100644 --- a/examples/utilities/diagnostics/inWorldTestTone.js +++ b/examples/utilities/diagnostics/inWorldTestTone.js @@ -21,13 +21,13 @@ var offset = Vec3.normalize(Quat.getFront(MyAvatar.orientation)); var position = Vec3.sum(MyAvatar.position, offset); function update(deltaTime) { - if (!soundPlaying.isPlaying) { - soundPlaying = Audio.playSound(sound, { - position: position, - loop: true - }); - print("Started sound loop"); - } + if (sound.downloaded && !soundPlaying) { + print("Started sound loop"); + soundPlaying = Audio.playSound(sound, { + position: position, + loop: true + }); + } } Script.update.connect(update); From 0154ac9734b7bdeb5ed81de94e55ac28f5323576 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 11:52:20 -0800 Subject: [PATCH 19/38] final script repairs for new AudioInjector API --- examples/utilities/diagnostics/orbitingSound.js | 1 - examples/utilities/diagnostics/playSoundLoop.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/utilities/diagnostics/orbitingSound.js b/examples/utilities/diagnostics/orbitingSound.js index 3ff73e9d35..1af6fab827 100644 --- a/examples/utilities/diagnostics/orbitingSound.js +++ b/examples/utilities/diagnostics/orbitingSound.js @@ -39,7 +39,6 @@ function update(deltaTime) { Script.scriptEnding.connect(function() { Entities.deleteEntity(objectId); - sound.stop(); }); Script.update.connect(update); \ No newline at end of file diff --git a/examples/utilities/diagnostics/playSoundLoop.js b/examples/utilities/diagnostics/playSoundLoop.js index d6a99ceea9..83c0ccf6d0 100644 --- a/examples/utilities/diagnostics/playSoundLoop.js +++ b/examples/utilities/diagnostics/playSoundLoop.js @@ -30,7 +30,7 @@ var playing = false; var ball = false; function maybePlaySound(deltaTime) { - if (sound.downloaded) { + if (sound.downloaded && !soundPlaying) { var properties = { type: "Sphere", position: options.position, From ef9cc2aacce6e1d38f58b3fe63af514611f99f60 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 12:49:29 -0800 Subject: [PATCH 20/38] repair ability for desktop to add gvr target --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7803259c2..04a5f3ee9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,8 @@ if (NOT ANDROID) add_subdirectory(interface) add_subdirectory(tests) add_subdirectory(tools) -elseif (ANDROID OR DESKTOP_GVR) +endif () + +if (ANDROID OR DESKTOP_GVR) add_subdirectory(gvr-interface) endif () \ No newline at end of file From 51ce8c7100486db53bb57654706d44db465eb2e5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 12 Feb 2015 13:09:37 -0800 Subject: [PATCH 21/38] Update editEntities to not select new entity when edit tools are off --- examples/editEntities.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index f25a873992..6938fc86b7 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -533,7 +533,9 @@ function highlightEntityUnderCursor(position, accurateRay) { function mouseReleaseEvent(event) { if (placingEntityID) { - selectionManager.setSelections([placingEntityID]); + if (isActive) { + selectionManager.setSelections([placingEntityID]); + } placingEntityID = null; } if (isActive && selectionManager.hasSelection()) { From ff6f8ea4a18b1f12c3e4bdaf668b19f6ad98b559 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 13:14:30 -0800 Subject: [PATCH 22/38] cleaner cleanup of AudioClient from RenderingClient --- gvr-interface/src/Client.h | 3 +++ gvr-interface/src/GVRInterface.cpp | 7 +++++++ gvr-interface/src/GVRInterface.h | 1 + gvr-interface/src/RenderingClient.cpp | 16 ++++++---------- gvr-interface/src/RenderingClient.h | 3 ++- libraries/audio-client/src/AudioClient.cpp | 3 ++- libraries/audio-client/src/AudioClient.h | 4 ++++ 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/gvr-interface/src/Client.h b/gvr-interface/src/Client.h index 6eb3913ea9..31bc8be3bd 100644 --- a/gvr-interface/src/Client.h +++ b/gvr-interface/src/Client.h @@ -22,7 +22,10 @@ class Client : public QObject { Q_OBJECT public: Client(QObject* parent = 0); + + virtual void cleanupBeforeQuit() = 0; protected: + void setupNetworking(); virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket); private slots: diff --git a/gvr-interface/src/GVRInterface.cpp b/gvr-interface/src/GVRInterface.cpp index 7476bfc764..3d58396322 100644 --- a/gvr-interface/src/GVRInterface.cpp +++ b/gvr-interface/src/GVRInterface.cpp @@ -86,6 +86,13 @@ GVRInterface::GVRInterface(int argc, char* argv[]) : QTimer* idleTimer = new QTimer(this); connect(idleTimer, &QTimer::timeout, this, &GVRInterface::idle); idleTimer->start(0); + + // call our quit handler before we go down + connect(this, &QCoreApplication::aboutToQuit, this, &GVRInterface::handleApplicationQuit); +} + +void GVRInterface::handleApplicationQuit() { + _client->cleanupBeforeQuit(); } void GVRInterface::idle() { diff --git a/gvr-interface/src/GVRInterface.h b/gvr-interface/src/GVRInterface.h index 926c44da15..9ffbd52909 100644 --- a/gvr-interface/src/GVRInterface.h +++ b/gvr-interface/src/GVRInterface.h @@ -53,6 +53,7 @@ private slots: void handleApplicationStateChange(Qt::ApplicationState state); void idle(); private: + void handleApplicationQuit(); void enterVRMode(); void leaveVRMode(); diff --git a/gvr-interface/src/RenderingClient.cpp b/gvr-interface/src/RenderingClient.cpp index 9737263eee..1abf4bbc95 100644 --- a/gvr-interface/src/RenderingClient.cpp +++ b/gvr-interface/src/RenderingClient.cpp @@ -37,7 +37,7 @@ RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString DependencyManager::set(); // get our audio client setup on its own thread - QThread* audioThread = new QThread(this); + QThread* audioThread = new QThread(); auto audioClient = DependencyManager::set(); audioClient->setPositionGetter(getPositionForAudio); @@ -45,6 +45,8 @@ RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString audioClient->moveToThread(audioThread); connect(audioThread, &QThread::started, audioClient.data(), &AudioClient::start); + connect(audioClient.data(), &AudioClient::destroyed, audioThread, &QThread::quit); + connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); audioThread->start(); @@ -68,15 +70,9 @@ void RenderingClient::sendAvatarPacket() { _fakeAvatar.sendIdentityPacket(); } -RenderingClient::~RenderingClient() { - auto audioClient = DependencyManager::get(); - - // stop the audio client - QMetaObject::invokeMethod(audioClient.data(), "stop", Qt::BlockingQueuedConnection); - - // ask the audio thread to quit and wait until it is done - audioClient->thread()->quit(); - audioClient->thread()->wait(); +void RenderingClient::cleanupBeforeQuit() { + // destroy the AudioClient so it and it's thread will safely go down + DependencyManager::destroy(); } void RenderingClient::processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket) { diff --git a/gvr-interface/src/RenderingClient.h b/gvr-interface/src/RenderingClient.h index 870bde748f..c4724bc086 100644 --- a/gvr-interface/src/RenderingClient.h +++ b/gvr-interface/src/RenderingClient.h @@ -26,7 +26,6 @@ class RenderingClient : public Client { Q_OBJECT public: RenderingClient(QObject* parent = 0, const QString& launchURLString = QString()); - ~RenderingClient(); const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } @@ -35,6 +34,8 @@ public: static glm::vec3 getPositionForAudio() { return _instance->getPosition(); } static glm::quat getOrientationForAudio() { return _instance->getOrientation(); } + virtual void cleanupBeforeQuit(); + private slots: void goToLocation(const glm::vec3& newPosition, bool hasOrientationChange, const glm::quat& newOrientation, diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1919f2bbe5..a45fae94dc 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -136,6 +136,8 @@ AudioClient::AudioClient() : } AudioClient::~AudioClient() { + stop(); + if (_gverbLocal) { gverb_free(_gverbLocal); } @@ -489,7 +491,6 @@ void AudioClient::start() { } void AudioClient::stop() { - _inputFrameBuffer.finalize(); _inputGain.finalize(); _sourceGain.finalize(); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index e55a116e06..4f16500103 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -188,6 +188,10 @@ protected: AudioClient(); ~AudioClient(); + virtual void customDeleter() { + deleteLater(); + } + private: void outputFormatChanged(); From 6c25205856cbace0494e306d22533db3c7efa145 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 13:35:22 -0800 Subject: [PATCH 23/38] fix audio cleanup and close action in Application --- gvr-interface/src/RenderingClient.cpp | 4 +++ interface/src/Application.cpp | 35 +++++++++++-------- interface/src/Application.h | 2 ++ .../networking/src/UserActivityLogger.cpp | 10 +----- libraries/networking/src/UserActivityLogger.h | 2 +- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/gvr-interface/src/RenderingClient.cpp b/gvr-interface/src/RenderingClient.cpp index 1abf4bbc95..8f7755a8c5 100644 --- a/gvr-interface/src/RenderingClient.cpp +++ b/gvr-interface/src/RenderingClient.cpp @@ -71,6 +71,10 @@ void RenderingClient::sendAvatarPacket() { } void RenderingClient::cleanupBeforeQuit() { + + QMetaObject::invokeMethod(DependencyManager::get().data(), + "stop", Qt::BlockingQueuedConnection); + // destroy the AudioClient so it and it's thread will safely go down DependencyManager::destroy(); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 474913fefb..aad1d51ac0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -328,7 +328,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); // put the audio processing on a separate thread - QThread* audioThread = new QThread(this); + QThread* audioThread = new QThread(); audioThread->setObjectName("Audio Thread"); auto audioIO = DependencyManager::get(); @@ -338,7 +338,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : audioIO->moveToThread(audioThread); connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); - connect(audioIO.data(), SIGNAL(muteToggled()), this, SLOT(audioMuteToggled())); + connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); + connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); + connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); audioThread->start(); @@ -516,21 +518,33 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : void Application::aboutToQuit() { _aboutToQuit = true; + + cleanupBeforeQuit(); } -Application::~Application() { +void Application::cleanupBeforeQuit() { QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection); _settingsThread.quit(); saveSettings(); + // TODO: now that this is in cleanupBeforeQuit do we really need it to stop and force + // an event loop to send the packet? + UserActivityLogger::getInstance().close(); + + // stop the AudioClient + QMetaObject::invokeMethod(DependencyManager::get().data(), + "stop", Qt::BlockingQueuedConnection); + + // destroy the AudioClient so it and its thread have a chance to go down safely + DependencyManager::destroy(); +} + +Application::~Application() { _entities.getTree()->setSimulation(NULL); qInstallMessageHandler(NULL); _window->saveGeometry(); - int DELAY_TIME = 1000; - UserActivityLogger::getInstance().close(DELAY_TIME); - // make sure we don't call the idle timer any more delete idleTimer; @@ -541,15 +555,6 @@ Application::~Application() { _nodeThread->quit(); _nodeThread->wait(); - auto audioIO = DependencyManager::get(); - - // stop the audio process - QMetaObject::invokeMethod(audioIO.data(), "stop", Qt::BlockingQueuedConnection); - - // ask the audio thread to quit and wait until it is done - audioIO->thread()->quit(); - audioIO->thread()->wait(); - _octreeProcessor.terminate(); _entityEditSender.terminate(); diff --git a/interface/src/Application.h b/interface/src/Application.h index d015d09035..da29920ef9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -410,6 +410,8 @@ private: void initDisplay(); void init(); + + void cleanupBeforeQuit(); void update(float deltaTime); diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 4fc6905e3a..f2646369c1 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -86,17 +86,9 @@ void UserActivityLogger::launch(QString applicationVersion) { logAction(ACTION_NAME, actionDetails); } -void UserActivityLogger::close(int delayTime) { +void UserActivityLogger::close() { const QString ACTION_NAME = "close"; - - // In order to get the end of the session, we need to give the account manager enough time to send the packet. - QEventLoop loop; - QTimer timer; - connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - // Now we can log it logAction(ACTION_NAME, QJsonObject()); - timer.start(delayTime); - loop.exec(); } void UserActivityLogger::changedDisplayName(QString displayName) { diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 9b100461ee..295ad5ee8d 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -30,7 +30,7 @@ public slots: void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters()); void launch(QString applicationVersion); - void close(int delayTime); + void close(); void changedDisplayName(QString displayName); void changedModel(QString typeOfModel, QString modelURL); void changedDomain(QString domainURL); From 4e71e128f3d6f8bf1ce0be722d1aa0f4a61021dd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 13:42:36 -0800 Subject: [PATCH 24/38] remove QThread not currently used in client --- gvr-interface/src/Client.cpp | 2 -- gvr-interface/src/Client.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/gvr-interface/src/Client.cpp b/gvr-interface/src/Client.cpp index e323ab96b5..65238ad784 100644 --- a/gvr-interface/src/Client.cpp +++ b/gvr-interface/src/Client.cpp @@ -9,8 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - #include #include #include diff --git a/gvr-interface/src/Client.h b/gvr-interface/src/Client.h index 31bc8be3bd..6fbe40f165 100644 --- a/gvr-interface/src/Client.h +++ b/gvr-interface/src/Client.h @@ -16,8 +16,6 @@ #include -class QThread; - class Client : public QObject { Q_OBJECT public: From 30b371559dca50c963472d885b68befe4516bd53 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Feb 2015 15:11:24 -0800 Subject: [PATCH 25/38] fix a typo in RenderingClient --- gvr-interface/src/RenderingClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gvr-interface/src/RenderingClient.cpp b/gvr-interface/src/RenderingClient.cpp index 8f7755a8c5..e6d2f6b585 100644 --- a/gvr-interface/src/RenderingClient.cpp +++ b/gvr-interface/src/RenderingClient.cpp @@ -75,7 +75,7 @@ void RenderingClient::cleanupBeforeQuit() { QMetaObject::invokeMethod(DependencyManager::get().data(), "stop", Qt::BlockingQueuedConnection); - // destroy the AudioClient so it and it's thread will safely go down + // destroy the AudioClient so it and its thread will safely go down DependencyManager::destroy(); } From f1a586520103301f0a733afe2a866690ed369d02 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 12 Feb 2015 15:17:48 -0800 Subject: [PATCH 26/38] added more logging to persist and backup, fixed bug in start up case for no models.svo and backup --- libraries/octree/src/OctreePersistThread.cpp | 30 ++++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index f0612cc9b1..b247f88059 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -213,7 +213,9 @@ void OctreePersistThread::persist() { } _tree->unlock(); + qDebug() << "persist operation calling backup..."; backup(); // handle backup if requested + qDebug() << "persist operation DONE with backup..."; // create our "lock" file to indicate we're saving. @@ -347,6 +349,7 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { void OctreePersistThread::backup() { + qDebug() << "backup operation wantBackup:" << _wantBackup; if (_wantBackup) { quint64 now = usecTimestampNow(); @@ -354,10 +357,12 @@ void OctreePersistThread::backup() { BackupRule& rule = _backupRules[i]; quint64 sinceLastBackup = now - rule.lastBackup; - quint64 SECS_TO_USECS = 1000 * 1000; quint64 intervalToBackup = rule.interval * SECS_TO_USECS; - + + qDebug() << "Checking [" << rule.name << "] - Time since last backup [" << sinceLastBackup << "] " << + "compared to backup interval [" << intervalToBackup << "]..."; + if (sinceLastBackup > intervalToBackup) { qDebug() << "Time since last backup [" << sinceLastBackup << "] for rule [" << rule.name << "] exceeds backup interval [" << intervalToBackup << "] doing backup now..."; @@ -379,15 +384,22 @@ void OctreePersistThread::backup() { } - qDebug() << "backing up persist file " << _filename << "to" << backupFileName << "..."; - bool result = QFile::copy(_filename, backupFileName); - if (result) { - qDebug() << "DONE backing up persist file..."; + QFile persistFile(_filename); + if (persistFile.exists()) { + qDebug() << "backing up persist file " << _filename << "to" << backupFileName << "..."; + bool result = QFile::copy(_filename, backupFileName); + if (result) { + qDebug() << "DONE backing up persist file..."; + rule.lastBackup = now; // only record successful backup in this case. + } else { + qDebug() << "ERROR in backing up persist file..."; + } } else { - qDebug() << "ERROR in backing up persist file..."; + qDebug() << "persist file " << _filename << " does not exist. " << + "nothing to backup for this rule ["<< rule.name << "]..."; } - - rule.lastBackup = now; + } else { + qDebug() << "Backup not needed for this rule ["<< rule.name << "]..."; } } } From 07a12f8b4c818bbb1a6575c930c5b961b233cd80 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 12 Feb 2015 16:55:14 -0800 Subject: [PATCH 27/38] remove a couple crash modes on shutdown --- interface/src/Application.cpp | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c720ed7606..e33938d959 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -519,37 +519,31 @@ void Application::aboutToQuit() { } Application::~Application() { + // stop idle calls + delete idleTimer; + + // save settings QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection); _settingsThread.quit(); saveSettings(); - - _entities.getTree()->setSimulation(NULL); - qInstallMessageHandler(NULL); - _window->saveGeometry(); - int DELAY_TIME = 1000; - UserActivityLogger::getInstance().close(DELAY_TIME); - - // make sure we don't call the idle timer any more - delete idleTimer; - // let the avatar mixer know we're out MyAvatar::sendKillAvatar(); + // log activity (sends data over HTTP) + // NOTE: there is still an occasional crash in UserActivitiyLogger::close() + int DELAY_TIME = 1000; + UserActivityLogger::getInstance().close(DELAY_TIME); + // ask the datagram processing thread to quit and wait until it is done _nodeThread->quit(); _nodeThread->wait(); - // kill any audio injectors that are still around + // kill audio AudioScriptingInterface::getInstance().stopAllInjectors(); - auto audioIO = DependencyManager::get(); - - // stop the audio process QMetaObject::invokeMethod(audioIO.data(), "stop", Qt::BlockingQueuedConnection); - - // ask the audio thread to quit and wait until it is done audioIO->thread()->quit(); audioIO->thread()->wait(); @@ -566,6 +560,17 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + + EntityTree* tree = _entities.getTree(); + tree->lockForWrite(); + _entities.getTree()->setSimulation(NULL); + tree->unlock(); + + qInstallMessageHandler(NULL); + + // At this point we return all memory to the operating system ASAP + // and don't worry about proper cleanup of global variables. + exit(0); } void Application::initializeGL() { From c3b432c4b5be2780244ebbb59154b7941400fb93 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 12 Feb 2015 17:02:21 -0800 Subject: [PATCH 28/38] wrap explicit exit() in #ifndef DEBUG --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e33938d959..57e7ffe065 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -568,9 +568,11 @@ Application::~Application() { qInstallMessageHandler(NULL); +#ifndef DEBUG // At this point we return all memory to the operating system ASAP // and don't worry about proper cleanup of global variables. exit(0); +#endif } void Application::initializeGL() { From 13fc0b1fcda4fa3a10b5bad5d3c7690e3d202f69 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 13 Feb 2015 09:36:16 -0800 Subject: [PATCH 29/38] fix AudioInjector debug line to be more clear --- libraries/audio/src/AudioInjector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1e66ad77cf..9981e0fba7 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -58,7 +58,7 @@ void AudioInjector::setIsFinished(bool isFinished) { if (_shouldDeleteAfterFinish) { // we've been asked to delete after finishing, trigger a queued deleteLater here - qDebug() << "triggering the delete since we are finished"; + qDebug() << "AudioInjector triggering delete from setIsFinished"; QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); } } From 6ff4f8c2c04c01789adcbe7cba95b594c228088d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Feb 2015 11:52:23 -0800 Subject: [PATCH 30/38] give butterflies an initial velocity so that they are on the kinematics list --- examples/example/entities/butterflies.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/example/entities/butterflies.js b/examples/example/entities/butterflies.js index 336e128d83..9cbe8f966c 100644 --- a/examples/example/entities/butterflies.js +++ b/examples/example/entities/butterflies.js @@ -13,6 +13,9 @@ // +print("BUTTERFLIES START"); + + var numButterflies = 25; @@ -109,7 +112,7 @@ function updateButterflies(deltaTime) { var properties = Entities.getEntityProperties(butterflies[i]); if (Vec3.length(Vec3.subtract(properties.position, flockPosition)) > range) { Entities.editEntity(butterflies[i], { position: flockPosition } ); - } else if (properties.velocity.y < 0.0) { + } else if (properties.velocity.y <= 0.0) { // If falling, Create a new direction and impulse var HORIZ_SCALE = 0.50; var VERT_SCALE = 0.50; @@ -139,3 +142,5 @@ Script.scriptEnding.connect(function() { Entities.deleteEntity(butterflies[i]); } }); + +print("BUTTERFLIES END"); From 589077bce64ab3761b7e78db85d9372b03d6f093 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 13 Feb 2015 14:01:56 -0800 Subject: [PATCH 31/38] try again: move stuff around in ~Application --- interface/src/Application.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a99fcde7ee..1e94d79457 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -523,13 +523,21 @@ void Application::aboutToQuit() { } void Application::cleanupBeforeQuit() { + // make sure we don't call the idle timer any more + delete idleTimer; + + // save state QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection); _settingsThread.quit(); saveSettings(); + _window->saveGeometry(); // TODO: now that this is in cleanupBeforeQuit do we really need it to stop and force // an event loop to send the packet? UserActivityLogger::getInstance().close(); + + // let the avatar mixer know we're out + MyAvatar::sendKillAvatar(); // stop the AudioClient QMetaObject::invokeMethod(DependencyManager::get().data(), @@ -540,13 +548,12 @@ void Application::cleanupBeforeQuit() { } Application::~Application() { - _window->saveGeometry(); - - // make sure we don't call the idle timer any more - delete idleTimer; - - // let the avatar mixer know we're out - MyAvatar::sendKillAvatar(); + EntityTree* tree = _entities.getTree(); + tree->lockForWrite(); + _entities.getTree()->setSimulation(NULL); + tree->unlock(); + + qInstallMessageHandler(NULL); // ask the datagram processing thread to quit and wait until it is done _nodeThread->quit(); @@ -565,13 +572,6 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); - - EntityTree* tree = _entities.getTree(); - tree->lockForWrite(); - _entities.getTree()->setSimulation(NULL); - tree->unlock(); - - qInstallMessageHandler(NULL); } void Application::initializeGL() { From bc5a1477e6bcd827b43daa5afddce72e6acc6609 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 13 Feb 2015 14:59:57 -0800 Subject: [PATCH 32/38] added logging of edit commands in the server --- .../src/entities/EntityServer.cpp | 11 ++++ assignment-client/src/entities/EntityServer.h | 1 + .../resources/describe-settings.json | 7 +++ libraries/entities/src/EntityItemProperties.h | 58 +++++++++++++++++-- .../entities/src/EntityItemPropertiesMacros.h | 4 ++ libraries/entities/src/EntityTree.cpp | 4 ++ libraries/entities/src/EntityTree.h | 5 ++ 7 files changed, 84 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index d882ea19ac..85d0a7414c 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -147,3 +147,14 @@ void EntityServer::pruneDeletedEntities() { } } +void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectionObject) { + bool wantEditLogging = false; + readOptionBool(QString("wantEditLogging"), settingsSectionObject, wantEditLogging); + qDebug("wantEditLogging=%s", debug::valueOf(wantEditLogging)); + + + EntityTree* tree = static_cast(_tree); + tree->setWantEditLogging(wantEditLogging); +} + + diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index d072d18cdf..d8c2e39f3b 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -41,6 +41,7 @@ public: virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode); + virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject); public slots: void pruneDeletedEntities(); diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 1ff82f58eb..fdaede8c44 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -409,6 +409,13 @@ "default": "", "advanced": true }, + { + "name": "wantEditLogging", + "type": "checkbox", + "help": "Logging of all edits to entities", + "default": true, + "advanced": true + }, { "name": "verboseDebug", "type": "checkbox", diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4db6cd5d79..51779d3f56 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -194,6 +194,7 @@ public: bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); } bool containsPositionChange() const { return _positionChanged; } bool containsDimensionsChange() const { return _dimensionsChanged; } + bool containsAnimationSettingsChange() const { return _animationSettingsChanged; } float getGlowLevel() const { return _glowLevel; } float getLocalRenderAlpha() const { return _localRenderAlpha; } @@ -256,12 +257,57 @@ inline void EntityItemProperties::setPosition(const glm::vec3& value) inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { - debug << "EntityItemProperties[" << "\n" - << " position:" << properties.getPosition() << "in meters" << "\n" - << " velocity:" << properties.getVelocity() << "in meters" << "\n" - << " last edited:" << properties.getLastEdited() << "\n" - << " edited ago:" << properties.getEditedAgo() << "\n" - << "]"; + debug << "EntityItemProperties[" << "\n"; + + // TODO: figure out why position and animationSettings don't seem to like the macro approach + if (properties.containsPositionChange()) { + debug << " position:" << properties.getPosition() << "in meters" << "\n"; + } + if (properties.containsAnimationSettingsChange()) { + debug << " animationSettings:" << properties.getAnimationSettings() << "\n"; + } + + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Dimensions, dimensions, "in meters"); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Velocity, velocity, "in meters"); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Visible, visible, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Rotation, rotation, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Density, density, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Gravity, gravity, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Damping, damping, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifetime, lifetime, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFPS, animationFPS, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFrameIndex, animationFrameIndex, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationIsPlaying, animationIsPlaying, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, RegistrationPoint, registrationPoint, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AngularVelocity, angularVelocity, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AngularDamping, angularDamping, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, IgnoreForCollisions, ignoreForCollisions, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionsWillMove, collisionsWillMove, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, IsSpotlight, isSpotlight, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, DiffuseColor, diffuseColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientColor, ambientColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, SpecularColor, specularColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ConstantAttenuation, constantAttenuation, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, LinearAttenuation, linearAttenuation, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, QuadraticAttenuation, quadraticAttenuation, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Exponent, exponent, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cutoff, cutoff, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundColor, backgroundColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ShapeType, shapeType, ""); + + debug << " last edited:" << properties.getLastEdited() << "\n"; + debug << " edited ago:" << properties.getEditedAgo() << "\n"; + debug << "]"; return debug; } diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 518035c0d3..592f808e1a 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -321,6 +321,10 @@ T _##n; \ bool _##n##Changed; +#define DEBUG_PROPERTY_IF_CHANGED(D, P, N, n, x) \ + if (P.n##Changed()) { \ + D << " " << #n << ":" << P.get##N() << x << "\n"; \ + } #endif // hifi_EntityItemPropertiesMacros_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 95617b4944..8bb759c1b4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -592,6 +592,10 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char // if the EntityItem exists, then update it if (existingEntity) { + if (wantEditLogging()) { + qDebug() << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID; + qDebug() << " properties:" << properties; + } updateEntity(entityItemID, properties, senderNode->getCanAdjustLocks()); existingEntity->markAsChangedOnServer(); } else { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 3bc0986799..d8b9b9f38f 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -151,6 +151,9 @@ public: void emitEntityScriptChanging(const EntityItemID& entityItemID); void setSimulation(EntitySimulation* simulation); + + bool wantEditLogging() const { return _wantEditLogging; } + void setWantEditLogging(bool value) { _wantEditLogging = value; } signals: void deletingEntity(const EntityItemID& entityID); @@ -180,6 +183,8 @@ private: QHash _entityToElementMap; EntitySimulation* _simulation; + + bool _wantEditLogging = false; }; #endif // hifi_EntityTree_h From 0495833ca6eb13b41373e67c9d4739541ada5d94 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 13 Feb 2015 15:00:37 -0800 Subject: [PATCH 33/38] Disallow entity models to be reset to natural dimensions if unknown --- examples/editEntities.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index a9b5bfeb16..f0bb123e96 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -957,9 +957,18 @@ PropertiesTool = function(opts) { selectionManager.saveProperties(); for (var i = 0; i < selectionManager.selections.length; i++) { var properties = selectionManager.savedProperties[selectionManager.selections[i].id]; - Entities.editEntity(selectionManager.selections[i], { - dimensions: properties.naturalDimensions, - }); + var naturalDimensions = properties.naturalDimensions; + + // If any of the natural dimensions are not 0, resize + if (properties.type == "Model" && naturalDimensions.x == 0 + && naturalDimensions.y == 0 || naturalDimensions.z == 0) { + Window.alert("Cannot reset entity to its natural dimensions: Model URL" + + " is invalid or the model has not yet been loaded."); + } else { + Entities.editEntity(selectionManager.selections[i], { + dimensions: properties.naturalDimensions, + }); + } } pushCommandForSelections(); selectionManager._update(); From 2df850eb8d75bd88aed5eec157a41618566cb437 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 13 Feb 2015 15:02:16 -0800 Subject: [PATCH 34/38] Fix natural dimensions check --- examples/editEntities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index f0bb123e96..b9e594a1c1 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -961,7 +961,7 @@ PropertiesTool = function(opts) { // If any of the natural dimensions are not 0, resize if (properties.type == "Model" && naturalDimensions.x == 0 - && naturalDimensions.y == 0 || naturalDimensions.z == 0) { + && naturalDimensions.y == 0 && naturalDimensions.z == 0) { Window.alert("Cannot reset entity to its natural dimensions: Model URL" + " is invalid or the model has not yet been loaded."); } else { From 98b445b22f9cac6e5a6e0ff86c3d8ad5d0a5e7e8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Feb 2015 15:21:03 -0800 Subject: [PATCH 35/38] the geometryCacheID for Rectangle3DOverlay wasn't being initialized (so was randomish). if they overlapped, some would be drawn looking like others --- interface/src/ui/overlays/Rectangle3DOverlay.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 6ee90b11cc..99023d389b 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -20,6 +20,7 @@ #include "Rectangle3DOverlay.h" Rectangle3DOverlay::Rectangle3DOverlay() { + _geometryCacheID(DependencyManager::get()->allocateID()) } Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) : From 42511f3b7f1ad65d997e23f3e77c147dd5d933e6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Feb 2015 15:23:22 -0800 Subject: [PATCH 36/38] M-x mangle buffer --- interface/src/ui/overlays/Rectangle3DOverlay.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 99023d389b..7a88b327d1 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -19,8 +19,9 @@ #include "Rectangle3DOverlay.h" -Rectangle3DOverlay::Rectangle3DOverlay() { +Rectangle3DOverlay::Rectangle3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) +{ } Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) : From 1fc0225939fa516333a17febfa8335922440bd1e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 13 Feb 2015 15:33:41 -0800 Subject: [PATCH 37/38] change ScriptCache and SoundCache to derive from Dependency --- assignment-client/src/Agent.cpp | 2 +- assignment-client/src/AssignmentClient.cpp | 2 +- interface/src/Application.cpp | 7 ++- interface/src/ui/CachesSizeDialog.cpp | 8 ++-- libraries/audio/src/SoundCache.cpp | 5 -- libraries/audio/src/SoundCache.h | 6 +-- libraries/metavoxels/src/Bitstream.cpp | 54 +++++++++++----------- libraries/metavoxels/src/MetavoxelUtil.cpp | 6 +-- libraries/metavoxels/src/ScriptCache.cpp | 9 +--- libraries/metavoxels/src/ScriptCache.h | 6 +-- tests/metavoxels/src/MetavoxelTests.cpp | 14 +++--- 11 files changed, 56 insertions(+), 63 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 08a510f32c..4755d9137a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -208,7 +208,7 @@ void Agent::run() { _scriptEngine.init(); // must be done before we set up the viewers - _scriptEngine.registerGlobalObject("SoundCache", &SoundCache::getInstance()); + _scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get().data()); _scriptEngine.registerGlobalObject("EntityViewer", &_entityViewer); _entityViewer.setJurisdictionListener(_scriptEngine.getEntityScriptingInterface()->getJurisdictionListener()); diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 9eb2fad739..80f3cbab5e 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -136,7 +136,7 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : // Create Singleton objects on main thread NetworkAccessManager::getInstance(); - SoundCache::getInstance(); + auto soundCache = DependencyManager::get(); } void AssignmentClient::sendAssignmentRequest() { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2011f9b82e..903cae413e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -211,6 +211,8 @@ bool setupEssentials(int& argc, char** argv) { auto addressManager = DependencyManager::set(); auto nodeList = DependencyManager::set(NodeType::Agent, listenPort); auto geometryCache = DependencyManager::set(); + auto scriptCache = DependencyManager::set(); + auto soundCache = DependencyManager::set(); auto glowEffect = DependencyManager::set(); auto faceshift = DependencyManager::set(); auto audio = DependencyManager::set(); @@ -563,11 +565,14 @@ Application::~Application() { _myAvatar = NULL; DependencyManager::destroy(); + + qDebug() << "start destroying ResourceCaches Application::~Application() line:" << __LINE__; DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + qDebug() << "done destroying ResourceCaches Application::~Application() line:" << __LINE__; } void Application::initializeGL() { @@ -3466,7 +3471,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("SoundCache", &SoundCache::getInstance()); + scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels); diff --git a/interface/src/ui/CachesSizeDialog.cpp b/interface/src/ui/CachesSizeDialog.cpp index 4d4457c922..d21d1c2db8 100644 --- a/interface/src/ui/CachesSizeDialog.cpp +++ b/interface/src/ui/CachesSizeDialog.cpp @@ -59,8 +59,8 @@ CachesSizeDialog::CachesSizeDialog(QWidget* parent) : void CachesSizeDialog::confirmClicked(bool checked) { DependencyManager::get()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES); - ScriptCache::getInstance()->setUnusedResourceCacheSize(_scripts->value() * BYTES_PER_MEGABYTES); - SoundCache::getInstance().setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); + DependencyManager::get()->setUnusedResourceCacheSize(_scripts->value() * BYTES_PER_MEGABYTES); + DependencyManager::get()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES); QDialog::close(); @@ -69,8 +69,8 @@ void CachesSizeDialog::confirmClicked(bool checked) { void CachesSizeDialog::resetClicked(bool checked) { _animations->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _geometries->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _scripts->setValue(ScriptCache::getInstance()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _sounds->setValue(SoundCache::getInstance().getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); + _scripts->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); + _sounds->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _textures->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); } diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index fe05372ce5..2949a6c70a 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -15,11 +15,6 @@ static int soundPointerMetaTypeId = qRegisterMetaType(); -SoundCache& SoundCache::getInstance() { - static SoundCache staticInstance; - return staticInstance; -} - SoundCache::SoundCache(QObject* parent) : ResourceCache(parent) { diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h index f9fbf51c10..7942e16010 100644 --- a/libraries/audio/src/SoundCache.h +++ b/libraries/audio/src/SoundCache.h @@ -17,11 +17,11 @@ #include "Sound.h" /// Scriptable interface for sound loading. -class SoundCache : public ResourceCache { +class SoundCache : public ResourceCache, public Dependency { Q_OBJECT + SINGLETON_DEPENDENCY + public: - static SoundCache& getInstance(); - Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); protected: diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 642562bfb5..9c672a415e 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -465,9 +465,9 @@ void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& ref } else if (reference.isArray()) { if (value.isArray()) { *this << false; - int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); *this << length; - int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + int referenceLength = reference.property(DependencyManager::get()->getLengthString()).toInt32(); for (int i = 0; i < length; i++) { if (i < referenceLength) { writeDelta(value.property(i), reference.property(i)); @@ -555,7 +555,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { QVariant variant; readRawDelta(variant, reference.toVariant()); - value = ScriptCache::getInstance()->getEngine()->newVariant(variant); + value = DependencyManager::get()->getEngine()->newVariant(variant); } } else if (reference.isQObject()) { bool typeChanged; @@ -566,7 +566,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { QObject* object; readRawDelta(object, reference.toQObject()); - value = ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); + value = DependencyManager::get()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); } } else if (reference.isQMetaObject()) { bool typeChanged; @@ -577,7 +577,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { const QMetaObject* metaObject; *this >> metaObject; - value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + value = DependencyManager::get()->getEngine()->newQMetaObject(metaObject); } } else if (reference.isDate()) { bool typeChanged; @@ -588,7 +588,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { QDateTime dateTime; *this >> dateTime; - value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + value = DependencyManager::get()->getEngine()->newDate(dateTime); } } else if (reference.isRegExp()) { bool typeChanged; @@ -599,7 +599,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { QRegExp regExp; *this >> regExp; - value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + value = DependencyManager::get()->getEngine()->newRegExp(regExp); } } else if (reference.isArray()) { bool typeChanged; @@ -610,8 +610,8 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { int length; *this >> length; - value = ScriptCache::getInstance()->getEngine()->newArray(length); - int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + value = DependencyManager::get()->getEngine()->newArray(length); + int referenceLength = reference.property(DependencyManager::get()->getLengthString()).toInt32(); for (int i = 0; i < length; i++) { QScriptValue element; if (i < referenceLength) { @@ -630,7 +630,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { // start by shallow-copying the reference - value = ScriptCache::getInstance()->getEngine()->newObject(); + value = DependencyManager::get()->getEngine()->newObject(); for (QScriptValueIterator it(reference); it.hasNext(); ) { it.next(); value.setProperty(it.scriptName(), it.value()); @@ -1036,7 +1036,7 @@ Bitstream& Bitstream::operator<<(const QScriptValue& value) { } else if (value.isArray()) { writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); - int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); *this << length; for (int i = 0; i < length; i++) { *this << value.property(i); @@ -1087,37 +1087,37 @@ Bitstream& Bitstream::operator>>(QScriptValue& value) { case VARIANT_SCRIPT_VALUE: { QVariant variantValue; *this >> variantValue; - value = ScriptCache::getInstance()->getEngine()->newVariant(variantValue); + value = DependencyManager::get()->getEngine()->newVariant(variantValue); break; } case QOBJECT_SCRIPT_VALUE: { QObject* object; *this >> object; - ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); + DependencyManager::get()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); break; } case QMETAOBJECT_SCRIPT_VALUE: { const QMetaObject* metaObject; *this >> metaObject; - ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + DependencyManager::get()->getEngine()->newQMetaObject(metaObject); break; } case DATE_SCRIPT_VALUE: { QDateTime dateTime; *this >> dateTime; - value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + value = DependencyManager::get()->getEngine()->newDate(dateTime); break; } case REGEXP_SCRIPT_VALUE: { QRegExp regExp; *this >> regExp; - value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + value = DependencyManager::get()->getEngine()->newRegExp(regExp); break; } case ARRAY_SCRIPT_VALUE: { int length; *this >> length; - value = ScriptCache::getInstance()->getEngine()->newArray(length); + value = DependencyManager::get()->getEngine()->newArray(length); for (int i = 0; i < length; i++) { QScriptValue element; *this >> element; @@ -1126,7 +1126,7 @@ Bitstream& Bitstream::operator>>(QScriptValue& value) { break; } case OBJECT_SCRIPT_VALUE: { - value = ScriptCache::getInstance()->getEngine()->newObject(); + value = DependencyManager::get()->getEngine()->newObject(); forever { QScriptString name; *this >> name; @@ -1477,7 +1477,7 @@ Bitstream& Bitstream::operator>(QScriptString& string) { QString rawString; *this >> rawString; string = (rawString == INVALID_STRING) ? QScriptString() : - ScriptCache::getInstance()->getEngine()->toStringHandle(rawString); + DependencyManager::get()->getEngine()->toStringHandle(rawString); return *this; } @@ -1828,7 +1828,7 @@ QJsonValue JSONWriter::getData(const QScriptValue& value) { } else if (value.isArray()) { object.insert("type", QString("ARRAY")); QJsonArray array; - int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); for (int i = 0; i < length; i++) { array.append(getData(value.property(i))); } @@ -2209,31 +2209,31 @@ void JSONReader::putData(const QJsonValue& data, QScriptValue& value) { } else if (type == "VARIANT") { QVariant variant; putData(object.value("value"), variant); - value = ScriptCache::getInstance()->getEngine()->newVariant(variant); + value = DependencyManager::get()->getEngine()->newVariant(variant); } else if (type == "QOBJECT") { QObject* qObject; putData(object.value("value"), qObject); - value = ScriptCache::getInstance()->getEngine()->newQObject(qObject, QScriptEngine::ScriptOwnership); + value = DependencyManager::get()->getEngine()->newQObject(qObject, QScriptEngine::ScriptOwnership); } else if (type == "QMETAOBJECT") { const QMetaObject* metaObject; putData(object.value("value"), metaObject); - value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + value = DependencyManager::get()->getEngine()->newQMetaObject(metaObject); } else if (type == "DATE") { QDateTime dateTime; putData(object.value("value"), dateTime); - value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + value = DependencyManager::get()->getEngine()->newDate(dateTime); } else if (type == "REGEXP") { QRegExp regExp; putData(object.value("value"), regExp); - value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + value = DependencyManager::get()->getEngine()->newRegExp(regExp); } else if (type == "ARRAY") { QJsonArray array = object.value("value").toArray(); - value = ScriptCache::getInstance()->getEngine()->newArray(array.size()); + value = DependencyManager::get()->getEngine()->newArray(array.size()); for (int i = 0; i < array.size(); i++) { QScriptValue element; putData(array.at(i), element); @@ -2241,7 +2241,7 @@ void JSONReader::putData(const QJsonValue& data, QScriptValue& value) { } } else if (type == "OBJECT") { QJsonObject jsonObject = object.value("value").toObject(); - value = ScriptCache::getInstance()->getEngine()->newObject(); + value = DependencyManager::get()->getEngine()->newObject(); for (QJsonObject::const_iterator it = jsonObject.constBegin(); it != jsonObject.constEnd(); it++) { QScriptValue element; putData(it.value(), element); diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 73900fdcbc..4e86e1c636 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -663,7 +663,7 @@ void ParameterizedURLEditor::updateURL() { QByteArray valuePropertyName = widget->property("valuePropertyName").toByteArray(); const QMetaObject* widgetMetaObject = widget->metaObject(); QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); - parameters.insert(ScriptCache::getInstance()->getEngine()->toStringHandle( + parameters.insert(DependencyManager::get()->getEngine()->toStringHandle( widget->property("parameterName").toString()), widgetProperty.read(widget)); } } @@ -677,7 +677,7 @@ void ParameterizedURLEditor::updateParameters() { if (_program) { _program->disconnect(this); } - _program = ScriptCache::getInstance()->getProgram(_url.getURL()); + _program = DependencyManager::get()->getProgram(_url.getURL()); if (_program->isLoaded()) { continueUpdatingParameters(); } else { @@ -698,7 +698,7 @@ void ParameterizedURLEditor::continueUpdatingParameters() { } delete form; } - QSharedPointer value = ScriptCache::getInstance()->getValue(_url.getURL()); + QSharedPointer value = DependencyManager::get()->getValue(_url.getURL()); const QList& parameters = static_cast(value.data())->getParameterInfo(); if (parameters.isEmpty()) { return; diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 7e8dbc4bae..b7e81b5381 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -57,8 +57,8 @@ bool operator==(const QScriptValue& first, const QScriptValue& second) { if (!second.isArray()) { return false; } - int length = first.property(ScriptCache::getInstance()->getLengthString()).toInt32(); - if (second.property(ScriptCache::getInstance()->getLengthString()).toInt32() != length) { + int length = first.property(DependencyManager::get()->getLengthString()).toInt32(); + if (second.property(DependencyManager::get()->getLengthString()).toInt32() != length) { return false; } for (int i = 0; i < length; i++) { @@ -103,11 +103,6 @@ bool operator<(const QScriptValue& first, const QScriptValue& second) { return first.lessThan(second); } -ScriptCache* ScriptCache::getInstance() { - static ScriptCache cache; - return &cache; -} - ScriptCache::ScriptCache() : _engine(NULL) { diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index 5d29157b3d..b9294a7504 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -25,13 +25,11 @@ class NetworkProgram; class NetworkValue; /// Maintains a cache of loaded scripts. -class ScriptCache : public ResourceCache { +class ScriptCache : public ResourceCache, public Dependency { Q_OBJECT + SINGLETON_DEPENDENCY public: - - static ScriptCache* getInstance(); - ScriptCache(); void setEngine(QScriptEngine* engine); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 106c56b0fe..938b5470e8 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -219,14 +219,14 @@ static QScriptValue createRandomScriptValue(bool complex = false, bool ensureHas case 4: { int length = randIntInRange(2, 6); - QScriptValue value = ScriptCache::getInstance()->getEngine()->newArray(length); + QScriptValue value = DependencyManager::get()->getEngine()->newArray(length); for (int i = 0; i < length; i++) { value.setProperty(i, createRandomScriptValue()); } return value; } default: { - QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject(); + QScriptValue value = DependencyManager::get()->getEngine()->newObject(); if (ensureHashOrder) { // we can't depend on the iteration order, so if we need it to be the same (as when comparing bytes), we // can only have one property @@ -747,7 +747,7 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { case 3: { SharedObjectPointer newState = state->clone(true); QScriptValue oldValue = static_cast(newState.data())->getBizzle(); - QScriptValue newValue = ScriptCache::getInstance()->getEngine()->newObject(); + QScriptValue newValue = DependencyManager::get()->getEngine()->newObject(); for (QScriptValueIterator it(oldValue); it.hasNext(); ) { it.next(); newValue.setProperty(it.scriptName(), it.value()); @@ -755,8 +755,8 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { switch (randIntInRange(0, 2)) { case 0: { QScriptValue oldArray = oldValue.property("foo"); - int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32(); - QScriptValue newArray = ScriptCache::getInstance()->getEngine()->newArray(oldLength); + int oldLength = oldArray.property(DependencyManager::get()->getLengthString()).toInt32(); + QScriptValue newArray = DependencyManager::get()->getEngine()->newArray(oldLength); for (int i = 0; i < oldLength; i++) { newArray.setProperty(i, oldArray.property(i)); } @@ -1203,8 +1203,8 @@ TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : _bong(bong) { sharedObjectsCreated++; - _bizzle = ScriptCache::getInstance()->getEngine()->newObject(); - _bizzle.setProperty("foo", ScriptCache::getInstance()->getEngine()->newArray(4)); + _bizzle = DependencyManager::get()->getEngine()->newObject(); + _bizzle.setProperty("foo", DependencyManager::get()->getEngine()->newArray(4)); } TestSharedObjectA::~TestSharedObjectA() { From cfbf8fdca763d812eb4bd52e121f7fcf03122c33 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 13 Feb 2015 16:19:10 -0800 Subject: [PATCH 38/38] explicit cleanup of loaded model animations in Application dtor --- interface/src/Application.cpp | 3 +++ libraries/entities/src/ModelEntityItem.cpp | 10 ---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e94d79457..cee5404287 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -565,6 +566,8 @@ Application::~Application() { Menu::getInstance()->deleteLater(); _myAvatar = NULL; + + ModelEntityItem::cleanupLoadedAnimations() ; DependencyManager::destroy(); DependencyManager::destroy(); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 4d8e741cc6..ed76b8c99f 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -163,16 +163,6 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit QMap ModelEntityItem::_loadedAnimations; // TODO: improve cleanup by leveraging the AnimationPointer(s) -// This class/instance will cleanup the animations once unloaded. -class EntityAnimationsBookkeeper { -public: - ~EntityAnimationsBookkeeper() { - ModelEntityItem::cleanupLoadedAnimations(); - } -}; - -EntityAnimationsBookkeeper modelAnimationsBookkeeperInstance; - void ModelEntityItem::cleanupLoadedAnimations() { foreach(AnimationPointer animation, _loadedAnimations) { animation.clear();