1 gpsnavi: Add AGL 4A playback support
3 To properly support 4A on AGL, pull in the binding, add some code
4 to open / close the "navigation" role, and use a simple gstreamer
5 pipeline to play the generated files. The existing script templates
6 have had their playback commands removed, but are retained for
7 driving the voice file generation.
9 Signed-off-by: Scott Murray <scott.murray@konsulko.com>
11 diff --git a/agl/config.xml b/agl/config.xml
12 index 9d4c0ca..960f652 100755
16 <author>AISIN AW</author>
17 <feature name="urn:AGL:widget:required-permission">
18 <param name="urn:AGL:permission::public:no-htdocs" value="required" />
19 + <param name="urn:AGL:permission:audio:public:audiostream" value="required" />
20 <param name="http://tizen.org/privilege/internal/dbus" value="required" />
22 <feature name="urn:AGL:widget:required-api">
23 <param name="homescreen" value="ws" />
24 <param name="windowmanager" value="ws" />
25 + <param name="ahl-4a" value="ws" />
27 <license>GPL</license>
28 <feature name="urn:AGL:widget:file-properties">
29 diff --git a/configure.ac b/configure.ac
30 index 33348c3..6b7b391 100755
33 @@ -26,11 +26,13 @@ PKG_CHECK_MODULES([GLIB], [glib-2.0 gthread-2.0])
34 PKG_CHECK_MODULES([FREETYPE2], [freetype2])
35 PKG_CHECK_MODULES([WAYLAND], [wayland-client wayland-egl egl])
36 PKG_CHECK_MODULES([GL], [glesv2])
37 +PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0])
38 PKG_CHECK_MODULES([ZLIB], [zlib])
39 PKG_CHECK_MODULES([SQLITE3], [sqlite3])
40 PKG_CHECK_MODULES([EXPAT], [expat])
41 PKG_CHECK_MODULES([OPENSSL], [openssl])
42 PKG_CHECK_MODULES([DBUSCXX], [dbus-c++-1])
43 +PKG_CHECK_MODULES([LIBAFBWSC], [libafbwsc])
44 PKG_CHECK_MODULES([WINDOWMANAGER], [libwindowmanager])
45 PKG_CHECK_MODULES([HOMESCREEN], [libhomescreen])
47 diff --git a/flite_agl.in b/flite_agl.in
48 index 28b512c..d11b043 100644
54 echo "$1" | flite_hts_engine -m @datadir@/Voice/us/cmu_us_arctic_slt.htsvoice -o $TMP
55 -paplay --property='media.role=Navi' $TMP
58 diff --git a/jtalk_agl.in b/jtalk_agl.in
59 index 76900f4..857c824 100644
65 echo "$1" | open_jtalk -ow $TMP -m @exec_prefix@/share/Voice/mei/mei_normal.htsvoice -x @exec_prefix@/share/dic/
66 -paplay --property='media.role=Navi' $TMP
69 diff --git a/src/Makefile.am b/src/Makefile.am
70 index affb9a5..6d0fa55 100755
73 @@ -26,6 +26,7 @@ sms/sms-core/SMCoreDM/RG/RG_GuideNear.c \
74 sms/sms-core/SMCoreDM/RG/RG_ShareData.c \
75 sms/sms-core/SMCoreDM/RG/RG_GuideVoiceBuild_en.c \
76 sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c \
77 +sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp \
78 sms/sms-core/SMCoreDM/RG/RG_GuideApi.c \
79 sms/sms-core/SMCoreDM/SCRTThread.c \
80 sms/sms-core/SMCoreDAL/SMDALAreaCls.c \
81 @@ -537,6 +538,8 @@ libnavicore_la_CFLAGS = -fPIC \
85 + @GSTREAMER_CFLAGS@ \
86 + @LIBAFBWSC_CFLAGS@ \
90 @@ -547,6 +550,8 @@ libnavicore_la_CXXFLAGS = -fPIC \
94 + @GSTREAMER_CFLAGS@ \
95 + @LIBAFBWSC_CFLAGS@ \
99 @@ -555,6 +560,8 @@ libnavicore_la_CXXFLAGS = -fPIC \
100 libnavicore_la_LIBADD = \
108 diff --git a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
109 index 3828d5c..36e6775 100755
110 --- a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
111 +++ b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
114 #include "sms-core/SMCoreDM/SMCoreDMInternal.h"
\r
116 +extern int play_voice(const char* voice_gen_cmd);
\r
118 typedef struct _tts_text_tbl
\r
121 @@ -205,9 +207,9 @@ E_SC_RESULT RG_CTL_CreateVoiceText(RT_NAME_t *in, INT32 language)
125 - strncat(tts_voice, "\" & ", (TTSMAX - len - 1));
\r
126 + strncat(tts_voice, "\"", (TTSMAX - len - 1));
\r
128 - system(tts_voice);
\r
129 + play_voice(tts_voice);
\r
132 return (e_SC_RESULT_SUCCESS);
\r
133 diff --git a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp
135 index 0000000..6b59c0e
137 +++ b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp
140 + * Copyright (C) 2018 Konsulko Group
141 + * Author: Scott Murray <scott.murray@konsulko.com>
143 + * This program is licensed under GPL version 2 license.
144 + * See the LICENSE file distributed with this source file.
151 +#include <sys/types.h>
152 +#include <sys/stat.h>
153 +#include <pthread.h>
154 +#include <gst/gst.h>
155 +#include <json-c/json.h>
159 +#include <afb/afb-wsj1.h>
160 +#include <afb/afb-ws-client.h>
161 +#include <systemd/sd-event.h>
164 +#define NAVI_TMPFILE "/tmp/navi.wav"
166 +static struct afb_wsj1* ws;
167 +static struct afb_wsj1_itf itf;
170 +// port and token from src/glview/glview_wayland.cpp
172 +extern std::string g_token;
174 +static int set_role_state(bool state);
176 +void play_voice_file(const char *output)
181 + // Initialize GStreamer
182 + gst_init(NULL, NULL);
184 + std::string pipeline_str = "filesrc location=";
185 + pipeline_str += NAVI_TMPFILE;
186 + pipeline_str += " ! wavparse ! audioconvert ! audioresample ! alsasink device=";
187 + pipeline_str += output;
188 + GstElement *pipeline = gst_parse_launch(pipeline_str.c_str(), NULL);
190 + std::cerr << "gstreamer pipeline construction failed!" << std::endl;
195 + gst_element_set_state(pipeline, GST_STATE_PLAYING);
196 + std::cerr << "Playing guidance" << std::endl;
198 + // Wait until error or EOS
199 + GstBus *bus = gst_element_get_bus(pipeline);
200 + GstMessage *msg = gst_bus_timed_pop_filtered(bus,
201 + GST_CLOCK_TIME_NONE,
202 + (GstMessageType) (GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
206 + gst_message_unref(msg);
207 + gst_object_unref(bus);
208 + gst_element_set_state(pipeline, GST_STATE_NULL);
209 + gst_object_unref(pipeline);
211 + // Remove temporary file
212 + unlink(NAVI_TMPFILE);
217 +static void on_hangup(void *closure, struct afb_wsj1 *wsj)
221 +static void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
225 +static void on_event(void* closure, const char* event, struct afb_wsj1_msg *msg)
229 +static void on_reply(void *closure, struct afb_wsj1_msg *msg)
231 + bool state = (bool) closure;
234 + // Role is closed, return
238 + // We opened the role, play the file
239 + struct json_object* reply = afb_wsj1_msg_object_j(msg);
241 + struct json_object* response;
242 + int rc = json_object_object_get_ex(reply, "response", &response);
244 + struct json_object* val;
245 + rc = json_object_object_get_ex(response, "device_uri", &val);
246 + if (rc && json_object_get_string_len(val)) {
247 + const char* jres_pcm = json_object_get_string(val);
248 + play_voice_file(jres_pcm);
253 + // Give up role now that we're done
254 + set_role_state(false);
257 +static void *event_loop_run(void *args)
259 + struct sd_event* loop = (struct sd_event*)(args);
262 + sd_event_run(loop, 30000000);
265 +static int start_event_loop(void)
268 + pthread_t thread_id;
269 + if(pthread_create(&thread_id, NULL, event_loop_run, loop) != 0) {
279 +static int init_ws(int port, std::string &token)
284 + if(sd_event_default(&loop) < 0) {
285 + std::cerr << __FUNCTION__ << ": Failed to create event loop" << std::endl;
289 + // Initialize interface for websocket
290 + itf.on_hangup = on_hangup;
291 + itf.on_call = on_call;
292 + itf.on_event = on_event;
294 + uri = "ws://localhost:" + std::to_string(port) + "/api?token=" + token;
295 + std::cerr << "Using URI: " << uri << std::endl;
296 + ws = afb_ws_client_connect_wsj1(loop, uri.c_str(), &itf, NULL);
298 + std::cerr << __FUNCTION__ << ": Failed to create websocket connection" << std::endl;
301 + start_event_loop();
305 + sd_event_unref(loop);
310 +static int set_role_state(bool state)
313 + json_object *jsonData = json_object_new_object();
315 + json_object_object_add(jsonData, "action", json_object_new_string(state ? "open" : "close"));
316 + rc = afb_wsj1_call_j(ws, "ahl-4a", "navigation", jsonData, on_reply, (void*) state);
318 + std::cerr << __FUNCTION__ << ": Failed to call ahl-4a/navigation!" << std::endl;
323 +pthread_mutex_t ws_mutex = PTHREAD_MUTEX_INITIALIZER;
325 +static void *play_voice_handler(void *data)
328 + char *voice_gen_cmd = (char*) data;
332 + pthread_mutex_lock(&ws_mutex);
334 + rc = init_ws(g_port, g_token);
335 + pthread_mutex_unlock(&ws_mutex);
339 + pthread_mutex_unlock(&ws_mutex);
341 + // Generate guidance voice file
342 + rc = system(voice_gen_cmd);
343 + free(voice_gen_cmd);
345 + // Try to get role and play file
346 + set_role_state(true);
351 +extern "C" int play_voice(const char* voice_gen_cmd)
353 + pthread_t handler_thread;
359 + tmp = strdup(voice_gen_cmd);
360 + return pthread_create(&handler_thread, NULL, play_voice_handler, (void*) tmp);