From dc5104d4254c9789a9b1ebce4543f0558e56aaac Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Sun, 29 Dec 2019 21:05:27 -0500 Subject: Add signal-composer voice event support Add support for triggering listening (push-to-talk/PTT) via the signal-composer "event.voice" event. This enables PTT from the steering wheel for the CES demo. Note that this relies on a fix to signal-composer to add the session context uuid to the replies from event subscribe calls. Without that, it's not feasible to catch the signal-composer events with the libappcontroller event scheme and some more significant rework would be required. Bug-AGL: SPEC-3048 Signed-off-by: Scott Murray Change-Id: I22c7265c2f08ecc0a598fd88ddf4fd53f8c3e880 --- conf.d/project/etc/vshl-core-api.json | 7 ++- conf.d/wgt/config.xml.in | 3 + src/plugins/VshlCoreApi.cpp | 108 ++++++++++++++++++++++++++++++++++ src/plugins/VshlCoreApi.h | 2 + 4 files changed, 119 insertions(+), 1 deletion(-) diff --git a/conf.d/project/etc/vshl-core-api.json b/conf.d/project/etc/vshl-core-api.json index 9121353..001ac42 100644 --- a/conf.d/project/etc/vshl-core-api.json +++ b/conf.d/project/etc/vshl-core-api.json @@ -4,13 +4,18 @@ "uid": "vshl-core", "version": "1.0", "api": "vshl-core", - "info": "High Level Voice Service Core APIs" + "info": "High Level Voice Service Core APIs", + "require": ["signal-composer"] }, "onload": [{ "uid": "loadVoiceAgentsConfig", "info": "Loading the information about voice agents managed by the high level voice service.", "action": "plugin://vshl-core#loadVoiceAgentsConfig", + }, { + "uid": "subscribeVoiceTriggerEvents", + "info": "Subscribe to voice recognition triggering events.", + "action": "plugin://vshl-core#subscribeVoiceTriggerEvents", }], "plugins": [{ diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in index 3c0b0a0..ee70db0 100644 --- a/conf.d/wgt/config.xml.in +++ b/conf.d/wgt/config.xml.in @@ -17,4 +17,7 @@ + + + diff --git a/src/plugins/VshlCoreApi.cpp b/src/plugins/VshlCoreApi.cpp index abe4577..b14f949 100644 --- a/src/plugins/VshlCoreApi.cpp +++ b/src/plugins/VshlCoreApi.cpp @@ -66,6 +66,11 @@ static std::unique_ptr sEventRouter; using json = nlohmann::json; using Level = vshlcore::utilities::logging::Logger::Level; +static const char *signalcomposer_events[] = { + "event.voice", + NULL, +}; + CTLP_ONLOAD(plugin, ret) { if (plugin->api == nullptr) { return -1; @@ -195,6 +200,29 @@ CTLP_CAPI(onLoginPairExpiredEvent, source, argsJ, eventJ) { return 0; } +CTLP_CAPI(onVoiceTriggerEvent, source, argsJ, eventJ) { + if (sVRRequestProcessor == nullptr) { + return -1; + } + + json eventJson = json::parse(json_object_to_json_string(eventJ)); + if (eventJson.find("uid") == eventJson.end()) { + sLogger->log(Level::ERROR, TAG, "onVoiceTriggerEvent: No uid found."); + return -1; + } + if (eventJson.find("value") == eventJson.end()) { + sLogger->log(Level::ERROR, TAG, "onVoiceTriggerEvent: No value found."); + return -1; + } + std::string uid(eventJson["uid"].get()); + bool value(eventJson["value"].get()); + if (uid == "event.voice" && value == true) { + sVRRequestProcessor->startListening(); + } + + return 0; +} + // Helper function to add events for voice agent to controller // configuration. It relies on the controller configuration being // available via the userdata of the API pointer. @@ -406,6 +434,86 @@ CTLP_CAPI(loadVoiceAgentsConfig, source, argsJ, eventJ) { return 0; } +// Helper function to add signal composer events for voice triggers to +// controller configuration. It relies on the controller configuration +// being available via the userdata of the API pointer. +static int AddVoiceTriggerEvents(std::string &agentApi, std::string &uuid) { + // Retrieve section config from api handle + CtlConfigT *ctrlConfig = (CtlConfigT*) afb_api_get_userdata(sAfbApi->getApi()); + if (ctrlConfig == nullptr) { + sLogger->log(Level::ERROR, TAG, "AddVoiceAgentEvents: ctrlConfig == nullptr"); + return -1; + } + + CtlSectionT* section = nullptr; + for (int idx = 0; ctrlConfig->sections[idx].key != NULL; ++idx) { + if (!strcasecmp(ctrlConfig->sections[idx].key, "events")) { + section = &(ctrlConfig->sections[idx]); + break; + } + } + if (section == nullptr) { + sLogger->log(Level::ERROR, TAG, "AddVoiceTriggerEvents: events section not found"); + return -1; + } + + // Fill out events JSON array to pass to the controller + json_object* eventsJ = json_object_new_array(); + + json_object *eventJ = json_object_new_object(); + std::string uid = agentApi + "/" + uuid; + json_object *uidJ = json_object_new_string(uid.c_str()); + json_object_object_add(eventJ, "uid", uidJ); + json_object *actionJ = json_object_new_string("plugin://vshl-core#onVoiceTriggerEvent"); + json_object_object_add(eventJ, "action", actionJ); + json_object_array_add(eventsJ, eventJ); + + // Call into controller to add event actions + // NOTE: AFAICT the JSON objects end up reused by the controller data + // structures, so eventsJ should not be freed with a put after + // this call. + int rc = AddActionsToSection(sAfbApi->getApi(), section, eventsJ, 0); + if (rc != 0) { + stringstream message; + message << "AddVoiceTriggerEvents: AddActionsToSection rc = " << rc; + sLogger->log(Level::WARNING, TAG, message.str().c_str()); + } + return rc; +} + +CTLP_CAPI(subscribeVoiceTriggerEvents, source, argsJ, eventJ) { + // Subscribe to signal-composer voice/PTT events + const char **tmp = signalcomposer_events; + json_object *args = json_object_new_object(); + json_object *signals = json_object_new_array(); + + while (*tmp) { + json_object_array_add(signals, json_object_new_string(*tmp++)); + } + json_object_object_add(args, "signal", signals); + if (json_object_array_length(signals)) { + struct json_object *responseJ; + int rc = afb_api_call_sync(sAfbApi->getApi(), "signal-composer", + "subscribe", args, &responseJ, NULL, NULL); + if (rc == 0 && responseJ) { + struct json_object *uuidJ; + json_object_object_get_ex(responseJ, "uuid", &uuidJ); + if (uuidJ) { + std::string uuid(json_object_get_string(uuidJ)); + std::string api("signal-composer"); + AddVoiceTriggerEvents(api, uuid); + } + } + if (responseJ) + json_object_put(responseJ); + } else { + json_object_put(args); + } + + // Always return zero so service will start + return 0; +} + CTLP_CAPI(startListening, source, argsJ, eventJ) { if (sVoiceAgentsDataManager == nullptr) { return -1; diff --git a/src/plugins/VshlCoreApi.h b/src/plugins/VshlCoreApi.h index eb90190..7c5bf69 100644 --- a/src/plugins/VshlCoreApi.h +++ b/src/plugins/VshlCoreApi.h @@ -29,7 +29,9 @@ int onConnectionStateEvent(CtlSourceT* source, json_object* argsJ, json_object* int onDialogStateEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ); int onLoginPairReceivedEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ); int onLoginPairExpiredEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int onVoiceTriggerEvent(CtlSourceT* source, json_object* argsJ, json_object* queryJ); int loadVoiceAgentsConfig(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int subscribeVoiceTriggerEvents(CtlSourceT* source, json_object* argsJ, json_object* queryJ); int startListening(CtlSourceT* source, json_object* argsJ, json_object* queryJ); int cancelListening(CtlSourceT* source, json_object* argsJ, json_object* queryJ); int enumerateVoiceAgents(CtlSourceT* source, json_object* argsJ, json_object* queryJ); -- cgit 1.2.3-korg