summaryrefslogtreecommitdiffstats
path: root/agl-speech-afb
diff options
context:
space:
mode:
Diffstat (limited to 'agl-speech-afb')
-rw-r--r--agl-speech-afb/CMakeLists.txt36
-rw-r--r--agl-speech-afb/agl-speech-binding.c289
2 files changed, 325 insertions, 0 deletions
diff --git a/agl-speech-afb/CMakeLists.txt b/agl-speech-afb/CMakeLists.txt
new file mode 100644
index 0000000..565a3e7
--- /dev/null
+++ b/agl-speech-afb/CMakeLists.txt
@@ -0,0 +1,36 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+# Add target to project dependency list
+PROJECT_TARGET_ADD(simpleservice)
+
+ # Define project Targets
+ file(GLOB sourcelist "*.c")
+
+ # Define project Targets
+ ADD_LIBRARY(${TARGET_NAME} MODULE ${sourcelist})
+
+ # Binder exposes a unique public entry point
+ SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+ PREFIX "afb-"
+ LABELS "BINDING"
+ LINK_FLAGS ${BINDINGS_LINK_FLAG}
+ OUTPUT_NAME ${TARGET_NAME}
+ )
+
+ TARGET_LINK_LIBRARIES(${TARGET_NAME}
+ ${link_libraries}
+ )
diff --git a/agl-speech-afb/agl-speech-binding.c b/agl-speech-afb/agl-speech-binding.c
new file mode 100644
index 0000000..bc9696e
--- /dev/null
+++ b/agl-speech-afb/agl-speech-binding.c
@@ -0,0 +1,289 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <json-c/json.h>
+#include <systemd/sd-event.h>
+#include <sys/timerfd.h>
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+afb_event event_tts_prompt_playing, event_tts_prompt_completed,
+ event_stt_result;
+
+static void subscribe(struct afb_req request)
+{
+ afb_req_subscribe(request, event_tts_prompt_playing);
+ afb_req_subscribe(request, event_tts_prompt_completed);
+ afb_req_subscribe(request, event_stt_result);
+
+ json_object *res = json_object_new_object();
+ json_object_object_add(res, "status", json_object_new_string("ok"));
+
+ afb_req_success(request, res, "subscribed to all events");
+
+ AFB_NOTICE("subscribe was called");
+}
+
+static void tts_get_available_languages(struct afb_req request)
+{
+ json_object *res = json_object_new_object();
+ json_object *list = json_object_new_array();
+ json_object_array_add(list, json_object_new_string("en-US"));
+ json_object_object_add(res, "languages", list);
+
+ afb_req_success(request, res, "tts_get_available_languages");
+
+ AFB_NOTICE("tts_get_available_languages was called");
+}
+
+static int on_prompt_completed(
+ sd_event_source *s,
+ uint64_t usec,
+ void *userdata)
+{
+ AFB_NOTICE("TTS prompt completed");
+
+ json_object* event_json = (json_object*) userdata;
+ afb_event_push(event_tts_prompt_completed, event_json);
+
+ return 0;
+}
+
+
+// Requests look like this:
+// {
+// "language": "en-US",
+// "text": "Hello AGL! What can I do for you?"
+// }
+//
+static void tts_play_prompt(struct afb_req request)
+{
+ json_object *tmpJ;
+ json_object *queryJ = afb_req_json(request);
+
+ json_bool success = json_object_object_get_ex(queryJ, "language", &tmpJ);
+ if (!success) {
+ afb_req_fail_f(request, "ERROR", "key language not found in '%s'", json_object_get_string(queryJ));
+ return;
+ }
+
+ if (json_object_get_type(tmpJ) != json_type_string) {
+ afb_req_fail(request, "ERROR", "key language not a string");
+ return;
+ }
+
+ const char* language = json_object_get_string(tmpJ);
+ if (strncmp(language, "en-US", 4) != 0) {
+ afb_req_fail(request, "ERROR", "Only supported language for now is en-US");
+ return;
+ }
+
+
+ success = json_object_object_get_ex(queryJ, "text", &tmpJ);
+ if (!success) {
+ afb_req_fail_f(request, "ERROR", "key text not found in '%s'", json_object_get_string(queryJ));
+ return;
+ }
+
+ if (json_object_get_type(tmpJ) != json_type_string) {
+ afb_req_fail(request, "ERROR", "key text not a string");
+ return;
+ }
+
+ const char* text = json_object_get_string(tmpJ);
+ AFB_NOTICE("Text to speech playing prompt: '%s'", text);
+
+ json_object *res = json_object_new_object();
+ json_object_object_add(res, "status", json_object_new_string("ok"));
+
+ // return success
+ afb_req_success(request, res, "tts_play_prompt");
+
+
+ // send event_tts_prompt_playing
+ json_object *event_playing_json = json_object_new_object();
+ json_object_object_add(event_playing_json, "text", json_object_new_string(text));
+ json_object_object_add(event_playing_json, "language", json_object_new_string(language));
+ json_object_object_add(event_playing_json, "elapsed_time_us", json_object_new_int(2500000));
+
+ afb_event_push(event_tts_prompt_playing, event_playing_json);
+
+
+ // create timer that fire event_tts_prompt_completed
+ struct sd_event* event_loop = afb_daemon_get_event_loop();
+
+ uint64_t current_time = 0;
+ sd_event_now(
+ event_loop,
+ CLOCK_MONOTONIC,
+ &current_time
+ );
+
+ json_object *event_completed_json = json_object_new_object();
+ json_object_object_add(event_completed_json, "text", json_object_new_string(text));
+ json_object_object_add(event_completed_json, "language", json_object_new_string(language));
+ json_object_object_add(event_completed_json, "elapsed_time_ms", json_object_new_int(3000));
+
+ sd_event_add_time(
+ event_loop,
+ NULL, /* event source */
+ CLOCK_MONOTONIC,
+ current_time + 3000000 , /* usec */
+ 0, /* accuracy */
+ on_prompt_completed,
+ event_completed_json /* userdata */
+ );
+}
+
+
+struct stt_fake_slot
+{
+ const char* name;
+ const char* value;
+};
+
+struct stt_fake_result
+{
+ int type; /* 0=intermediate, 1=final */
+ int usec_delay;
+ double confidence;
+ const char* domain;
+ const char* intent;
+ const struct stt_fake_slot slots; // only one because I'm lazy
+};
+
+static const struct stt_fake_result stt_fake_results[] = {
+ {
+ .type = 1,
+ .usec_delay = 5000000,
+ .confidence = 0.99,
+ .domain = "hvac",
+ .intent = "set_temperature",
+ .slots = {
+ .name = "temperature",
+ .value = "70"
+ }
+ }
+};
+
+static int on_recognition_result(
+ sd_event_source *s,
+ uint64_t usec,
+ void *userdata)
+{
+ struct stt_fake_result result = stt_fake_results[0];
+ AFB_NOTICE("Speech to text recognition result at offset %llu with intent '%s'",
+ (unsigned long long)result.usec_delay,
+ result.intent);
+
+ json_object *event_json = json_object_new_object();
+
+ json_object *intent_result = json_object_new_object();
+ json_object_object_add(intent_result, "confidence", json_object_new_double(result.confidence));
+ json_object_object_add(intent_result, "domain", json_object_new_string(result.domain));
+ json_object_object_add(intent_result, "intent", json_object_new_string(result.intent));
+
+ json_object_object_add(event_json, "time_offset_usec", json_object_new_int(result.usec_delay));
+ json_object_object_add(event_json, "result", intent_result);
+
+ json_object *slots = json_object_new_array();
+ json_object *slot = json_object_new_object();
+ json_object_object_add(slot, "name", json_object_new_string(result.slots.name));
+ json_object_object_add(slot, "value", json_object_new_string(result.slots.value));
+ json_object_array_add(slots, slot);
+
+ json_object_object_add(intent_result, "slots", slots);
+
+ afb_event_push(event_stt_result, event_json);
+
+ return 0;
+}
+
+// Configuration comes later...
+static void stt_recognize(struct afb_req request)
+{
+ json_object *res = json_object_new_object();
+ json_object_object_add(res, "status", json_object_new_string("ok"));
+
+ afb_req_success(request, res, "stt_recognize");
+
+ struct sd_event* event_loop = afb_daemon_get_event_loop();
+
+ uint64_t current_time = 0;
+ sd_event_now(
+ event_loop,
+ CLOCK_MONOTONIC,
+ &current_time
+ );
+
+ sd_event_add_time(
+ event_loop,
+ NULL /* event source */,
+ CLOCK_MONOTONIC,
+ current_time + stt_fake_results[0].usec_delay , /* usec */
+ 0, /* accuracy */
+ on_recognition_result,
+ NULL /* userdata */
+ );
+
+ AFB_NOTICE("stt_recognize was called");
+}
+
+
+static const struct afb_auth _afb_auths_v2_monitor[] = {
+ {.type = afb_auth_Permission, .text = "urn:AGL:permission:monitor:public:set"},
+ {.type = afb_auth_Permission, .text = "urn:AGL:permission:monitor:public:get"},
+ {.type = afb_auth_Or, .first = &_afb_auths_v2_monitor[1], .next = &_afb_auths_v2_monitor[0]}
+};
+
+static const struct afb_verb_v2 verbs[] = {
+
+ {.verb = "subscribe", .session = AFB_SESSION_NONE, .callback = subscribe, .auth = NULL},
+
+ {.verb = "tts_play_prompt", .session = AFB_SESSION_NONE, .callback = tts_play_prompt, .auth = NULL},
+ {.verb = "tts_get_available_languages", .session = AFB_SESSION_NONE, .callback = tts_get_available_languages, .auth = NULL},
+
+ {.verb = "stt_recognize", .session = AFB_SESSION_NONE, .callback = stt_recognize, .auth = NULL},
+
+ {NULL}
+};
+
+static int init()
+{
+ AFB_NOTICE("init was called");
+ event_tts_prompt_playing = afb_daemon_make_event("event_tts_prompt_playing");
+ event_tts_prompt_completed = afb_daemon_make_event("event_tts_prompt_completed");
+ event_stt_result = afb_daemon_make_event("event_stt_final_result");
+
+ if (!afb_event_is_valid(event_tts_prompt_playing) ||
+ !afb_event_is_valid(event_tts_prompt_completed) ||
+ !afb_event_is_valid(event_stt_result))
+ {
+ AFB_ERROR("Failed to create events!");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int preinit()
+{
+ AFB_NOTICE("preinit was called");
+ return 0;
+}
+
+static void onevent(const char *event, struct json_object *object)
+{
+ AFB_NOTICE("onevent called with event %s", event);
+}
+
+const struct afb_binding_v2 afbBindingV2 = {
+ .api = "agl-speech",
+ .specification = NULL,
+ .verbs = verbs,
+ .preinit = preinit,
+ .init = init,
+ .onevent = onevent,
+ .noconcurrency = 0
+};