1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 //    * Redistributions of source code must retain the above copyright notice,
11 //      this list of conditions and the following disclaimer.
12 //
13 //    * Redistributions in binary form must reproduce the above copyright
14 //      notice, this list of conditions and the following disclaimer in the
15 //      documentation and/or other materials provided with the distribution.
16 //
17 //    * Neither the name of the author nor the names of other contributors may
18 //      be used to endorse or promote products derived from this software
19 //      without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34 
35 #include <pch.hpp>
36 
37 #include <musikcore/musikcore_c.h>
38 #include <musikcore/c_context.h>
39 #include <musikcore/debug.h>
40 #include <musikcore/runtime/MessageQueue.h>
41 #include <musikcore/runtime/Message.h>
42 #include <musikcore/library/LibraryFactory.h>
43 #include <musikcore/library/LocalLibrary.h>
44 #include <musikcore/audio/PlaybackService.h>
45 #include <musikcore/plugin/Plugins.h>
46 #include <musikcore/library/LocalMetadataProxy.h>
47 #include <musikcore/support/PreferenceKeys.h>
48 
49 #pragma warning(push, 0)
50 #include <boost/locale.hpp>
51 #include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
52 #include <boost/filesystem.hpp>
53 #pragma warning(pop)
54 
55 #include <thread>
56 
57 using namespace musik;
58 using namespace musik::core;
59 using namespace musik::core::library;;
60 using namespace musik::core::library::query;
61 using namespace musik::core::audio;
62 using namespace musik::core::sdk;
63 using namespace musik::core::runtime;
64 
65 /*
66  * mcsdk_context_message_queue
67  */
68 
mcsdk_context_message_queue()69 mcsdk_context_message_queue::mcsdk_context_message_queue(): MessageQueue() {
70     this->quit = false;
71 }
72 
~mcsdk_context_message_queue()73 mcsdk_context_message_queue::~mcsdk_context_message_queue() {
74 }
75 
Quit()76 void mcsdk_context_message_queue::Quit() {
77     {
78         LockT lock(this->mutex);
79         this->quit = true;
80     }
81     this->Post(Message::Create(0, 0, 0, 0));
82 }
83 
Run()84 void mcsdk_context_message_queue::Run() {
85     while (true) {
86         this->WaitAndDispatch();
87         {
88             LockT lock(this->mutex);
89             if (this->quit) {
90                 return;
91             }
92         }
93     }
94 }
95 
96 /*
97  * globals
98  */
99 
100 static std::recursive_mutex global_mutex;
101 static bool environment_initialized = false;
102 static mcsdk_context* plugin_context = nullptr;
103 static mcsdk_context_message_queue* message_queue = nullptr;
104 static std::thread message_queue_thread;
105 
106 /*
107  * mcsdk_svc_indexer_callback_proxy
108  */
109 
110 struct mcsdk_svc_indexer_callback_proxy: public sigslot::has_slots<> {
111     mcsdk_svc_indexer_context_internal* context;
112 
mcsdk_svc_indexer_callback_proxymcsdk_svc_indexer_callback_proxy113     mcsdk_svc_indexer_callback_proxy(mcsdk_svc_indexer_context_internal* context) {
114         this->context = context;
115     }
116 
on_startedmcsdk_svc_indexer_callback_proxy117     void on_started() {
118         for (auto cb : context->callbacks) {
119             if (cb->on_started) {
120                 cb->on_started(mcsdk_svc_indexer { context });
121             }
122         }
123     }
124 
on_finishedmcsdk_svc_indexer_callback_proxy125     void on_finished(int tracks_processed) {
126         for (auto cb : context->callbacks) {
127             if (cb->on_finished) {
128                 cb->on_finished(mcsdk_svc_indexer { context }, tracks_processed);
129             }
130         }
131     }
132 
on_progressmcsdk_svc_indexer_callback_proxy133     void on_progress(int tracks_processed) {
134         for (auto cb : context->callbacks) {
135             if (cb->on_progress) {
136                 cb->on_progress(mcsdk_svc_indexer { context }, tracks_processed);
137             }
138         }
139     }
140 };
141 
mcsdk_env_init()142 mcsdk_export void mcsdk_env_init() {
143     std::unique_lock<std::recursive_mutex> lock(global_mutex);
144 
145     if (!environment_initialized) {
146         std::locale locale = std::locale();
147         std::locale utf8Locale(locale, new boost::filesystem::detail::utf8_codecvt_facet);
148         boost::filesystem::path::imbue(utf8Locale);
149         debug::Start();
150         message_queue = new mcsdk_context_message_queue();
151         message_queue_thread = std::thread([]{ /* needs to be last */
152             ::message_queue->Run();
153         });
154         environment_initialized = true;
155     }
156 }
157 
mcsdk_env_release()158 mcsdk_export void mcsdk_env_release() {
159     if (environment_initialized) {
160         LibraryFactory::Instance().Shutdown();
161         debug::Stop();
162         message_queue->Quit();
163         message_queue_thread.join();
164         delete message_queue;
165         message_queue = nullptr;
166         environment_initialized = false;
167     }
168 }
169 
170 /*
171  * mcsdk_context_*
172  */
173 
mcsdk_context_init(mcsdk_context ** context)174 mcsdk_export void mcsdk_context_init(mcsdk_context** context) {
175     std::unique_lock<std::recursive_mutex> lock(global_mutex);
176 
177     if (!environment_initialized) {
178         mcsdk_env_init();
179     }
180 
181     plugin::Init();
182 
183     auto c = new mcsdk_context();
184     memset(c, 0, sizeof(mcsdk_context));
185 
186     auto internal = new mcsdk_context_internal();
187 
188     LibraryFactory::Initialize(*message_queue);
189     internal->library = LibraryFactory::Instance().DefaultLocalLibrary();
190     internal->playback = new PlaybackService(*message_queue, internal->library);
191     internal->metadata = new LocalMetadataProxy(internal->library);
192     internal->preferences = Preferences::ForComponent(prefs::components::Settings);
193 
194     c->internal.opaque = internal;
195     c->metadata.opaque = internal->metadata;
196     c->preferences.opaque = internal->preferences.get();
197     c->playback.opaque = internal->playback;
198     c->library.opaque = internal->library.get();
199 
200     auto localLibrary = dynamic_cast<LocalLibrary*>(internal->library.get());
201     if (localLibrary) {
202         c->db.opaque = localLibrary;
203     }
204 
205     auto indexer = internal->library->Indexer();
206     auto indexer_internal = new mcsdk_svc_indexer_context_internal();
207     indexer_internal->indexer = indexer;
208     indexer_internal->callback_proxy = new mcsdk_svc_indexer_callback_proxy(indexer_internal);
209     indexer->Started.connect(indexer_internal->callback_proxy, &mcsdk_svc_indexer_callback_proxy::on_started);
210     indexer->Progress.connect(indexer_internal->callback_proxy, &mcsdk_svc_indexer_callback_proxy::on_progress);
211     indexer->Finished.connect(indexer_internal->callback_proxy, &mcsdk_svc_indexer_callback_proxy::on_finished);
212     c->indexer.opaque = indexer_internal;
213 
214     if (!plugin_context) {
215         mcsdk_set_plugin_context(c);
216     }
217 
218     *context = c;
219 }
220 
mcsdk_context_release(mcsdk_context ** context)221 mcsdk_export void mcsdk_context_release(mcsdk_context** context) {
222     std::unique_lock<std::recursive_mutex> lock(global_mutex);
223 
224     auto c = *context;
225     auto internal = static_cast<mcsdk_context_internal*>(c->internal.opaque);
226 
227     delete internal->playback;
228 
229     internal->playback = nullptr;
230     internal->library->Indexer()->Stop();
231     internal->library.reset();
232     internal->preferences.reset();
233 
234     delete internal->metadata;
235 
236     auto indexer_internal = static_cast<mcsdk_svc_indexer_context_internal*>(c->indexer.opaque);
237     delete indexer_internal->callback_proxy;
238     delete indexer_internal;
239 
240     delete internal;
241 
242     if (plugin_context == c) {
243         mcsdk_set_plugin_context(nullptr);
244     }
245 
246     delete c;
247 
248     *context = 0;
249 }
250 
mcsdk_set_plugin_context(mcsdk_context * context)251 mcsdk_export void mcsdk_set_plugin_context(mcsdk_context* context) {
252     if (plugin_context && plugin_context != context) {
253         plugin::Deinit();
254     }
255     plugin_context = context;
256     if (plugin_context) {
257         auto internal = static_cast<mcsdk_context_internal*>(context->internal.opaque);
258         plugin::Start(message_queue, internal->playback, internal->library);
259     }
260 }
261 
mcsdk_is_plugin_context(mcsdk_context * context)262 mcsdk_export bool mcsdk_is_plugin_context(mcsdk_context* context) {
263     return context && context == plugin_context;
264 }