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 }