1 #include "wakeup_engine_manager.h"
2 #include "wakeup_manager_main.h"
6 #include <pkgmgr-info.h>
9 namespace multiassistant
14 /* Need to check whether this value needs to be configurable */
15 static int g_speech_pcm_wait_count = 400;
17 /* Utility function for checking if an element exists in a container */
18 template<class C, class T>
19 static auto contains(const C& v, const T& x) -> decltype(end(v), true)
21 return end(v) != find(begin(v), end(v), x);
24 CWakeupEngineManager::CWakeupEngineManager()
28 CWakeupEngineManager::~CWakeupEngineManager()
32 CWakeupEngineManager::CWakeupEngineManager(IEngineEventObserver *observer) : CWakeupEngineManager()
37 void CWakeupEngineManager::initialize()
39 DIR* dp = opendir(MA_WAKEUP_ENGINE_PATH);
41 MWR_LOGD("Failed opening directory : %s", (const char*)MA_WAKEUP_ENGINE_PATH);
43 struct dirent *dirp = nullptr;
44 char dirpath[_POSIX_PATH_MAX];
48 if (nullptr != dirp) {
49 const string current_directory{"."};
50 const string parent_directory{".."};
51 if (0 == current_directory.compare(dirp->d_name) ||
52 0 == parent_directory.compare(dirp->d_name))
55 if (DT_DIR != dirp->d_type) /* If not a directory */
58 int dirpath_len = strlen(MA_WAKEUP_ENGINE_PATH) + strlen(dirp->d_name) + 1;
59 if (dirpath_len >= _POSIX_PATH_MAX) {
60 MWR_LOGD("File path is too long : %s", dirp->d_name);
65 memset(dirpath, '\0', _POSIX_PATH_MAX);
66 snprintf(dirpath, _POSIX_PATH_MAX, "%s/%s",
67 (const char*)(MA_WAKEUP_ENGINE_PATH), dirp->d_name);
69 add_engine_directory(string{dirp->d_name}, dirpath);
71 } while (nullptr != dirp);
77 void CWakeupEngineManager::deinitialize()
79 for (auto& info : mEngineInfo) {
80 if (info.interface.set_wakeup_event_callback) {
81 info.interface.set_wakeup_event_callback(nullptr, nullptr);
83 if (info.interface.set_speech_status_callback) {
84 info.interface.set_speech_status_callback(nullptr, nullptr);
86 if (info.interface.set_error_callback) {
87 info.interface.set_error_callback(nullptr, nullptr);
89 if (info.interface.set_audio_data_require_status_callback) {
90 info.interface.set_audio_data_require_status_callback(nullptr, nullptr);
92 if (info.interface.deinitialize) {
93 info.interface.deinitialize();
95 if (info.engine_handle) {
96 dlclose(info.engine_handle);
97 info.engine_handle = nullptr;
100 mSelectedEngine = nullptr;
104 void CWakeupEngineManager::subscribe(IEngineEventObserver *observer)
106 mObservers.push_back(observer);
107 MWR_LOGD("Added Observer : %p %zu", observer, mObservers.size());
110 void CWakeupEngineManager::unsubscribe(IEngineEventObserver *observer)
112 auto iter = find(mObservers.begin(), mObservers.end(), observer);
113 if (iter != mObservers.end()) {
114 mObservers.erase(iter);
118 bool CWakeupEngineManager::get_audio_data_required()
120 return mAudioDataRequired;
123 void CWakeupEngineManager::set_selected_wakeup_info(wakeup_event_info wakeup_info)
125 mSelectedEngine = nullptr;
126 for (const auto& info : mEngineInfo) {
127 string appid = string{wakeup_info.wakeup_appid};
128 bool found = contains(info.assistant_list, appid);
131 mSelectedEngine = &info;
132 MWR_LOGD("Selected : %s", info.engine_name.c_str());
137 bool CWakeupEngineManager::set_language(string language)
139 for (const auto& info : mEngineInfo) {
140 if (info.interface.set_language) {
141 info.interface.set_language(language.c_str());
147 void CWakeupEngineManager::set_assistant_activated(string appid, bool activated)
149 MWR_LOGD("[ENTER] : %s %d", appid.c_str(), activated);
150 for (auto& info : mEngineInfo) {
151 const auto& iter = find_if(info.assistant_list.begin(), info.assistant_list.end(),
152 [appid](const string& assistant) {
153 return (0 == assistant.compare(appid));
156 /* If the appid is in the assistant list */
157 if (info.assistant_list.end() != iter) {
158 bool previously_activated = info.activated;
160 info.activated_assistants.insert(appid);
162 info.activated_assistants.erase(appid);
164 info.activated = (info.activated_assistants.size() > 0);
165 if (previously_activated != info.activated) {
166 if (info.activated) {
167 info.interface.activate();
169 info.interface.deactivate();
171 /* Activated status changed, need to update audio_data_require_status too */
172 on_audio_data_require_status(info.engine_name, info.audio_data_require_status);
178 void CWakeupEngineManager::set_wake_word_audio_require_flag(bool require)
181 mWakeWordAudioRequired = require;
182 for (const auto& info : mEngineInfo) {
183 if (info.interface.set_wake_word_audio_require_flag) {
184 info.interface.set_wake_word_audio_require_flag(require);
189 void CWakeupEngineManager::streaming_speech_data_thread_func()
193 if (nullptr == mSelectedEngine)
196 const wakeup_engine_interface *interface = &(mSelectedEngine->interface);
198 if (NULL == interface ||
199 NULL == interface->get_utterance_data ||
200 NULL == interface->get_utterance_data_count)
203 MWR_LOGD("data_count : %d", interface->get_utterance_data_count());
205 wakeup_speech_data speech_data;
207 bool finish_event_sent = false;
209 if (mWakeWordAudioRequired &&
210 NULL != interface->get_wake_word_data &&
211 NULL != interface->get_wake_word_data_count) {
212 for (const auto& observer : mObservers) {
214 if (!observer->on_audio_streaming_data_section(MA_AUDIO_STREAMING_DATA_SECTION_WAKE_WORD)) {
215 LOGE("[Recorder WARNING] One of the observer returned false");
219 int count = interface->get_wake_word_data_count();
220 while (!(mStopStreamingThread.load()) && index < count) {
221 int ret = interface->get_wake_word_data(index, &speech_data);
223 for (const auto& observer : mObservers) {
225 if (!observer->on_streaming_audio_data(
226 speech_data.event, speech_data.buffer, speech_data.len)) {
227 LOGE("[Recorder WARNING] One of the observer returned false");
237 for (const auto& observer : mObservers) {
239 if (!observer->on_audio_streaming_data_section(MA_AUDIO_STREAMING_DATA_SECTION_UTTERANCE)) {
240 LOGE("[Recorder WARNING] One of the observer returned false");
246 while (!(mStopStreamingThread.load())) {
250 /* get feedback data */
251 if (interface && interface->get_utterance_data) {
252 ret = interface->get_utterance_data(index, &speech_data);
255 MWR_LOGD("[DEBUG] No feedback data. Waiting mode : %d", ret);
258 while (!(mStopStreamingThread.load())) {
259 this_thread::sleep_for(chrono::milliseconds(10));
260 if (index < interface->get_utterance_data_count()) {
261 MWR_LOGI("[INFO] Resume thread");
264 if (g_speech_pcm_wait_count < cnt) {
265 MWR_LOGE("[ERROR] Wrong request, there's no pcm data");
266 for (const auto& observer : mObservers) {
268 if (!observer->on_streaming_audio_data(
269 WAKEUP_SPEECH_STREAMING_EVENT_FAIL, NULL, 0)) {
270 LOGE("[Recorder WARNING] One of the observer returned false");
278 MWR_LOGI("[INFO] Finish to wait for new feedback data come");
280 /* resume feedback thread */
284 for (const auto& observer : mObservers) {
286 if (!observer->on_streaming_audio_data(
287 speech_data.event, speech_data.buffer, speech_data.len)) {
288 LOGE("[Recorder WARNING] One of the observer returned false");
293 if (WAKEUP_SPEECH_STREAMING_EVENT_FINISH == speech_data.event) {
294 MWR_LOGI("[INFO] Finish to get and send speech data");
295 finish_event_sent = true;
303 if (true != finish_event_sent) {
304 unsigned char final_buffer[2] = {'\0', };
305 for (const auto& observer : mObservers) {
307 if (!observer->on_streaming_audio_data(
308 WAKEUP_SPEECH_STREAMING_EVENT_FINISH, final_buffer, sizeof(final_buffer))) {
309 LOGE("[Recorder WARNING] One of the observer returned false");
316 void CWakeupEngineManager::start_streaming_current_utterance_data()
318 if (mStreamingThread.joinable()) {
319 MWR_LOGE("ERROR : mStreamingThread is joinable, will not start a new thread");
322 mStreamingThread = thread(&CWakeupEngineManager::streaming_speech_data_thread_func, this);
325 void CWakeupEngineManager::stop_streaming_current_utterance_data()
327 if (mStreamingThread.joinable()) {
328 MWR_LOGD("mStreamingThread is joinable, trying join()");
329 mStopStreamingThread.store(true);
330 mStreamingThread.join();
332 mStopStreamingThread.store(false);
335 void CWakeupEngineManager::update_manager_state(wakeup_manager_state_e state)
337 for (const auto& info : mEngineInfo) {
338 if (info.interface.update_manager_state) {
339 info.interface.update_manager_state(state);
344 void CWakeupEngineManager::update_recognition_result(string appid, int result)
346 if (mSelectedEngine) {
347 if (mSelectedEngine->interface.update_recognition_result) {
348 mSelectedEngine->interface.update_recognition_result(appid.c_str(), result);
353 void CWakeupEngineManager::engine_add_target_assistant(string engine_name, string appid)
355 const auto& iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
356 [engine_name](const EngineInfo& info) {
357 return (0 == info.engine_name.compare(engine_name));
360 if (mEngineInfo.end() == iter) {
361 /* Not found, add new library */
362 pkgmgrinfo_appinfo_h handle;
363 int ret = pkgmgrinfo_appinfo_get_appinfo(engine_name.c_str(), &handle);
364 if (PMINFO_R_OK == ret) {
365 char *root_path = nullptr;
366 ret = pkgmgrinfo_appinfo_get_root_path(handle, &root_path);
367 if (PMINFO_R_OK == ret && nullptr != root_path) {
368 string path = root_path;
370 path += MA_WAKEUP_DEDICATED_ENGINE_PATH;
371 add_engine(engine_name, path);
373 pkgmgrinfo_appinfo_destroy_appinfo(handle);
375 /* Find again to add appid to the newly created engine's assistant list */
376 const auto &new_iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
377 [engine_name](const EngineInfo& info) {
378 return (0 == info.engine_name.compare(engine_name));
380 if (mEngineInfo.end() != new_iter) {
381 new_iter->assistant_list.push_back(appid);
384 /* If the engine already exists, simply add the appid to the assistant list */
385 iter->assistant_list.push_back(appid);
389 void CWakeupEngineManager::engine_add_wakeup_word(string appid, string wakeup_word, string language)
391 for (const auto& info : mEngineInfo) {
392 bool found = contains(info.assistant_list, appid);
394 if (info.interface.add_wakeup_word) {
395 info.interface.add_wakeup_word(appid.c_str(), wakeup_word.c_str(), language.c_str());
401 void CWakeupEngineManager::engine_set_assistant_specific_command(string appid, string command)
403 for (const auto& info : mEngineInfo) {
404 bool found = contains(info.assistant_list, appid);
406 if (info.interface.set_assistant_specific_command) {
407 info.interface.set_assistant_specific_command(appid.c_str(), command.c_str());
413 void CWakeupEngineManager::engine_feed_audio_data(long time, void* data, int len)
415 for (const auto& info : mEngineInfo) {
416 if (info.activated &&
417 info.audio_data_require_status &&
418 info.interface.feed_audio_data) {
419 int ret = info.interface.feed_audio_data(time, data, len);
421 LOGE("[ERROR] Fail to feed speech data, ret(%d) : %s", ret, info.engine_name.c_str());
427 bool CWakeupEngineManager::on_wakeup_event(string engine_name, wakeup_event_info info)
431 for (const auto& observer : mObservers) {
433 if (!observer->on_wakeup_event(engine_name, info)) {
434 LOGE("[Recorder WARNING] One of the observer returned false");
442 bool CWakeupEngineManager::on_speech_status(string engine_name, wakeup_service_speech_status_e status)
446 for (const auto& observer : mObservers) {
448 if (!observer->on_speech_status(engine_name, status)) {
449 LOGE("[Recorder WARNING] One of the observer returned false");
457 bool CWakeupEngineManager::on_error(string engine_name, int error_code, string error_message)
461 for (const auto& observer : mObservers) {
463 if (!observer->on_error(engine_name, error_code, error_message)) {
464 LOGE("[Recorder WARNING] One of the observer returned false");
472 bool CWakeupEngineManager::on_audio_data_require_status(string engine_name, bool require)
474 MWR_LOGD("[ENTER] %s, %d", engine_name.c_str(), require);
479 for (auto& info : mEngineInfo) {
480 if (info.engine_name.compare(engine_name) == 0) {
482 info.audio_data_require_status = require;
484 if (info.activated && info.audio_data_require_status) {
488 MWR_LOGD("count : %d", count);
490 mAudioDataRequired = true;
492 mAudioDataRequired = false;
496 for (const auto& observer : mObservers) {
498 if (!observer->on_audio_data_require_status(engine_name, require)) {
499 LOGE("[Recorder WARNING] One of the observer returned false");
508 void CWakeupEngineManager::add_engine(string name, string path)
510 MWR_LOGD("Name (%s), Filepath(%s)", name.c_str(), path.c_str());
514 info.engine_handle = dlopen(path.c_str(), RTLD_LAZY);
515 if (nullptr != (error = dlerror()) || nullptr == info.engine_handle) {
516 MWR_LOGD("[ERROR] Fail to dlopen(%s), error(%s)", path.c_str(), error);
517 if (info.engine_handle) dlclose(info.engine_handle);
521 /* Interfaces without version information */
522 info.interface.initialize =
523 (wakeup_engine_initialize)dlsym(info.engine_handle,
524 MA_WAKEUP_ENGINE_FUNC_INITIALIZE);
525 info.interface.deinitialize =
526 (wakeup_engine_deinitialize)dlsym(info.engine_handle,
527 MA_WAKEUP_ENGINE_FUNC_DEINITIALIZE);
528 info.interface.activate =
529 (wakeup_engine_activate)dlsym(info.engine_handle,
530 MA_WAKEUP_ENGINE_FUNC_ACTIVATE);
531 info.interface.deactivate =
532 (wakeup_engine_deactivate)dlsym(info.engine_handle,
533 MA_WAKEUP_ENGINE_FUNC_DEACTIVATE);
534 info.interface.add_wakeup_word =
535 (wakeup_engine_add_wakeup_word)dlsym(info.engine_handle,
536 MA_WAKEUP_ENGINE_FUNC_ADD_WAKEUP_WORD);
537 info.interface.add_language =
538 (wakeup_engine_add_language)dlsym(info.engine_handle,
539 MA_WAKEUP_ENGINE_FUNC_ADD_LANGUAGE);
540 info.interface.set_language =
541 (wakeup_engine_set_language)dlsym(info.engine_handle,
542 MA_WAKEUP_ENGINE_FUNC_SET_LANGUAGE);
543 info.interface.update_manager_state =
544 (wakeup_engine_update_manager_state)dlsym(info.engine_handle,
545 MA_WAKEUP_ENGINE_FUNC_UPDATE_MANAGER_STATE);
546 info.interface.update_recognition_result =
547 (wakeup_engine_update_recognition_result)dlsym(info.engine_handle,
548 MA_WAKEUP_ENGINE_FUNC_UPDATE_RECOGNITION_RESULT);
549 info.interface.set_audio_format =
550 (wakeup_engine_set_audio_format)dlsym(info.engine_handle,
551 MA_WAKEUP_ENGINE_FUNC_SET_AUDIO_FORMAT);
552 info.interface.get_audio_format =
553 (wakeup_engine_get_audio_format)dlsym(info.engine_handle,
554 MA_WAKEUP_ENGINE_FUNC_GET_AUDIO_FORMAT);
555 info.interface.feed_audio_data =
556 (wakeup_engine_feed_audio_data)dlsym(info.engine_handle,
557 MA_WAKEUP_ENGINE_FUNC_FEED_AUDIO_DATA);
558 info.interface.get_utterance_data_count =
559 (wakeup_engine_get_utterance_data_count)dlsym(info.engine_handle,
560 MA_WAKEUP_ENGINE_FUNC_GET_UTTERANCE_DATA_COUNT);
561 info.interface.get_utterance_data =
562 (wakeup_engine_get_utterance_data)dlsym(info.engine_handle,
563 MA_WAKEUP_ENGINE_FUNC_GET_UTTERANCE_DATA);
564 info.interface.get_wake_word_data_count =
565 (wakeup_engine_get_wake_word_data_count)dlsym(info.engine_handle,
566 MA_WAKEUP_ENGINE_FUNC_GET_WAKE_WORD_DATA_COUNT);
567 info.interface.get_wake_word_data =
568 (wakeup_engine_get_wake_word_data)dlsym(info.engine_handle,
569 MA_WAKEUP_ENGINE_FUNC_GET_WAKE_WORD_DATA);
570 info.interface.set_assistant_specific_command =
571 (wakeup_engine_set_assistant_specific_command)dlsym(info.engine_handle,
572 MA_WAKEUP_ENGINE_FUNC_SET_ASSISTANT_SPECIFIC_COMMAND);
573 info.interface.set_wake_word_audio_require_flag =
574 (wakeup_engine_set_wake_word_audio_require_flag)dlsym(info.engine_handle,
575 MA_WAKEUP_ENGINE_FUNC_SET_WAKE_WORD_AUDIO_REQUIRE_FLAG);
576 info.interface.set_wakeup_event_callback =
577 (wakeup_engine_set_wakeup_event_callback)dlsym(info.engine_handle,
578 MA_WAKEUP_ENGINE_FUNC_SET_WAKEUP_EVENT_CALLBACK);
579 info.interface.set_speech_status_callback =
580 (wakeup_engine_set_speech_status_callback)dlsym(info.engine_handle,
581 MA_WAKEUP_ENGINE_FUNC_SET_SPEECH_STATUS_CALLBACK);
582 info.interface.set_error_callback =
583 (wakeup_engine_set_error_callback)dlsym(info.engine_handle,
584 MA_WAKEUP_ENGINE_FUNC_SET_ERROR_CALLBACK);
585 info.interface.set_audio_data_require_status_callback =
586 (wakeup_engine_set_audio_data_require_status_callback)dlsym(info.engine_handle,
587 MA_WAKEUP_ENGINE_FUNC_SET_AUDIO_DATA_REQUIRE_STATUS_CALLBACK);
589 /* Interfaces after version 1 */
590 info.interface.get_version =
591 (wakeup_engine_get_version)dlsym(info.engine_handle,
592 MA_WAKEUP_ENGINE_FUNC_GET_VERSION);
595 info.engine_path = path;
596 info.engine_name = name;
598 info.activated = false;
599 info.audio_data_require_status = false;
601 /* All the necessary information has already been set properly */
602 mEngineInfo.push_back(info);
604 MWR_LOGD("Initializing wakeup engine : %s %p",
605 info.engine_path.c_str(),
606 info.interface.initialize);
608 /* Workaround for registering C-style callbacks */
610 CWakeupEngineManager *manager;
614 static deque<CallbackUserData> callback_user_data;
616 CallbackUserData user_data;
617 user_data.manager = this;
618 user_data.engine_name = info.engine_name;
619 callback_user_data.push_back(user_data);
621 if (info.interface.set_wakeup_event_callback) {
622 info.interface.set_wakeup_event_callback(
623 [](wakeup_event_info info, void* user_data) {
624 CallbackUserData *data = static_cast<CallbackUserData*>(user_data);
625 if (nullptr == data) return;
626 if (nullptr == data->manager) return;
627 info.wakeup_engine = data->engine_name.c_str();
628 data->manager->on_wakeup_event(data->engine_name, info);
629 }, &(callback_user_data.back()));
632 if (info.interface.set_audio_data_require_status_callback) {
633 info.interface.set_audio_data_require_status_callback(
634 [](bool require, void* user_data) {
635 CallbackUserData *data = static_cast<CallbackUserData*>(user_data);
636 if (nullptr == data) return;
637 if (nullptr == data->manager) return;
638 data->manager->on_audio_data_require_status(data->engine_name, require);
639 }, &(callback_user_data.back()));
642 if (info.interface.initialize) {
643 info.interface.initialize();
645 if (info.interface.get_version) {
647 if (0 == info.interface.get_version(&version)) {
648 info.version = version;
653 void CWakeupEngineManager::add_engine_directory(string name, string path)
655 if (0 == path.size()) return;
657 DIR* dp = opendir(path.c_str());
659 MWR_LOGD("Failed opening directory : %s", path.c_str());
661 struct dirent *dirp = NULL;
667 if (!strcmp(".", dirp->d_name) || !strcmp("..", dirp->d_name))
670 if (DT_REG != dirp->d_type) /* If not a regular file */
675 filepath += dirp->d_name;
677 if (filepath.length() >= _POSIX_PATH_MAX) {
678 MWR_LOGD("File path is too long : %s", filepath.c_str());
682 add_engine(name, filepath);
684 } while (NULL != dirp);