diff --git a/src/musikcore/audio/Visualizer.cpp b/src/musikcore/audio/Visualizer.cpp index 7d7f1e78a..7802269f3 100644 --- a/src/musikcore/audio/Visualizer.cpp +++ b/src/musikcore/audio/Visualizer.cpp @@ -102,6 +102,10 @@ namespace musik { } } + void Shutdown() { + HideSelectedVisualizer(); + } + ISpectrumVisualizer* SpectrumVisualizer() { return spectrumVisualizer; } diff --git a/src/musikcore/audio/Visualizer.h b/src/musikcore/audio/Visualizer.h index 735c2c1d0..c25bf343b 100644 --- a/src/musikcore/audio/Visualizer.h +++ b/src/musikcore/audio/Visualizer.h @@ -48,5 +48,6 @@ namespace musik { namespace core { namespace audio { namespace vis { void SetSelectedVisualizer(std::shared_ptr visualizer); std::shared_ptr SelectedVisualizer(); void HideSelectedVisualizer(); + void Shutdown(); } } } } diff --git a/src/musikcore/c_context.cpp b/src/musikcore/c_context.cpp index 234d0ab53..44cf28174 100644 --- a/src/musikcore/c_context.cpp +++ b/src/musikcore/c_context.cpp @@ -158,7 +158,7 @@ mcsdk_export void mcsdk_env_init() { mcsdk_export void mcsdk_env_release() { if (environment_initialized) { LibraryFactory::Instance().Shutdown(); - debug::Stop(); + debug::Shutdown(); message_queue->Quit(); message_queue_thread.join(); delete message_queue; @@ -227,7 +227,7 @@ mcsdk_export void mcsdk_context_release(mcsdk_context** context) { delete internal->playback; internal->playback = nullptr; - internal->library->Indexer()->Stop(); + internal->library->Indexer()->Shutdown(); internal->library.reset(); internal->preferences.reset(); @@ -250,7 +250,7 @@ mcsdk_export void mcsdk_context_release(mcsdk_context** context) { mcsdk_export void mcsdk_set_plugin_context(mcsdk_context* context) { if (plugin_context && plugin_context != context) { - plugin::Deinit(); + plugin::Shutdown(); } plugin_context = context; if (plugin_context) { diff --git a/src/musikcore/c_interface_wrappers.cpp b/src/musikcore/c_interface_wrappers.cpp index 5d6d13132..1e96268c4 100644 --- a/src/musikcore/c_interface_wrappers.cpp +++ b/src/musikcore/c_interface_wrappers.cpp @@ -1243,7 +1243,7 @@ mcsdk_export void mcsdk_svc_indexer_schedule(mcsdk_svc_indexer in, mcsdk_svc_ind } mcsdk_export void mcsdk_svc_indexer_stop(mcsdk_svc_indexer in) { - INDEXER(in)->Stop(); + INDEXER(in)->Shutdown(); } mcsdk_export mcsdk_svc_indexer_state mcsdk_svc_indexer_get_state(mcsdk_svc_indexer in) { diff --git a/src/musikcore/debug.cpp b/src/musikcore/debug.cpp index be3b78a4d..ad438b732 100755 --- a/src/musikcore/debug.cpp +++ b/src/musikcore/debug.cpp @@ -182,7 +182,7 @@ void musik::debug::Start(std::vector backends) { info("LOG SESSION", "---------- START ----------"); } -void musik::debug::Stop() { +void musik::debug::Shutdown() { std::unique_lock lock(mutex); cancel = true; diff --git a/src/musikcore/debug.h b/src/musikcore/debug.h index 44ce92b7b..271b29a7e 100755 --- a/src/musikcore/debug.h +++ b/src/musikcore/debug.h @@ -83,7 +83,7 @@ namespace musik { }; static void Start(std::vector backends = { new SimpleFileBackend() }); - static void Stop(); + static void Shutdown(); static void verbose(const std::string& tag, const std::string& string) noexcept; static void v(const std::string& tag, const std::string& string) noexcept; diff --git a/src/musikcore/library/IIndexer.h b/src/musikcore/library/IIndexer.h index 9dd698036..d7d54ba5e 100755 --- a/src/musikcore/library/IIndexer.h +++ b/src/musikcore/library/IIndexer.h @@ -64,7 +64,7 @@ namespace musik { namespace core { virtual void RemovePath(const std::string& path) = 0; virtual void GetPaths(std::vector& paths) = 0; virtual void Schedule(SyncType type) = 0; - virtual void Stop() = 0; + virtual void Shutdown() = 0; virtual State GetState() = 0; }; } } diff --git a/src/musikcore/library/Indexer.cpp b/src/musikcore/library/Indexer.cpp index d3eb26d79..abee59817 100644 --- a/src/musikcore/library/Indexer.cpp +++ b/src/musikcore/library/Indexer.cpp @@ -140,10 +140,10 @@ Indexer::Indexer(const std::string& libraryPath, const std::string& dbFilename) Indexer::~Indexer() { closeLogFile(); - this->Stop(); + this->Shutdown(); } -void Indexer::Stop() { +void Indexer::Shutdown() { if (this->thread) { { boost::mutex::scoped_lock lock(this->stateMutex); diff --git a/src/musikcore/library/Indexer.h b/src/musikcore/library/Indexer.h index 03e4703b5..6fd967fbf 100644 --- a/src/musikcore/library/Indexer.h +++ b/src/musikcore/library/Indexer.h @@ -80,7 +80,7 @@ namespace musik { namespace core { void RemovePath(const std::string& path) override; void GetPaths(std::vector& paths) override; void Schedule(SyncType type) override; - void Stop() override; + void Shutdown() override; State GetState() noexcept override { return this->state; diff --git a/src/musikcore/library/RemoteLibrary.cpp b/src/musikcore/library/RemoteLibrary.cpp index af9abd926..4c9c9ab9a 100644 --- a/src/musikcore/library/RemoteLibrary.cpp +++ b/src/musikcore/library/RemoteLibrary.cpp @@ -72,7 +72,7 @@ class NullIndexer: public musik::core::IIndexer { void RemovePath(const std::string& path) noexcept override { } void GetPaths(std::vector& paths) noexcept override { } void Schedule(SyncType type) noexcept override { } - void Stop() noexcept override { } + void Shutdown() noexcept override { } State GetState() noexcept override { return StateIdle; } } kNullIndexer; diff --git a/src/musikcore/net/PiggyWebSocketClient.cpp b/src/musikcore/net/PiggyWebSocketClient.cpp index 5ce593445..3d65487f3 100644 --- a/src/musikcore/net/PiggyWebSocketClient.cpp +++ b/src/musikcore/net/PiggyWebSocketClient.cpp @@ -49,6 +49,10 @@ using ClientPtr = PiggyWebSocketClient::ClientPtr; using ClientMessage = PiggyWebSocketClient::ClientMessage; using Connection = PiggyWebSocketClient::Connection; using Message = PiggyWebSocketClient::Message; +using MessageQueue = PiggyWebSocketClient::MessageQueue; + +std::recursive_mutex instanceMutex; +static std::shared_ptr instance; static const int64_t kLatencyTimeoutMs = INT_MAX; static const int64_t kReconnectIntervalMs = 10000; @@ -60,7 +64,21 @@ static inline std::string generateSessionId() { return "musikcube-" + std::to_string((unsigned long) time(nullptr)); } -PiggyWebSocketClient::PiggyWebSocketClient(IMessageQueue* messageQueue) +std::shared_ptr PiggyWebSocketClient::Instance(MessageQueue* messageQueue) { + std::unique_lock lock(instanceMutex); + if (!instance) { + instance = std::shared_ptr(new PiggyWebSocketClient(messageQueue)); + } + instance->SetMessageQueue(messageQueue); + return instance; +} + +void PiggyWebSocketClient::Shutdown() { + std::unique_lock lock(instanceMutex); + instance.reset(); +} + +PiggyWebSocketClient::PiggyWebSocketClient(MessageQueue* messageQueue) : messageQueue(nullptr) , sessionId(generateSessionId()) { this->SetMessageQueue(messageQueue); @@ -157,7 +175,11 @@ void PiggyWebSocketClient::Connect(const std::string& host, unsigned short port, void PiggyWebSocketClient::Reconnect() { std::unique_lockmutex)> lock(this->mutex); + /* Disconnect() will reset the internal URI to implicitly disable auto-reconnect; + go ahead and cache it, disconnect, then restore it. */ + auto originalUri = this->uri; this->Disconnect(); + this->uri = originalUri; #if BOOST_VERSION < 106600 io.reset(); @@ -195,6 +217,7 @@ void PiggyWebSocketClient::Disconnect() { { std::unique_lockmutex)> lock(this->mutex); oldThread = std::unique_ptr(std::move(this->thread)); + this->uri = ""; } if (oldThread) { @@ -235,7 +258,8 @@ void PiggyWebSocketClient::SetState(State state) { } } -void PiggyWebSocketClient::SetMessageQueue(IMessageQueue* messageQueue) { +void PiggyWebSocketClient::SetMessageQueue(MessageQueue* messageQueue) { + std::unique_lockmutex)> lock(this->mutex); if (messageQueue == this->messageQueue) { return; } @@ -253,7 +277,7 @@ void PiggyWebSocketClient::SetMessageQueue(IMessageQueue* messageQueue) { void PiggyWebSocketClient::ProcessMessage(IMessage& message) { if (message.Type() == kReconnectMessage) { std::unique_lockmutex)> lock(this->mutex); - if (this->state == State::Disconnected) { + if (this->state == State::Disconnected && this->uri.length()) { this->Reconnect(); } this->messageQueue->Post(runtime::Message::Create(this, kReconnectMessage), kReconnectIntervalMs); diff --git a/src/musikcore/net/PiggyWebSocketClient.h b/src/musikcore/net/PiggyWebSocketClient.h index 1038206a7..4456cb6d9 100644 --- a/src/musikcore/net/PiggyWebSocketClient.h +++ b/src/musikcore/net/PiggyWebSocketClient.h @@ -57,6 +57,7 @@ namespace musik { namespace core { namespace net { using ClientMessage = websocketpp::config::asio_client::message_type::ptr; using Connection = websocketpp::connection_hdl; using Message = std::shared_ptr; + using MessageQueue = musik::core::runtime::IMessageQueue; enum class State: int { Disconnected = 0, @@ -73,7 +74,9 @@ namespace musik { namespace core { namespace net { sigslot::signal3 StateChanged; - PiggyWebSocketClient(musik::core::runtime::IMessageQueue* messageQueue); + static std::shared_ptr Instance(MessageQueue* messageQueue); + static void Shutdown(); + PiggyWebSocketClient(const PiggyWebSocketClient&) = delete; virtual ~PiggyWebSocketClient(); @@ -87,9 +90,12 @@ namespace musik { namespace core { namespace net { ConnectionError LastConnectionError() const; std::string Uri() const; - void SetMessageQueue(musik::core::runtime::IMessageQueue* messageQueue); + void SetMessageQueue(MessageQueue* messageQueue); void ProcessMessage(musik::core::runtime::IMessage& message) override; + protected: + PiggyWebSocketClient(MessageQueue* messageQueue); + private: void SetState(State state); void SetDisconnected(ConnectionError errorCode); @@ -107,7 +113,7 @@ namespace musik { namespace core { namespace net { std::atomic quit{ false }; ConnectionError connectionError{ ConnectionError::None }; State state{ State::Disconnected }; - musik::core::runtime::IMessageQueue* messageQueue; + MessageQueue* messageQueue; }; } } } diff --git a/src/musikcore/plugin/Plugins.cpp b/src/musikcore/plugin/Plugins.cpp index 113e23112..dc2a6069b 100644 --- a/src/musikcore/plugin/Plugins.cpp +++ b/src/musikcore/plugin/Plugins.cpp @@ -412,7 +412,7 @@ namespace musik { namespace core { namespace plugin { return environment; } - void Deinit() { + void Shutdown() { /* preferences */ Preferences::SavePluginPreferences(); diff --git a/src/musikcore/plugin/Plugins.h b/src/musikcore/plugin/Plugins.h index 3658ea35e..cc6966204 100644 --- a/src/musikcore/plugin/Plugins.h +++ b/src/musikcore/plugin/Plugins.h @@ -50,6 +50,6 @@ namespace musik { namespace core { namespace plugin { musik::core::sdk::IEnvironment& Environment(); - void Deinit(); + void Shutdown(); } } } diff --git a/src/musikcube/Main.cpp b/src/musikcube/Main.cpp index 0217a507c..80d04ed7e 100644 --- a/src/musikcube/Main.cpp +++ b/src/musikcube/Main.cpp @@ -125,7 +125,7 @@ int main(int argc, char* argv[]) { }; if (prefs->GetBool(core::prefs::keys::PiggyEnabled, false)) { - auto piggyClient = std::make_shared(&Window::MessageQueue()); + auto piggyClient = PiggyWebSocketClient::Instance(&Window::MessageQueue()); piggyClient->Connect(prefs->GetString(core::prefs::keys::PiggyHostname, "localhost")); debuggerBackends.push_back(new PiggyDebugBackend(piggyClient)); } @@ -226,21 +226,20 @@ int main(int argc, char* argv[]) { app.Run(mainLayout); /* done with the app */ - mainLayout->Stop(); + mainLayout->Shutdown(); #ifdef WIN32 win32::HideMainWindow(); #endif - library->Indexer()->Stop(); + library->Indexer()->Shutdown(); } - audio::vis::HideSelectedVisualizer(); - plugin::Deinit(); - + PiggyWebSocketClient::Shutdown(); LibraryFactory::Instance().Shutdown(); - - debug::Stop(); + vis::Shutdown(); + plugin::Shutdown(); + debug::Shutdown(); return 0; } diff --git a/src/musikcube/app/layout/MainLayout.cpp b/src/musikcube/app/layout/MainLayout.cpp index fbbb66117..0519cc33e 100755 --- a/src/musikcube/app/layout/MainLayout.cpp +++ b/src/musikcube/app/layout/MainLayout.cpp @@ -212,7 +212,7 @@ void MainLayout::Start() { MessageQueue().RegisterForBroadcasts(this->shared_from_this()); } -void MainLayout::Stop() { +void MainLayout::Shutdown() { MessageQueue().UnregisterForBroadcasts(this); } diff --git a/src/musikcube/app/layout/MainLayout.h b/src/musikcube/app/layout/MainLayout.h index 5e4b3a7b1..b0a5ec7fe 100755 --- a/src/musikcube/app/layout/MainLayout.h +++ b/src/musikcube/app/layout/MainLayout.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -66,7 +67,7 @@ namespace musik { virtual ~MainLayout(); void Start(); - void Stop(); + void Shutdown(); /* IWindow */ bool KeyPress(const std::string& key) override; diff --git a/src/musikcube/app/layout/SettingsLayout.cpp b/src/musikcube/app/layout/SettingsLayout.cpp index 6ddf8671e..054cae669 100755 --- a/src/musikcube/app/layout/SettingsLayout.cpp +++ b/src/musikcube/app/layout/SettingsLayout.cpp @@ -70,6 +70,7 @@ using namespace musik::core; using namespace musik::core::audio; using namespace musik::core::library::constants; using namespace musik::core::sdk; +using namespace musik::core::net; using namespace musik::cube; using namespace cursespp; @@ -165,6 +166,8 @@ SettingsLayout::SettingsLayout( , indexer(library->Indexer()) , playback(playback) { this->prefs = Preferences::ForComponent(core::prefs::components::Settings); + this->piggyClient = PiggyWebSocketClient::Instance(&MessageQueue()); + this->piggyClient->StateChanged.connect(this, &SettingsLayout::OnPiggyClientStateChange); this->UpdateServerAvailability(); this->InitializeWindows(); } @@ -173,6 +176,18 @@ SettingsLayout::~SettingsLayout() { updateCheck.Cancel(); } +void SettingsLayout::OnPiggyClientStateChange( + PiggyWebSocketClient* client, + PiggyWebSocketClient::State newState, + PiggyWebSocketClient::State oldState) +{ + /* trigger a redraw on the main thread */ + using State = PiggyWebSocketClient::State; + if (newState == State::Connected || newState == State::Disconnected) { + this->Post(core::message::EnvironmentUpdated); + } +} + void SettingsLayout::OnCheckboxChanged(cursespp::Checkbox* cb, bool checked) { if (cb == syncOnStartupCheckbox.get()) { this->prefs->SetBool(core::prefs::keys::SyncOnStartup, checked); @@ -451,8 +466,6 @@ void SettingsLayout::InitializeWindows() { this->appVersion = std::make_shared(); this->appVersion->SetContentColor(Color::TextDisabled); this->appVersion->SetAlignment(text::AlignCenter); - std::string version = u8fmt("%s %s", VERSION, VERSION_COMMIT_HASH); - this->appVersion->SetText(u8fmt(_TSTR("console_version"), version.c_str())); int order = 0; this->libraryTypeDropdown->SetFocusOrder(order++); @@ -639,9 +652,23 @@ void SettingsLayout::LoadPreferences() { this->localLibraryLayout->LoadPreferences(); this->remoteLibraryLayout->LoadPreferences(); + /* version, status */ + std::string piggyStatus = ""; + if (this->piggyAvailable) { + if (this->piggyClient->ConnectionState() == PiggyWebSocketClient::State::Connected) { + piggyStatus = " (oo)"; + } + else { + piggyStatus = " (..)"; + } + } + std::string version = u8fmt("%s %s%s", VERSION, VERSION_COMMIT_HASH, piggyStatus.c_str()); + this->appVersion->SetText(u8fmt(_TSTR("console_version"), version.c_str())); + this->Layout(); } void SettingsLayout::UpdateServerAvailability() { this->serverAvailable = !!ServerOverlay::FindServerPlugin().get(); + this->piggyAvailable = this->prefs->GetBool(core::prefs::keys::PiggyEnabled, false); } diff --git a/src/musikcube/app/layout/SettingsLayout.h b/src/musikcube/app/layout/SettingsLayout.h index f050ace04..21288e338 100755 --- a/src/musikcube/app/layout/SettingsLayout.h +++ b/src/musikcube/app/layout/SettingsLayout.h @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,7 @@ namespace musik { namespace cube { { public: using MasterLibraryPtr = std::shared_ptr; + using PiggyWebSocketClient = musik::core::net::PiggyWebSocketClient; DELETE_COPY_AND_ASSIGNMENT_DEFAULTS(SettingsLayout) @@ -110,6 +112,11 @@ namespace musik { namespace cube { void OnLastFmDropdownActivate(cursespp::TextLabel* label); void OnAdvancedSettingsActivate(cursespp::TextLabel* label); + void OnPiggyClientStateChange( + PiggyWebSocketClient* client, + PiggyWebSocketClient::State newState, + PiggyWebSocketClient::State oldState); + cursespp::App& app; MasterLibraryPtr library; musik::core::IIndexer* indexer; @@ -143,8 +150,10 @@ namespace musik { namespace cube { std::shared_ptr localLibraryLayout; std::shared_ptr remoteLibraryLayout; + std::shared_ptr piggyClient; UpdateCheck updateCheck; bool serverAvailable = false; + bool piggyAvailable = false; }; } } diff --git a/src/musikcubed/main.cpp b/src/musikcubed/main.cpp index 5e0b2d5a3..fbe9a7201 100644 --- a/src/musikcubed/main.cpp +++ b/src/musikcubed/main.cpp @@ -298,10 +298,10 @@ int main(int argc, char** argv) { messageQueue.Run(); - library->Indexer()->Stop(); + library->Indexer()->Shutdown(); } - plugin::Deinit(); + plugin::Shutdown(); remove(getLockfileFn().c_str()); } diff --git a/src/plugins/taglib_plugin/TaglibMetadataReader.cpp b/src/plugins/taglib_plugin/TaglibMetadataReader.cpp index 4bdb849ff..1e805bd7e 100644 --- a/src/plugins/taglib_plugin/TaglibMetadataReader.cpp +++ b/src/plugins/taglib_plugin/TaglibMetadataReader.cpp @@ -460,7 +460,7 @@ void TaglibMetadataReader::ReadBasicData(const T* tag, const char* uri, ITagStor } this->SetTagValue("album", tag->album(), target); - this->SetSlashSeparatedValues("artist", tag->artist(), target); + this->SetTagValue("artist", tag->artist(), target); this->SetTagValue("genre", tag->genre(), target); this->SetTagValue("comment", tag->comment(), target); @@ -638,7 +638,7 @@ bool TaglibMetadataReader::ReadID3V2(TagLib::ID3v2::Tag *id3v2, ITagStore *track this->SetTagValues("org.writer", allTags["TOLY"], track); this->SetSlashSeparatedValues("publisher", allTags["TPUB"], track); this->SetTagValues("mood", allTags["TMOO"], track); - this->SetSlashSeparatedValues("org.artist", allTags["TOPE"], track); + this->SetTagValues("org.artist", allTags["TOPE"], track); this->SetTagValues("language", allTags["TLAN"], track); this->SetTagValues("lyrics", allTags["USLT"], track); this->SetTagValues("disc", allTags["TPOS"], track); @@ -696,8 +696,8 @@ bool TaglibMetadataReader::ReadID3V2(TagLib::ID3v2::Tag *id3v2, ITagStore *track /* artists */ - this->SetSlashSeparatedValues("artist", allTags["TPE1"], track); - this->SetSlashSeparatedValues("album_artist", allTags["TPE2"], track); + this->SetTagValues("artist", allTags["TPE1"], track); + this->SetTagValues("album_artist", allTags["TPE2"], track); this->SetSlashSeparatedValues("conductor", allTags["TPE3"], track); this->SetSlashSeparatedValues("interpreted", allTags["TPE4"], track); @@ -707,7 +707,7 @@ bool TaglibMetadataReader::ReadID3V2(TagLib::ID3v2::Tag *id3v2, ITagStore *track TagLib::ID3v2::FrameList::Iterator it = comments.begin(); for (; it != comments.end(); ++it) { - TagLib::ID3v2::CommentsFrame *comment + const TagLib::ID3v2::CommentsFrame *comment = dynamic_cast (*it); TagLib::String temp = comment->description(); @@ -738,7 +738,7 @@ bool TaglibMetadataReader::ReadID3V2(TagLib::ID3v2::Tag *id3v2, ITagStore *track static_cast(pictures.front()); TagLib::ByteVector pictureData = picture->picture(); - long long size = pictureData.size(); + const long long size = pictureData.size(); if (size > 32) { /* noticed that some id3tags have like a 4-8 byte size with no thumbnail */ track->SetThumbnail(pictureData.data(), size); @@ -800,7 +800,7 @@ void TaglibMetadataReader::SetTagValues( void TaglibMetadataReader::SetSlashSeparatedValues( const char* key, TagLib::String tagString, ITagStore *track) { - if(!tagString.isEmpty()) { + if (!tagString.isEmpty()) { std::string value(tagString.to8Bit(true)); std::vector splitValues = str::split(value, "/"); std::vector::iterator it = splitValues.begin(); @@ -815,7 +815,7 @@ void TaglibMetadataReader::SetSlashSeparatedValues( const TagLib::ID3v2::FrameList &frame, ITagStore *track) { - if(!frame.isEmpty()) { + if (!frame.isEmpty()) { TagLib::ID3v2::FrameList::ConstIterator value = frame.begin(); for ( ; value != frame.end(); ++value) { TagLib::String tagString = (*value)->toString();