summaryrefslogtreecommitdiffstats
path: root/recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch')
-rw-r--r--recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch361
1 files changed, 361 insertions, 0 deletions
diff --git a/recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch b/recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch
new file mode 100644
index 000000000..b4192358c
--- /dev/null
+++ b/recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch
@@ -0,0 +1,361 @@
+gpsnavi: Add AGL 4A playback support
+
+To properly support 4A on AGL, pull in the binding, add some code
+to open / close the "navigation" role, and use a simple gstreamer
+pipeline to play the generated files. The existing script templates
+have had their playback commands removed, but are retained for
+driving the voice file generation.
+
+Signed-off-by: Scott Murray <scott.murray@konsulko.com>
+
+diff --git a/agl/config.xml b/agl/config.xml
+index 9d4c0ca..960f652 100755
+--- a/agl/config.xml
++++ b/agl/config.xml
+@@ -7,11 +7,13 @@
+ <author>AISIN AW</author>
+ <feature name="urn:AGL:widget:required-permission">
+ <param name="urn:AGL:permission::public:no-htdocs" value="required" />
++ <param name="urn:AGL:permission:audio:public:audiostream" value="required" />
+ <param name="http://tizen.org/privilege/internal/dbus" value="required" />
+ </feature>
+ <feature name="urn:AGL:widget:required-api">
+ <param name="homescreen" value="ws" />
+ <param name="windowmanager" value="ws" />
++ <param name="ahl-4a" value="ws" />
+ </feature>
+ <license>GPL</license>
+ <feature name="urn:AGL:widget:file-properties">
+diff --git a/configure.ac b/configure.ac
+index 33348c3..6b7b391 100755
+--- a/configure.ac
++++ b/configure.ac
+@@ -26,11 +26,13 @@ PKG_CHECK_MODULES([GLIB], [glib-2.0 gthread-2.0])
+ PKG_CHECK_MODULES([FREETYPE2], [freetype2])
+ PKG_CHECK_MODULES([WAYLAND], [wayland-client wayland-egl egl])
+ PKG_CHECK_MODULES([GL], [glesv2])
++PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0])
+ PKG_CHECK_MODULES([ZLIB], [zlib])
+ PKG_CHECK_MODULES([SQLITE3], [sqlite3])
+ PKG_CHECK_MODULES([EXPAT], [expat])
+ PKG_CHECK_MODULES([OPENSSL], [openssl])
+ PKG_CHECK_MODULES([DBUSCXX], [dbus-c++-1])
++PKG_CHECK_MODULES([LIBAFBWSC], [libafbwsc])
+ PKG_CHECK_MODULES([WINDOWMANAGER], [libwindowmanager])
+ PKG_CHECK_MODULES([HOMESCREEN], [libhomescreen])
+
+diff --git a/flite_agl.in b/flite_agl.in
+index 28b512c..d11b043 100644
+--- a/flite_agl.in
++++ b/flite_agl.in
+@@ -1,6 +1,3 @@
+ #!/bin/sh
+ TMP=/tmp/navi.wav
+ echo "$1" | flite_hts_engine -m @datadir@/Voice/us/cmu_us_arctic_slt.htsvoice -o $TMP
+-paplay --property='media.role=Navi' $TMP
+-rm -f $TMP
+-
+diff --git a/jtalk_agl.in b/jtalk_agl.in
+index 76900f4..857c824 100644
+--- a/jtalk_agl.in
++++ b/jtalk_agl.in
+@@ -1,6 +1,3 @@
+ #!/bin/sh
+ TMP=/tmp/navi.wav
+ echo "$1" | open_jtalk -ow $TMP -m @exec_prefix@/share/Voice/mei/mei_normal.htsvoice -x @exec_prefix@/share/dic/
+-paplay --property='media.role=Navi' $TMP
+-rm -f $TMP
+-
+diff --git a/src/Makefile.am b/src/Makefile.am
+index affb9a5..6d0fa55 100755
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -26,6 +26,7 @@ sms/sms-core/SMCoreDM/RG/RG_GuideNear.c \
+ sms/sms-core/SMCoreDM/RG/RG_ShareData.c \
+ sms/sms-core/SMCoreDM/RG/RG_GuideVoiceBuild_en.c \
+ sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c \
++sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp \
+ sms/sms-core/SMCoreDM/RG/RG_GuideApi.c \
+ sms/sms-core/SMCoreDM/SCRTThread.c \
+ sms/sms-core/SMCoreDAL/SMDALAreaCls.c \
+@@ -537,6 +538,8 @@ libnavicore_la_CFLAGS = -fPIC \
+ @FREETYPE2_CFLAGS@ \
+ @WAYLAND_CFLAGS@ \
+ @GL_CFLAGS@ \
++ @GSTREAMER_CFLAGS@ \
++ @LIBAFBWSC_CFLAGS@ \
+ @ZLIB_CFLAGS@ \
+ @SQLITE3_CFLAGS@ \
+ @EXPAT_CFLAGS@ \
+@@ -547,6 +550,8 @@ libnavicore_la_CXXFLAGS = -fPIC \
+ @FREETYPE2_CFLAGS@ \
+ @WAYLAND_CFLAGS@ \
+ @GL_CFLAGS@ \
++ @GSTREAMER_CFLAGS@ \
++ @LIBAFBWSC_CFLAGS@ \
+ @ZLIB_CFLAGS@ \
+ @SQLITE3_CFLAGS@ \
+ @EXPAT_CFLAGS@ \
+@@ -555,6 +560,8 @@ libnavicore_la_CXXFLAGS = -fPIC \
+ libnavicore_la_LIBADD = \
+ @OPENSSL_LIBS@ \
+ @GL_LIBS@ \
++ @GSTREAMER_LIBS@ \
++ @LIBAFBWSC_LIBS@ \
+ @ZLIB_LIBS@ \
+ @SQLITE3_LIBS@ \
+ @EXPAT_LIBS@
+diff --git a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
+index 3828d5c..36e6775 100755
+--- a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
++++ b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
+@@ -16,6 +16,8 @@
+
+ #include "sms-core/SMCoreDM/SMCoreDMInternal.h"
+
++extern int play_voice(const char* voice_gen_cmd);
++
+ typedef struct _tts_text_tbl
+ {
+ INT32 code;
+@@ -205,9 +207,9 @@ E_SC_RESULT RG_CTL_CreateVoiceText(RT_NAME_t *in, INT32 language)
+ }
+ else
+ {
+- strncat(tts_voice, "\" & ", (TTSMAX - len - 1));
++ strncat(tts_voice, "\"", (TTSMAX - len - 1));
+
+- system(tts_voice);
++ play_voice(tts_voice);
+ }
+
+ return (e_SC_RESULT_SUCCESS);
+diff --git a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp
+new file mode 100644
+index 0000000..6b59c0e
+--- /dev/null
++++ b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp
+@@ -0,0 +1,223 @@
++/*
++ * Copyright (C) 2018 Konsulko Group
++ * Author: Scott Murray <scott.murray@konsulko.com>
++ *
++ * This program is licensed under GPL version 2 license.
++ * See the LICENSE file distributed with this source file.
++ */
++
++#include <string>
++#include <iostream>
++#include <cstring>
++#include <unistd.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <pthread.h>
++#include <gst/gst.h>
++#include <json-c/json.h>
++
++extern "C"
++{
++#include <afb/afb-wsj1.h>
++#include <afb/afb-ws-client.h>
++#include <systemd/sd-event.h>
++}
++
++#define NAVI_TMPFILE "/tmp/navi.wav"
++
++static struct afb_wsj1* ws;
++static struct afb_wsj1_itf itf;
++sd_event* loop;
++
++// port and token from src/glview/glview_wayland.cpp
++extern long g_port;
++extern std::string g_token;
++
++static int set_role_state(bool state);
++
++void play_voice_file(const char *output)
++{
++ if(!output)
++ return;
++
++ // Initialize GStreamer
++ gst_init(NULL, NULL);
++
++ std::string pipeline_str = "filesrc location=";
++ pipeline_str += NAVI_TMPFILE;
++ pipeline_str += " ! wavparse ! audioconvert ! audioresample ! alsasink device=";
++ pipeline_str += output;
++ GstElement *pipeline = gst_parse_launch(pipeline_str.c_str(), NULL);
++ if(!pipeline) {
++ std::cerr << "gstreamer pipeline construction failed!" << std::endl;
++ return;
++ }
++
++ // Start pipeline
++ gst_element_set_state(pipeline, GST_STATE_PLAYING);
++ std::cerr << "Playing guidance" << std::endl;
++
++ // Wait until error or EOS
++ GstBus *bus = gst_element_get_bus(pipeline);
++ GstMessage *msg = gst_bus_timed_pop_filtered(bus,
++ GST_CLOCK_TIME_NONE,
++ (GstMessageType) (GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
++
++ // Free resources
++ if(msg != NULL)
++ gst_message_unref(msg);
++ gst_object_unref(bus);
++ gst_element_set_state(pipeline, GST_STATE_NULL);
++ gst_object_unref(pipeline);
++
++ // Remove temporary file
++ unlink(NAVI_TMPFILE);
++
++ return;
++}
++
++static void on_hangup(void *closure, struct afb_wsj1 *wsj)
++{
++}
++
++static void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
++{
++}
++
++static void on_event(void* closure, const char* event, struct afb_wsj1_msg *msg)
++{
++}
++
++static void on_reply(void *closure, struct afb_wsj1_msg *msg)
++{
++ bool state = (bool) closure;
++
++ if(!state) {
++ // Role is closed, return
++ return;
++ }
++
++ // We opened the role, play the file
++ struct json_object* reply = afb_wsj1_msg_object_j(msg);
++ if(reply) {
++ struct json_object* response;
++ int rc = json_object_object_get_ex(reply, "response", &response);
++ if(rc) {
++ struct json_object* val;
++ rc = json_object_object_get_ex(response, "device_uri", &val);
++ if (rc && json_object_get_string_len(val)) {
++ const char* jres_pcm = json_object_get_string(val);
++ play_voice_file(jres_pcm);
++ }
++ }
++ }
++
++ // Give up role now that we're done
++ set_role_state(false);
++}
++
++static void *event_loop_run(void *args)
++{
++ struct sd_event* loop = (struct sd_event*)(args);
++
++ for(;;)
++ sd_event_run(loop, 30000000);
++}
++
++static int start_event_loop(void)
++{
++ if(ws && loop) {
++ pthread_t thread_id;
++ if(pthread_create(&thread_id, NULL, event_loop_run, loop) != 0) {
++ return -1;
++ } else {
++ return thread_id;
++ }
++ } else {
++ return -1;
++ }
++}
++
++static int init_ws(int port, std::string &token)
++{
++ loop = NULL;
++ std::string uri;
++
++ if(sd_event_default(&loop) < 0) {
++ std::cerr << __FUNCTION__ << ": Failed to create event loop" << std::endl;
++ goto error;
++ }
++
++ // Initialize interface for websocket
++ itf.on_hangup = on_hangup;
++ itf.on_call = on_call;
++ itf.on_event = on_event;
++
++ uri = "ws://localhost:" + std::to_string(port) + "/api?token=" + token;
++ std::cerr << "Using URI: " << uri << std::endl;
++ ws = afb_ws_client_connect_wsj1(loop, uri.c_str(), &itf, NULL);
++ if(ws == NULL) {
++ std::cerr << __FUNCTION__ << ": Failed to create websocket connection" << std::endl;
++ goto error;
++ }
++ start_event_loop();
++ return 0;
++error:
++ if(loop) {
++ sd_event_unref(loop);
++ }
++ return -1;
++}
++
++static int set_role_state(bool state)
++{
++ int rc;
++ json_object *jsonData = json_object_new_object();
++
++ json_object_object_add(jsonData, "action", json_object_new_string(state ? "open" : "close"));
++ rc = afb_wsj1_call_j(ws, "ahl-4a", "navigation", jsonData, on_reply, (void*) state);
++ if (rc < 0) {
++ std::cerr << __FUNCTION__ << ": Failed to call ahl-4a/navigation!" << std::endl;
++ }
++ return rc;
++}
++
++pthread_mutex_t ws_mutex = PTHREAD_MUTEX_INITIALIZER;
++
++static void *play_voice_handler(void *data)
++{
++ int rc;
++ char *voice_gen_cmd = (char*) data;
++ if(!voice_gen_cmd)
++ return NULL;
++
++ pthread_mutex_lock(&ws_mutex);
++ if(!ws) {
++ rc = init_ws(g_port, g_token);
++ pthread_mutex_unlock(&ws_mutex);
++ if(rc < 0)
++ return NULL;
++ }
++ pthread_mutex_unlock(&ws_mutex);
++
++ // Generate guidance voice file
++ rc = system(voice_gen_cmd);
++ free(voice_gen_cmd);
++
++ // Try to get role and play file
++ set_role_state(true);
++
++ return NULL;
++}
++
++extern "C" int play_voice(const char* voice_gen_cmd)
++{
++ pthread_t handler_thread;
++ char *tmp;
++
++ if(!voice_gen_cmd)
++ return -1;
++
++ tmp = strdup(voice_gen_cmd);
++ return pthread_create(&handler_thread, NULL, play_voice_handler, (void*) tmp);
++}