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) {
81 if (info.interface.set_wakeup_event_callback) {
82 info.interface.set_wakeup_event_callback(nullptr, nullptr);
84 if (info.interface.set_speech_status_callback) {
85 info.interface.set_speech_status_callback(nullptr, nullptr);
87 if (info.interface.set_error_callback) {
88 info.interface.set_error_callback(nullptr, nullptr);
90 if (info.interface.set_audio_data_require_status_callback) {
91 info.interface.set_audio_data_require_status_callback(nullptr, nullptr);
93 if (info.interface.deinitialize) {
94 info.interface.deinitialize();
96 } catch (const std::exception& e) {
97 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
98 info.engine_name.c_str(), e.what());
100 if (info.engine_handle) {
101 dlclose(info.engine_handle);
102 info.engine_handle = nullptr;
105 mSelectedEngine = nullptr;
109 void CWakeupEngineManager::subscribe(IEngineEventObserver *observer)
111 mObservers.push_back(observer);
112 MWR_LOGD("Added Observer : %p %zu", observer, mObservers.size());
115 void CWakeupEngineManager::unsubscribe(IEngineEventObserver *observer)
117 auto iter = find(mObservers.begin(), mObservers.end(), observer);
118 if (iter != mObservers.end()) {
119 mObservers.erase(iter);
123 bool CWakeupEngineManager::get_audio_data_required()
125 return mAudioDataRequired;
128 void CWakeupEngineManager::set_selected_wakeup_info(wakeup_event_info wakeup_info)
130 mSelectedEngine = nullptr;
131 for (const auto& info : mEngineInfo) {
132 string appid = string{wakeup_info.wakeup_appid};
133 bool found = contains(info.assistant_list, appid);
136 mSelectedEngine = &info;
137 MWR_LOGD("Selected : %s", info.engine_name.c_str());
142 bool CWakeupEngineManager::set_language(string language)
144 for (const auto& info : mEngineInfo) {
145 if (info.interface.set_language) {
147 info.interface.set_language(language.c_str());
148 } catch (const std::exception& e) {
149 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
150 info.engine_name.c_str(), e.what());
157 void CWakeupEngineManager::set_assistant_activated(string appid, bool activated)
159 MWR_LOGD("[ENTER] : %s %d", appid.c_str(), activated);
160 for (auto& info : mEngineInfo) {
161 const auto& iter = find_if(info.assistant_list.begin(), info.assistant_list.end(),
162 [appid](const string& assistant) {
163 return (0 == assistant.compare(appid));
166 /* If the appid is in the assistant list */
167 if (info.assistant_list.end() != iter) {
168 bool previously_activated = info.activated;
170 info.activated_assistants.insert(appid);
172 info.activated_assistants.erase(appid);
174 info.activated = (info.activated_assistants.size() > 0);
175 if (previously_activated != info.activated) {
176 if (info.activated) {
178 info.interface.activate();
179 } catch (const std::exception& e) {
180 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
181 info.engine_name.c_str(), e.what());
185 info.interface.deactivate();
186 } catch (const std::exception& e) {
187 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
188 info.engine_name.c_str(), e.what());
191 /* Activated status changed, need to update audio_data_require_status too */
192 on_audio_data_require_status(info.engine_name, info.audio_data_require_status);
198 void CWakeupEngineManager::set_wake_word_audio_require_flag(bool require)
201 mWakeWordAudioRequired = require;
202 for (const auto& info : mEngineInfo) {
203 if (info.interface.set_wake_word_audio_require_flag) {
205 info.interface.set_wake_word_audio_require_flag(require);
206 } catch (const std::exception& e) {
207 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
208 info.engine_name.c_str(), e.what());
214 void CWakeupEngineManager::streaming_speech_data_thread_func()
218 if (nullptr == mSelectedEngine)
221 const wakeup_engine_interface *interface = &(mSelectedEngine->interface);
223 if (NULL == interface ||
224 NULL == interface->get_utterance_data ||
225 NULL == interface->get_utterance_data_count)
228 MWR_LOGD("data_count : %d", interface->get_utterance_data_count());
230 wakeup_speech_data speech_data;
232 bool finish_event_sent = false;
234 if (mWakeWordAudioRequired &&
235 NULL != interface->get_wake_word_data &&
236 NULL != interface->get_wake_word_data_count) {
237 for (const auto& observer : mObservers) {
239 if (!observer->on_audio_streaming_data_section(MA_AUDIO_STREAMING_DATA_SECTION_WAKE_WORD)) {
240 LOGE("[Recorder WARNING] One of the observer returned false");
244 int count = interface->get_wake_word_data_count();
245 while (!(mStopStreamingThread.load()) && index < count) {
246 int ret = interface->get_wake_word_data(index, &speech_data);
248 for (const auto& observer : mObservers) {
250 if (!observer->on_streaming_audio_data(
251 speech_data.event, speech_data.buffer, speech_data.len)) {
252 LOGE("[Recorder WARNING] One of the observer returned false");
262 for (const auto& observer : mObservers) {
264 if (!observer->on_audio_streaming_data_section(MA_AUDIO_STREAMING_DATA_SECTION_UTTERANCE)) {
265 LOGE("[Recorder WARNING] One of the observer returned false");
271 while (!(mStopStreamingThread.load())) {
275 /* get feedback data */
276 if (interface && interface->get_utterance_data) {
277 ret = interface->get_utterance_data(index, &speech_data);
280 MWR_LOGD("[DEBUG] No feedback data. Waiting mode : %d", ret);
283 while (!(mStopStreamingThread.load())) {
284 this_thread::sleep_for(chrono::milliseconds(10));
285 if (index < interface->get_utterance_data_count()) {
286 MWR_LOGI("[INFO] Resume thread");
289 if (g_speech_pcm_wait_count < cnt) {
290 MWR_LOGE("[ERROR] Wrong request, there's no pcm data");
291 for (const auto& observer : mObservers) {
293 if (!observer->on_streaming_audio_data(
294 WAKEUP_SPEECH_STREAMING_EVENT_FAIL, NULL, 0)) {
295 LOGE("[Recorder WARNING] One of the observer returned false");
303 MWR_LOGI("[INFO] Finish to wait for new feedback data come");
305 /* resume feedback thread */
309 for (const auto& observer : mObservers) {
311 if (!observer->on_streaming_audio_data(
312 speech_data.event, speech_data.buffer, speech_data.len)) {
313 LOGE("[Recorder WARNING] One of the observer returned false");
318 if (WAKEUP_SPEECH_STREAMING_EVENT_FINISH == speech_data.event) {
319 MWR_LOGI("[INFO] Finish to get and send speech data");
320 finish_event_sent = true;
328 if (true != finish_event_sent) {
329 unsigned char final_buffer[2] = {'\0', };
330 for (const auto& observer : mObservers) {
332 if (!observer->on_streaming_audio_data(
333 WAKEUP_SPEECH_STREAMING_EVENT_FINISH, final_buffer, sizeof(final_buffer))) {
334 LOGE("[Recorder WARNING] One of the observer returned false");
341 void CWakeupEngineManager::start_streaming_current_utterance_data()
343 if (mStreamingThread.joinable()) {
344 MWR_LOGE("ERROR : mStreamingThread is joinable, will not start a new thread");
347 mStreamingThread = thread(&CWakeupEngineManager::streaming_speech_data_thread_func, this);
350 void CWakeupEngineManager::stop_streaming_current_utterance_data()
352 if (mStreamingThread.joinable()) {
353 MWR_LOGD("mStreamingThread is joinable, trying join()");
354 mStopStreamingThread.store(true);
355 mStreamingThread.join();
357 mStopStreamingThread.store(false);
360 void CWakeupEngineManager::update_manager_state(wakeup_manager_state_e state)
362 for (const auto& info : mEngineInfo) {
363 if (info.interface.update_manager_state) {
365 info.interface.update_manager_state(state);
366 } catch (const std::exception& e) {
367 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
368 info.engine_name.c_str(), e.what());
374 void CWakeupEngineManager::update_recognition_result(string appid, int result)
376 if (mSelectedEngine) {
377 if (mSelectedEngine->interface.update_recognition_result) {
378 mSelectedEngine->interface.update_recognition_result(appid.c_str(), result);
383 void CWakeupEngineManager::engine_add_target_assistant(string engine_name, string appid)
385 const auto& iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
386 [engine_name](const EngineInfo& info) {
387 return (0 == info.engine_name.compare(engine_name));
390 if (mEngineInfo.end() == iter) {
391 /* Not found, add new library */
392 pkgmgrinfo_appinfo_h handle;
393 int ret = pkgmgrinfo_appinfo_get_appinfo(engine_name.c_str(), &handle);
394 if (PMINFO_R_OK == ret) {
395 char *root_path = nullptr;
396 ret = pkgmgrinfo_appinfo_get_root_path(handle, &root_path);
397 if (PMINFO_R_OK == ret && nullptr != root_path) {
398 string path = root_path;
400 path += MA_WAKEUP_DEDICATED_ENGINE_PATH;
401 add_engine(engine_name, path);
403 pkgmgrinfo_appinfo_destroy_appinfo(handle);
405 /* Find again to add appid to the newly created engine's assistant list */
406 const auto &new_iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
407 [engine_name](const EngineInfo& info) {
408 return (0 == info.engine_name.compare(engine_name));
410 if (mEngineInfo.end() != new_iter) {
411 new_iter->assistant_list.push_back(appid);
414 /* If the engine already exists, simply add the appid to the assistant list */
415 iter->assistant_list.push_back(appid);
419 void CWakeupEngineManager::engine_add_wakeup_word(string appid, string wakeup_word, string language)
421 for (const auto& info : mEngineInfo) {
422 bool found = contains(info.assistant_list, appid);
424 if (info.interface.add_wakeup_word) {
426 info.interface.add_wakeup_word(appid.c_str(), wakeup_word.c_str(), language.c_str());
427 } catch (const std::exception& e) {
428 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
429 info.engine_name.c_str(), e.what());
436 void CWakeupEngineManager::engine_set_assistant_specific_command(string appid, string command)
438 for (const auto& info : mEngineInfo) {
439 bool found = contains(info.assistant_list, appid);
441 if (info.interface.set_assistant_specific_command) {
443 info.interface.set_assistant_specific_command(appid.c_str(), command.c_str());
444 } catch (const std::exception& e) {
445 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
446 info.engine_name.c_str(), e.what());
453 void CWakeupEngineManager::engine_feed_audio_data(long time, void* data, int len)
455 for (const auto& info : mEngineInfo) {
456 if (info.activated &&
457 info.audio_data_require_status &&
458 info.interface.feed_audio_data) {
460 int ret = info.interface.feed_audio_data(time, data, len);
462 LOGE("[ERROR] Fail to feed speech data, ret(%d) : %s", ret, info.engine_name.c_str());
464 } catch (const std::exception& e) {
465 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
466 info.engine_name.c_str(), e.what());
472 bool CWakeupEngineManager::on_wakeup_event(string engine_name, wakeup_event_info info)
476 for (const auto& observer : mObservers) {
478 if (!observer->on_wakeup_event(engine_name, info)) {
479 LOGE("[Recorder WARNING] One of the observer returned false");
487 bool CWakeupEngineManager::on_speech_status(string engine_name, wakeup_service_speech_status_e status)
491 for (const auto& observer : mObservers) {
493 if (!observer->on_speech_status(engine_name, status)) {
494 LOGE("[Recorder WARNING] One of the observer returned false");
502 bool CWakeupEngineManager::on_error(string engine_name, int error_code, string error_message)
506 for (const auto& observer : mObservers) {
508 if (!observer->on_error(engine_name, error_code, error_message)) {
509 LOGE("[Recorder WARNING] One of the observer returned false");
517 bool CWakeupEngineManager::on_audio_data_require_status(string engine_name, bool require)
519 MWR_LOGD("[ENTER] %s, %d", engine_name.c_str(), require);
524 for (auto& info : mEngineInfo) {
525 if (info.engine_name.compare(engine_name) == 0) {
527 info.audio_data_require_status = require;
529 if (info.activated && info.audio_data_require_status) {
533 MWR_LOGD("count : %d", count);
535 mAudioDataRequired = true;
537 mAudioDataRequired = false;
541 for (const auto& observer : mObservers) {
543 if (!observer->on_audio_data_require_status(engine_name, require)) {
544 LOGE("[Recorder WARNING] One of the observer returned false");
553 void CWakeupEngineManager::add_engine(string name, string path)
555 MWR_LOGD("Name (%s), Filepath(%s)", name.c_str(), path.c_str());
559 info.engine_handle = dlopen(path.c_str(), RTLD_LAZY);
560 if (nullptr != (error = dlerror()) || nullptr == info.engine_handle) {
561 MWR_LOGD("[ERROR] Fail to dlopen(%s), error(%s)", path.c_str(), error);
562 if (info.engine_handle) dlclose(info.engine_handle);
566 /* Interfaces without version information */
567 info.interface.initialize =
568 (wakeup_engine_initialize)dlsym(info.engine_handle,
569 MA_WAKEUP_ENGINE_FUNC_INITIALIZE);
570 info.interface.deinitialize =
571 (wakeup_engine_deinitialize)dlsym(info.engine_handle,
572 MA_WAKEUP_ENGINE_FUNC_DEINITIALIZE);
573 info.interface.activate =
574 (wakeup_engine_activate)dlsym(info.engine_handle,
575 MA_WAKEUP_ENGINE_FUNC_ACTIVATE);
576 info.interface.deactivate =
577 (wakeup_engine_deactivate)dlsym(info.engine_handle,
578 MA_WAKEUP_ENGINE_FUNC_DEACTIVATE);
579 info.interface.add_wakeup_word =
580 (wakeup_engine_add_wakeup_word)dlsym(info.engine_handle,
581 MA_WAKEUP_ENGINE_FUNC_ADD_WAKEUP_WORD);
582 info.interface.add_language =
583 (wakeup_engine_add_language)dlsym(info.engine_handle,
584 MA_WAKEUP_ENGINE_FUNC_ADD_LANGUAGE);
585 info.interface.set_language =
586 (wakeup_engine_set_language)dlsym(info.engine_handle,
587 MA_WAKEUP_ENGINE_FUNC_SET_LANGUAGE);
588 info.interface.update_manager_state =
589 (wakeup_engine_update_manager_state)dlsym(info.engine_handle,
590 MA_WAKEUP_ENGINE_FUNC_UPDATE_MANAGER_STATE);
591 info.interface.update_recognition_result =
592 (wakeup_engine_update_recognition_result)dlsym(info.engine_handle,
593 MA_WAKEUP_ENGINE_FUNC_UPDATE_RECOGNITION_RESULT);
594 info.interface.set_audio_format =
595 (wakeup_engine_set_audio_format)dlsym(info.engine_handle,
596 MA_WAKEUP_ENGINE_FUNC_SET_AUDIO_FORMAT);
597 info.interface.get_audio_format =
598 (wakeup_engine_get_audio_format)dlsym(info.engine_handle,
599 MA_WAKEUP_ENGINE_FUNC_GET_AUDIO_FORMAT);
600 info.interface.feed_audio_data =
601 (wakeup_engine_feed_audio_data)dlsym(info.engine_handle,
602 MA_WAKEUP_ENGINE_FUNC_FEED_AUDIO_DATA);
603 info.interface.get_utterance_data_count =
604 (wakeup_engine_get_utterance_data_count)dlsym(info.engine_handle,
605 MA_WAKEUP_ENGINE_FUNC_GET_UTTERANCE_DATA_COUNT);
606 info.interface.get_utterance_data =
607 (wakeup_engine_get_utterance_data)dlsym(info.engine_handle,
608 MA_WAKEUP_ENGINE_FUNC_GET_UTTERANCE_DATA);
609 info.interface.get_wake_word_data_count =
610 (wakeup_engine_get_wake_word_data_count)dlsym(info.engine_handle,
611 MA_WAKEUP_ENGINE_FUNC_GET_WAKE_WORD_DATA_COUNT);
612 info.interface.get_wake_word_data =
613 (wakeup_engine_get_wake_word_data)dlsym(info.engine_handle,
614 MA_WAKEUP_ENGINE_FUNC_GET_WAKE_WORD_DATA);
615 info.interface.set_assistant_specific_command =
616 (wakeup_engine_set_assistant_specific_command)dlsym(info.engine_handle,
617 MA_WAKEUP_ENGINE_FUNC_SET_ASSISTANT_SPECIFIC_COMMAND);
618 info.interface.set_wake_word_audio_require_flag =
619 (wakeup_engine_set_wake_word_audio_require_flag)dlsym(info.engine_handle,
620 MA_WAKEUP_ENGINE_FUNC_SET_WAKE_WORD_AUDIO_REQUIRE_FLAG);
621 info.interface.set_wakeup_event_callback =
622 (wakeup_engine_set_wakeup_event_callback)dlsym(info.engine_handle,
623 MA_WAKEUP_ENGINE_FUNC_SET_WAKEUP_EVENT_CALLBACK);
624 info.interface.set_speech_status_callback =
625 (wakeup_engine_set_speech_status_callback)dlsym(info.engine_handle,
626 MA_WAKEUP_ENGINE_FUNC_SET_SPEECH_STATUS_CALLBACK);
627 info.interface.set_error_callback =
628 (wakeup_engine_set_error_callback)dlsym(info.engine_handle,
629 MA_WAKEUP_ENGINE_FUNC_SET_ERROR_CALLBACK);
630 info.interface.set_audio_data_require_status_callback =
631 (wakeup_engine_set_audio_data_require_status_callback)dlsym(info.engine_handle,
632 MA_WAKEUP_ENGINE_FUNC_SET_AUDIO_DATA_REQUIRE_STATUS_CALLBACK);
634 /* Interfaces after version 1 */
635 info.interface.get_version =
636 (wakeup_engine_get_version)dlsym(info.engine_handle,
637 MA_WAKEUP_ENGINE_FUNC_GET_VERSION);
640 info.engine_path = path;
641 info.engine_name = name;
643 info.activated = false;
644 info.audio_data_require_status = false;
646 /* All the necessary information has already been set properly */
647 mEngineInfo.push_back(info);
649 MWR_LOGD("Initializing wakeup engine : %s %p",
650 info.engine_path.c_str(),
651 info.interface.initialize);
653 /* Workaround for registering C-style callbacks */
655 CWakeupEngineManager *manager;
659 static deque<CallbackUserData> callback_user_data;
661 CallbackUserData user_data;
662 user_data.manager = this;
663 user_data.engine_name = info.engine_name;
664 callback_user_data.push_back(user_data);
667 if (info.interface.set_wakeup_event_callback) {
668 info.interface.set_wakeup_event_callback(
669 [](wakeup_event_info info, void* user_data) {
670 CallbackUserData *data = static_cast<CallbackUserData*>(user_data);
671 if (nullptr == data) return;
672 if (nullptr == data->manager) return;
673 info.wakeup_engine = data->engine_name.c_str();
674 data->manager->on_wakeup_event(data->engine_name, info);
675 }, &(callback_user_data.back()));
678 if (info.interface.set_audio_data_require_status_callback) {
679 info.interface.set_audio_data_require_status_callback(
680 [](bool require, void* user_data) {
681 CallbackUserData *data = static_cast<CallbackUserData*>(user_data);
682 if (nullptr == data) return;
683 if (nullptr == data->manager) return;
684 data->manager->on_audio_data_require_status(data->engine_name, require);
685 }, &(callback_user_data.back()));
688 if (info.interface.initialize) {
689 info.interface.initialize();
691 if (info.interface.get_version) {
693 if (0 == info.interface.get_version(&version)) {
694 info.version = version;
697 } catch (const std::exception& e) {
698 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
699 info.engine_name.c_str(), e.what());
703 void CWakeupEngineManager::add_engine_directory(string name, string path)
705 if (0 == path.size()) return;
707 DIR* dp = opendir(path.c_str());
709 MWR_LOGD("Failed opening directory : %s", path.c_str());
711 struct dirent *dirp = NULL;
717 if (!strcmp(".", dirp->d_name) || !strcmp("..", dirp->d_name))
720 if (DT_REG != dirp->d_type) /* If not a regular file */
725 filepath += dirp->d_name;
727 if (filepath.length() >= _POSIX_PATH_MAX) {
728 MWR_LOGD("File path is too long : %s", filepath.c_str());
732 add_engine(name, filepath);
734 } while (NULL != dirp);