diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 07214176bd..46e826c596 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -53,7 +54,8 @@ static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10; Agent::Agent(ReceivedMessage& message) : ThreadedAssignment(message), - _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) { + _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) +{ DependencyManager::get()->setPacketSender(&_entityEditSender); ResourceManager::init(); @@ -64,12 +66,16 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(ScriptEngine::AGENT_SCRIPT); + DependencyManager::set(); + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes( @@ -327,6 +333,8 @@ void Agent::executeScript() { _scriptEngine = std::unique_ptr(new ScriptEngine(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload)); _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do + DependencyManager::get()->setScriptEngine(_scriptEngine.get()); + // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); @@ -470,6 +478,8 @@ void Agent::executeScript() { Frame::clearFrameHandler(AUDIO_FRAME_TYPE); Frame::clearFrameHandler(AVATAR_FRAME_TYPE); + DependencyManager::destroy(); + setFinished(true); } @@ -753,8 +763,19 @@ void Agent::aboutToFinish() { // cleanup the AudioInjectorManager (and any still running injectors) DependencyManager::destroy(); + + // destroy all other created dependencies + DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + emit stopAvatarAudioTimer(); _avatarAudioTimerThread.quit(); diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 620ac8e047..2a156aba18 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -46,9 +46,6 @@ class Agent : public ThreadedAssignment { public: Agent(ReceivedMessage& message); - void setIsAvatar(bool isAvatar); - bool isAvatar() const { return _isAvatar; } - bool isPlayingAvatarSound() const { return _avatarSound != NULL; } bool isListeningToAudioStream() const { return _isListeningToAudioStream; } @@ -65,6 +62,9 @@ public: public slots: void run() override; void playAvatarSound(SharedSoundPointer avatarSound); + + void setIsAvatar(bool isAvatar); + bool isAvatar() const { return _isAvatar; } private slots: void requestScript(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9c523f53e8..6ab4dc2420 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -118,6 +118,7 @@ #include #include #include +#include #include #include #include @@ -467,6 +468,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(ScriptEngine::CLIENT_SCRIPT); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -5439,6 +5441,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri // AvatarManager has some custom types AvatarManager::registerMetaTypes(scriptEngine); + // give the script engine to the RecordingScriptingInterface for its callbacks + DependencyManager::get()->setScriptEngine(scriptEngine); + if (property(hifi::properties::TEST).isValid()) { scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance()); } diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 78a05677fd..73096825e0 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -19,7 +19,6 @@ QThreadStorage networkAccessManagers; QNetworkAccessManager& NetworkAccessManager::getInstance() { if (!networkAccessManagers.hasLocalData()) { networkAccessManagers.setLocalData(new QNetworkAccessManager()); - } return *networkAccessManagers.localData(); diff --git a/libraries/recording/src/recording/ClipCache.cpp b/libraries/recording/src/recording/ClipCache.cpp index 66d5fb0d88..5c55c6bb1c 100644 --- a/libraries/recording/src/recording/ClipCache.cpp +++ b/libraries/recording/src/recording/ClipCache.cpp @@ -5,8 +5,12 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#include + #include "ClipCache.h" #include "impl/PointerClip.h" +#include "Logging.h" using namespace recording; NetworkClipLoader::NetworkClipLoader(const QUrl& url) : @@ -21,18 +25,28 @@ void NetworkClip::init(const QByteArray& clipData) { void NetworkClipLoader::downloadFinished(const QByteArray& data) { _clip->init(data); finishedLoading(true); + emit clipLoaded(); } -ClipCache& ClipCache::instance() { - static ClipCache _instance; - return _instance; +ClipCache::ClipCache(QObject* parent) : + ResourceCache(parent) +{ + } NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) { - return ResourceCache::getResource(url, QUrl(), nullptr).staticCast(); + if (QThread::currentThread() != thread()) { + NetworkClipLoaderPointer result; + QMetaObject::invokeMethod(this, "getClipLoader", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(NetworkClipLoaderPointer, result), Q_ARG(const QUrl&, url)); + return result; + } + + return getResource(url).staticCast(); } QSharedPointer ClipCache::createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) { + qCDebug(recordingLog) << "Loading recording at" << url; return QSharedPointer(new NetworkClipLoader(url), &Resource::deleter); } diff --git a/libraries/recording/src/recording/ClipCache.h b/libraries/recording/src/recording/ClipCache.h index 7df83efdbf..2c3465e725 100644 --- a/libraries/recording/src/recording/ClipCache.h +++ b/libraries/recording/src/recording/ClipCache.h @@ -30,26 +30,34 @@ private: }; class NetworkClipLoader : public Resource { + Q_OBJECT public: NetworkClipLoader(const QUrl& url); virtual void downloadFinished(const QByteArray& data) override; ClipPointer getClip() { return _clip; } bool completed() { return _failedToLoad || isLoaded(); } +signals: + void clipLoaded(); + private: const NetworkClip::Pointer _clip; }; using NetworkClipLoaderPointer = QSharedPointer; -class ClipCache : public ResourceCache { -public: - static ClipCache& instance(); - +class ClipCache : public ResourceCache, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public slots: NetworkClipLoaderPointer getClipLoader(const QUrl& url); protected: virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) override; + +private: + ClipCache(QObject* parent = nullptr); }; } diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index 41bb780b47..36de1c1ef7 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -52,32 +52,49 @@ float RecordingScriptingInterface::playerLength() const { return _player->length(); } -bool RecordingScriptingInterface::loadRecording(const QString& url) { - using namespace recording; +void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) { + auto clipLoader = DependencyManager::get()->getClipLoader(url); - auto loader = ClipCache::instance().getClipLoader(url); - if (!loader) { - qWarning() << "Clip failed to load from " << url; - return false; - } + // hold a strong pointer to the loading clip so that it has a chance to load + _clipLoaders.insert(clipLoader); - if (!loader->isLoaded()) { - QEventLoop loop; - QObject::connect(loader.data(), &Resource::loaded, &loop, &QEventLoop::quit); - QObject::connect(loader.data(), &Resource::failed, &loop, &QEventLoop::quit); - loop.exec(); - } + auto weakClipLoader = clipLoader.toWeakRef(); - if (!loader->isLoaded()) { - qWarning() << "Clip failed to load from " << url; - return false; - } + // when clip loaded, call the callback with the URL and success boolean + connect(clipLoader.data(), &recording::NetworkClipLoader::clipLoaded, this, + [this, weakClipLoader, url, callback]() mutable { - _player->queueClip(loader->getClip()); - return true; + if (auto clipLoader = weakClipLoader.toStrongRef()) { + qCDebug(scriptengine) << "Loaded recording from" << url; + + _player->queueClip(clipLoader->getClip()); + + if (callback.isFunction()) { + QScriptValueList args { true, url }; + callback.call(_scriptEngine->globalObject(), args); + } + + // drop our strong pointer to this clip so it is cleaned up + _clipLoaders.remove(clipLoader); + } + }); + + // when clip load fails, call the callback with the URL and failure boolean + connect(clipLoader.data(), &recording::NetworkClipLoader::failed, this, [this, weakClipLoader, url, callback](QNetworkReply::NetworkError error) mutable { + qCDebug(scriptengine) << "Failed to load recording from" << url; + + if (callback.isFunction()) { + QScriptValueList args { false, url }; + callback.call(_scriptEngine->currentContext()->thisObject(), args); + } + + if (auto clipLoader = weakClipLoader.toStrongRef()) { + // drop out strong pointer to this clip so it is cleaned up + _clipLoaders.remove(clipLoader); + } + }); } - void RecordingScriptingInterface::startPlaying() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h index 9661ee9d80..a9fdf1deb4 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.h +++ b/libraries/script-engine/src/RecordingScriptingInterface.h @@ -15,9 +15,11 @@ #include #include +#include #include #include +class QScriptEngine; class QScriptValue; class RecordingScriptingInterface : public QObject, public Dependency { @@ -26,8 +28,10 @@ class RecordingScriptingInterface : public QObject, public Dependency { public: RecordingScriptingInterface(); + void setScriptEngine(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } + public slots: - bool loadRecording(const QString& url); + void loadRecording(const QString& url, QScriptValue callback = QScriptValue()); void startPlaying(); void pausePlayer(); @@ -79,6 +83,9 @@ protected: Flag _useAttachments { false }; Flag _useSkeletonModel { false }; recording::ClipPointer _lastClip; + + QScriptEngine* _scriptEngine; + QSet _clipLoaders; }; #endif // hifi_RecordingScriptingInterface_h diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index 719763f706..7a453e63c3 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -21,7 +21,7 @@ #include #define SINGLETON_DEPENDENCY \ - friend class DependencyManager; + friend class ::DependencyManager; class Dependency { public: diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index a7e251f0e0..81a0eed9f3 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -769,7 +769,7 @@ bool similarStrings(const QString& stringA, const QString& stringB) { void disableQtBearerPoll() { // to work around the Qt constant wireless scanning, set the env for polling interval very high - const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); + const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT16_MAX).toLocal8Bit(); qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); } diff --git a/script-archive/acScripts/BetterClientSimulationBotFromRecording.js b/script-archive/acScripts/BetterClientSimulationBotFromRecording.js index fafb761729..c276d302e1 100644 --- a/script-archive/acScripts/BetterClientSimulationBotFromRecording.js +++ b/script-archive/acScripts/BetterClientSimulationBotFromRecording.js @@ -138,7 +138,13 @@ Agent.isAvatar = true; Agent.isListeningToAudioStream = true; Avatar.skeletonModelURL = AVATAR_URL; // FIXME - currently setting an avatar while playing a recording doesn't work it will be ignored -Recording.loadRecording(RECORDING_URL); +Recording.loadRecording(RECORDING_URL, function(success) { + if (success) { + Script.update.connect(update); + } else { + print("Failed to load recording from " + RECORDING_URL); + } +}); count = 300; // This is necessary to wait for the audio mixer to connect function update(event) { @@ -174,10 +180,8 @@ function update(event) { +" FT: " + Avatar.getDataRate("faceTrackerOutbound").toFixed(2) + "\n" +" JD: " + Avatar.getDataRate("jointDataOutbound").toFixed(2)); } - + if (!Recording.isPlaying()) { Script.update.disconnect(update); } } - -Script.update.connect(update); diff --git a/script-archive/acScripts/PlayRecordingOnAC.js b/script-archive/acScripts/PlayRecordingOnAC.js index 0961f65079..5979913d23 100644 --- a/script-archive/acScripts/PlayRecordingOnAC.js +++ b/script-archive/acScripts/PlayRecordingOnAC.js @@ -20,7 +20,13 @@ Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); Avatar.scale = 1.0; Agent.isAvatar = true; -Recording.loadRecording(recordingFile); +Recording.loadRecording(recordingFile, function(success) { + if (success) { + Script.update.connect(update); + } else { + print("Failed to load recording from " + recordingFile); + } +}); count = 300; // This is necessary to wait for the audio mixer to connect function update(event) { @@ -39,10 +45,8 @@ function update(event) { Vec3.print("Playing from ", Avatar.position); count--; } - + if (!Recording.isPlaying()) { Script.update.disconnect(update); } } - -Script.update.connect(update); diff --git a/script-archive/acScripts/triggeredRecordingOnAC.js b/script-archive/acScripts/triggeredRecordingOnAC.js index 9de5173615..964e82321f 100644 --- a/script-archive/acScripts/triggeredRecordingOnAC.js +++ b/script-archive/acScripts/triggeredRecordingOnAC.js @@ -42,10 +42,15 @@ var playRecording = function() { Recording.setPlayerLoop(false); Recording.setPlayerTime(STARTING_TIME); Recording.setPlayerAudioOffset(AUDIO_OFFSET); - Recording.loadRecording(CLIP_URL); - Recording.startPlaying(); - isPlaying = true; - isPlayable = false; // Set this true again after the cooldown period + Recording.loadRecording(CLIP_URL, function(success) { + if (success) { + Recording.startPlaying(); + isPlaying = true; + isPlayable = false; // Set this true again after the cooldown period + } else { + print("Failed to load recording from " + CLIP_URL); + } + }); }; Script.update.connect(function(deltaTime) { diff --git a/script-archive/tests/playbackAcTest.js b/script-archive/tests/playbackAcTest.js index 5630b17ed8..5439f6f474 100644 --- a/script-archive/tests/playbackAcTest.js +++ b/script-archive/tests/playbackAcTest.js @@ -2,7 +2,7 @@ var origin = {x: 512, y: 512, z: 512}; var millisecondsToWaitBeforeStarting = 2 * 1000; // To give the various servers a chance to start. -var millisecondsToWaitBeforeEnding = 30 * 1000; +var millisecondsToWaitBeforeEnding = 30 * 1000; Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy Avatar.displayName = "AC Avatar"; @@ -10,9 +10,15 @@ Agent.isAvatar = true; Script.setTimeout(function () { Avatar.position = origin; - Recording.loadRecording("d:/hifi.rec"); - Recording.setPlayerLoop(true); - Recording.startPlaying(); + Recording.loadRecording("d:/hifi.rec", function(success) { + if (success) { + Recording.setPlayerLoop(true); + Recording.startPlaying(); + } else { + print("Failed to load recording"); + } + }); + }, millisecondsToWaitBeforeStarting); @@ -21,4 +27,4 @@ Script.setTimeout(function () { Agent.isAvatar = false; Recording.stopPlaying(); Script.stop(); -}, millisecondsToWaitBeforeEnding); \ No newline at end of file +}, millisecondsToWaitBeforeEnding); diff --git a/script-archive/vrShop/cash/shopCashierAC.js b/script-archive/vrShop/cash/shopCashierAC.js index 1219806e8a..5236fa1218 100644 --- a/script-archive/vrShop/cash/shopCashierAC.js +++ b/script-archive/vrShop/cash/shopCashierAC.js @@ -26,7 +26,7 @@ var PLAY = "Play"; function getAction(channel, message, senderID) { if(subscribed) { print("I'm the agent and I received this: " + message); - + switch(message) { case PLAY: print("Play"); @@ -35,7 +35,7 @@ function getAction(channel, message, senderID) { Recording.startPlaying(); } break; - + default: print("Unknown action: " + action); break; @@ -49,16 +49,21 @@ function update(deltaTime) { totalTime += deltaTime; if (totalTime > WAIT_FOR_AUDIO_MIXER) { - if (!subscribed) { + if (!subscribed) { Messages.subscribe(PLAYBACK_CHANNEL); subscribed = true; - Recording.loadRecording(clip_url); - Recording.setPlayFromCurrentLocation(playFromCurrentLocation); - Recording.setPlayerUseDisplayName(useDisplayName); - Recording.setPlayerUseAttachments(useAttachments); - Recording.setPlayerUseHeadModel(false); - Recording.setPlayerUseSkeletonModel(useAvatarModel); - Agent.isAvatar = true; + Recording.loadRecording(clip_url, function(success) { + if (success) { + Recording.setPlayFromCurrentLocation(playFromCurrentLocation); + Recording.setPlayerUseDisplayName(useDisplayName); + Recording.setPlayerUseAttachments(useAttachments); + Recording.setPlayerUseHeadModel(false); + Recording.setPlayerUseSkeletonModel(useAvatarModel); + Agent.isAvatar = true; + } else { + print("Failed to load recording from " + clip_url); + } + }); } } @@ -70,4 +75,4 @@ Messages.messageReceived.connect(function (channel, message, senderID) { } }); -Script.update.connect(update); \ No newline at end of file +Script.update.connect(update); diff --git a/script-archive/vrShop/review/shopReviewerAC.js b/script-archive/vrShop/review/shopReviewerAC.js index 7b7776fe98..47b08e0a85 100644 --- a/script-archive/vrShop/review/shopReviewerAC.js +++ b/script-archive/vrShop/review/shopReviewerAC.js @@ -9,7 +9,7 @@ var command = null; -var clip_url = null; +var clip_url = null; var REVIEW_CHANNEL = "reviewChannel"; var playFromCurrentLocation = true; @@ -28,16 +28,16 @@ var HIDE = "Hide"; function getAction(channel, message, senderID) { if(subscribed) { print("I'm the agent and I received this: " + message); - + if (Recording.isPlaying()) { Recording.stopPlaying(); } - + m = JSON.parse(message); - + command = m.command; clip_url = m.clip_url; - + switch(command) { case PLAY: print("Play"); @@ -46,21 +46,25 @@ function getAction(channel, message, senderID) { Recording.startPlaying(); } break; - + case SHOW: print("Show"); - Recording.loadRecording(clip_url); - Agent.isAvatar = true; - Recording.setPlayerTime(0.0); - Recording.startPlaying(); - Recording.stopPlaying(); + Recording.loadRecording(clip_url, function(success) { + if (success) { + Agent.isAvatar = true; + Recording.setPlayerTime(0.0); + Recording.startPlaying(); + Recording.stopPlaying(); + } + }); + break; - + case HIDE: print("Hide"); Agent.isAvatar = false; break; - + default: print("Unknown action: " + action); break; @@ -74,7 +78,7 @@ function update(deltaTime) { totalTime += deltaTime; if (totalTime > WAIT_FOR_AUDIO_MIXER) { - if (!subscribed) { + if (!subscribed) { Messages.subscribe(REVIEW_CHANNEL); subscribed = true; Recording.setPlayFromCurrentLocation(playFromCurrentLocation); @@ -93,4 +97,4 @@ Messages.messageReceived.connect(function (channel, message, senderID) { } }); -Script.update.connect(update); \ No newline at end of file +Script.update.connect(update); diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index ba1c8b0393..bda4edc125 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -55,13 +55,13 @@ function setupToolBar() { } Tool.IMAGE_HEIGHT /= 2; Tool.IMAGE_WIDTH /= 2; - + toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); - + toolBar.onMove = onToolbarMove; toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); - + recordIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "recording-record.svg", subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, @@ -71,7 +71,7 @@ function setupToolBar() { alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON, visible: true }, true, !Recording.isRecording()); - + var playLoopWidthFactor = 1.65; playIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "play-pause.svg", @@ -80,7 +80,7 @@ function setupToolBar() { alpha: (Recording.isRecording() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, visible: true }, false); - + playLoopIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "play-and-loop.svg", subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, @@ -89,10 +89,10 @@ function setupToolBar() { alpha: (Recording.isRecording() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, visible: true }, false); - + timerOffset = toolBar.width + ToolBar.SPACING; spacing = toolBar.addSpacing(0); - + saveIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "recording-save.svg", width: Tool.IMAGE_WIDTH, @@ -100,7 +100,7 @@ function setupToolBar() { alpha: (Recording.isRecording() || Recording.isPlaying() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, visible: true }, false); - + loadIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "recording-upload.svg", width: Tool.IMAGE_WIDTH, @@ -153,10 +153,10 @@ function onToolbarMove(newX, newY, deltaX, deltaY) { x: newX + timerOffset - ToolBar.SPACING, y: newY }); - + slider.x = newX - ToolBar.SPACING; slider.y = newY - slider.h - ToolBar.SPACING; - + Overlays.editOverlay(slider.background, { x: slider.x, y: slider.y @@ -182,13 +182,13 @@ function updateTimer() { width: timerWidth }); toolBar.changeSpacing(timerWidth + ToolBar.SPACING, spacing); - + if (Recording.isRecording()) { slider.pos = 1.0; } else if (Recording.playerLength() > 0) { slider.pos = Recording.playerElapsed() / Recording.playerLength(); } - + Overlays.editOverlay(slider.foreground, { width: slider.pos * slider.w }); @@ -221,7 +221,7 @@ function moveUI() { function mousePressEvent(event) { var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - + if (recordIcon === toolBar.clicked(clickedOverlay, false) && !Recording.isPlaying()) { if (!Recording.isRecording()) { Recording.startRecording(); @@ -281,8 +281,13 @@ function mousePressEvent(event) { if (!Recording.isRecording() && !Recording.isPlaying()) { recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { - Recording.loadRecording(recordingFile); - setDefaultPlayerOptions(); + Recording.loadRecording(recordingFile, function(success) { + if (success) { + setDefaultPlayerOptions(); + } else { + print("Failed to load recording from " + recordingFile); + } + }); } if (Recording.playerLength() > 0) { toolBar.setAlpha(ALPHA_ON, playIcon); @@ -323,7 +328,7 @@ function update() { } updateTimer(); - + if (watchStop && !Recording.isPlaying()) { watchStop = false; toolBar.setAlpha(ALPHA_ON, recordIcon);