From 5f2a3caabafcb4476ba4f9aab29d55a12e81cf01 Mon Sep 17 00:00:00 2001 From: brianjjones Date: Thu, 22 Aug 2013 10:26:03 +0300 Subject: [PATCH 01/26] Adding a spec file and updating the run-speech-daemon.sh to work on Tizen IVI. Change-Id: Ic81e4ab4a0b4e464a74206a35b2666b6e34839a7 --- packaging/speech-recognition.spec | 54 +++++++++++++++++++++++++++++++ run-speech-daemon.sh | 6 ++-- 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 packaging/speech-recognition.spec diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec new file mode 100644 index 0000000..85c5b34 --- /dev/null +++ b/packaging/speech-recognition.spec @@ -0,0 +1,54 @@ +Summary: Speech recognition for Tizen +Name: speech-recognition +Version: 0.0.1 +Release: 0 +License: LGPLv2.1 +Group: System Environment/Daemons +URL: https://github.com/otcshare/speech-recognition +Source0: %{name}-%{version}.tar.gz +#BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root +BuildRequires: pkgconfig(pocketsphinx) +BuildRequires: pkgconfig(sphinxbase) +BuildRequires: pkgconfig(libpulse) +BuildRequires: pkgconfig(murphy-common) +BuildRequires: pkgconfig(murphy-pulse) +BuildRequires: pkgconfig(dbus-1) +BuildRequires: pkgconfig(libudev) +BuildRequires: pkgconfig(json) +Requires: pulseaudio + +%description +This package contains a pulseaudio module that enforces (mostly audio) routing, +corking and muting policy decisions. + +%prep +%setup -q -n %{name}-%{version} + +%build +./bootstrap --prefix=/usr --sysconfdir=/etc --libdir=/usr/lib \ + --enable-gpl --enable-dbus --enable-sphinx + +make + +%install +rm -rf %{buildroot} +%make_install + +mkdir -p %{buildroot}/usr/share/srs +cp run-speech-daemon.sh %{buildroot}/usr/share/srs +cp speech-recognition.conf %{buildroot}/usr/share/srs +cp demo/dictionary/demo.* %{buildroot}/usr/share/srs + + +%clean +rm -rf %{buildroot} + +%files +%defattr(-,root,root,-) +/usr/bin/srs-client +/usr/lib/src/plugins/plugin-* +/usr/sbin/srs-daemon +/usr/share/doc/speech-recognition/* +/usr/share/srs/demo.* +/usr/share/srs/speech-recognition.conf +/usr/share/srs/run-speech-daemon.sh diff --git a/run-speech-daemon.sh b/run-speech-daemon.sh index 5ccbb95..50b877c 100755 --- a/run-speech-daemon.sh +++ b/run-speech-daemon.sh @@ -1,7 +1,7 @@ #!/bin/bash VERBOSITY=${VERBOSITY:--vvv} -PLUGINDIR=${PLUGINDIR:-$(pwd)/src/.libs} +PLUGINDIR=${PLUGINDIR:-/usr/lib/src/plugins/} CONFIGFILE=${CONFIGFILE:-$(pwd)/speech-recognition.conf} plugins="simple-disambiguator sphinx-speech simple-voice" @@ -76,7 +76,7 @@ EXTRA_OPTIONS="" while [ -n "$1" ]; do case $1 in --valgrind) - export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PUGINDIR" + export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PLUGINDIR" opts="${1#--valgrind}" case $opts in =*) xeq="valgrind ${opts#=}";; @@ -90,5 +90,5 @@ while [ -n "$1" ]; do shift done -$xeq ./src/srs-daemon -f $VERBOSITY -P $PLUGINDIR -c $CONFIGFILE \ +$xeq srs-daemon -f $VERBOSITY -P $PLUGINDIR -c $CONFIGFILE \ $LOAD_OPTIONS $PULSESRC $EXTRA_OPTIONS From 8002538d27e5cb67a8d329b905980bb9ae611bb5 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Thu, 22 Aug 2013 13:54:23 +0300 Subject: [PATCH 02/26] packaging: updated spec file, added config and systemd service files. Change-Id: Ibf1b08df6f56d4234aa04e1c08ba745927a664e1 --- packaging/speech-recognition.changes | 2 + packaging/speech-recognition.conf | 12 +++++ packaging/speech-recognition.env | 11 ++++ packaging/speech-recognition.service | 11 ++++ packaging/speech-recognition.spec | 78 +++++++++++++++++++--------- 5 files changed, 90 insertions(+), 24 deletions(-) create mode 100644 packaging/speech-recognition.changes create mode 100644 packaging/speech-recognition.conf create mode 100644 packaging/speech-recognition.env create mode 100644 packaging/speech-recognition.service diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes new file mode 100644 index 0000000..c35cb66 --- /dev/null +++ b/packaging/speech-recognition.changes @@ -0,0 +1,2 @@ +* Thu Aug 22 10:20:28 EEST 2013 Krisztian Litkey - 0.0.1 +- Initial packaging/release for Tizen IVI. diff --git a/packaging/speech-recognition.conf b/packaging/speech-recognition.conf new file mode 100644 index 0000000..19d702a --- /dev/null +++ b/packaging/speech-recognition.conf @@ -0,0 +1,12 @@ +# default mike input +sphinx.pulsesrc = alsa_input.usb-Logitech_Logitech_USB_Microphone-00-Microphone.analog-mono +# language models and dictionaries +sphinx.hmm = /usr/share/pocketsphinx/model/hmm/en_US/hub4wsj_sc_8k +sphinx.lm = /usr/share/speech-recognition/dictionaries/demo/demo.DMP +sphinx.dict = /usr/share/speech-recognition/dictionaries/demo/demo.dic +sphinx.fsg = /usr/share/speech-recognition/dictionaries/demo/demo.fsg +sphinx.decoder = general +sphinx.general.lm = /usr/share/pocketsphinx/model/lm/en_US/wsj0vp.5000.DMP +sphinx.general.dict = /usr/share/pocketsphinx/model/lm/en_US/cmu07a.dic +# search plugin command +search.command = /usr/bin/MiniBrowser "http://google.com/search?q=__url__" diff --git a/packaging/speech-recognition.env b/packaging/speech-recognition.env new file mode 100644 index 0000000..59fd4cb --- /dev/null +++ b/packaging/speech-recognition.env @@ -0,0 +1,11 @@ +# PulseAudio source to use for the mike +MIKE="-s sphinx.pulsesrc=alsa_input.usb-Logitech_Logitech_USB_Microphone-00-Microphone.analog-mono" + +# built-in clients to load +CLIENTS="-s gmainloop=true -L wrt-media-client" + +# plugins to load +PLUGINS="-L simple-disambiguator -L sphinx-speech" + +# basic configuration file +CONFIG="-c /etc/speech-recognition/speech-recognition.conf -P /usr/lib/srs/plugins" diff --git a/packaging/speech-recognition.service b/packaging/speech-recognition.service new file mode 100644 index 0000000..5d0a79d --- /dev/null +++ b/packaging/speech-recognition.service @@ -0,0 +1,11 @@ +[Unit] +Description=Speech recognition service +After=sound.target + +[Service] +EnvironmentFile=/etc/sysconfig/speech-recognition +ExecStart=/usr/sbin/srs-daemon -f $CONFIG $MIKE $PLUGINS $CLIENTS +Type=simple + +[Install] +WantedBy=tizen-middleware.target diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 85c5b34..1d6187a 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -1,54 +1,84 @@ -Summary: Speech recognition for Tizen +Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.1 +Version: 0.0.1 Release: 0 -License: LGPLv2.1 -Group: System Environment/Daemons +License: BSD-3-Clause +Group: Base/Utilities URL: https://github.com/otcshare/speech-recognition Source0: %{name}-%{version}.tar.gz -#BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root + BuildRequires: pkgconfig(pocketsphinx) BuildRequires: pkgconfig(sphinxbase) BuildRequires: pkgconfig(libpulse) BuildRequires: pkgconfig(murphy-common) BuildRequires: pkgconfig(murphy-pulse) +BuildRequires: pkgconfig(murphy-glib) BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(json) + Requires: pulseaudio +Requires: sphinxbase +Requires: pocketsphinx %description -This package contains a pulseaudio module that enforces (mostly audio) routing, -corking and muting policy decisions. +SRS/Winthorpe speech recognition system service. + +%package doc +Summary: Documentation +Group: Development/Tools + +%description doc +Documentation for the speech recognition service. %prep %setup -q -n %{name}-%{version} %build -./bootstrap --prefix=/usr --sysconfdir=/etc --libdir=/usr/lib \ - --enable-gpl --enable-dbus --enable-sphinx - -make +./bootstrap && \ + %configure --disable-gpl --disable-dbus \ + --enable-sphinx --enable-wrt-client && \ + make %install -rm -rf %{buildroot} +rm -fr $RPM_BUILD_ROOT + %make_install -mkdir -p %{buildroot}/usr/share/srs -cp run-speech-daemon.sh %{buildroot}/usr/share/srs -cp speech-recognition.conf %{buildroot}/usr/share/srs -cp demo/dictionary/demo.* %{buildroot}/usr/share/srs +# Install dictionaries, configuration and service files. +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig \ + $RPM_BUILD_ROOT/lib/systemd/user \ + $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition \ + $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo +/usr/bin/install -m 644 packaging/speech-recognition.conf \ + $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition +/usr/bin/install -m 644 packaging/speech-recognition.env \ + $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/speech-recognition +/usr/bin/install -m 644 packaging/speech-recognition.service \ + $RPM_BUILD_ROOT/lib/systemd/user +/usr/bin/install -m 644 \ + -t $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo \ + dictionaries/demo/demo.* %clean -rm -rf %{buildroot} +rm -rf $RPM_BUILD_ROOT + +%post +ldconfig + +%postun +ldconfig %files %defattr(-,root,root,-) -/usr/bin/srs-client -/usr/lib/src/plugins/plugin-* -/usr/sbin/srs-daemon -/usr/share/doc/speech-recognition/* -/usr/share/srs/demo.* -/usr/share/srs/speech-recognition.conf -/usr/share/srs/run-speech-daemon.sh +%{_sbindir}/srs-daemon +%{_libdir}/srs +%{_sysconfdir}/speech-recognition/speech-recognition.conf +%{_sysconfdir}/sysconfig/speech-recognition +%{_datadir}/speech-recognition/dictionaries +/lib/systemd/user/speech-recognition.service + +%files doc +%defattr(-,root,root,-) +%doc AUTHORS COPYING ChangeLog INSTALL NEWS README From cb97d11690943701f575f2599983dbcbef7bc5f8 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Fri, 6 Sep 2013 01:21:56 +0300 Subject: [PATCH 03/26] packaging: enabled festival support. Change-Id: Ic2dd1908c45e32ac7adaf5a3225ae391a75cde52 --- packaging/speech-recognition.spec | 65 ++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 1d6187a..d15f7b0 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -1,3 +1,9 @@ +%{!?_with_debug:%{!?_without_debug:%define _with_debug 1}} +%{!?_with_sphinx:%{!?_without_sphinx:%define _with_sphinx 1}} +%{!?_with_festival:%{!?_without_festival:%define _with_festival 1}} +%{!?_with_wrt:%{!?_without_wrt:%define _with_wrt 1}} +%{!?_with_dbus:%{!?_without_dbus:%define _without_dbus 1}} + Summary: Speech recognition service for Tizen Name: speech-recognition Version: 0.0.1 @@ -7,19 +13,33 @@ Group: Base/Utilities URL: https://github.com/otcshare/speech-recognition Source0: %{name}-%{version}.tar.gz -BuildRequires: pkgconfig(pocketsphinx) -BuildRequires: pkgconfig(sphinxbase) BuildRequires: pkgconfig(libpulse) BuildRequires: pkgconfig(murphy-common) BuildRequires: pkgconfig(murphy-pulse) BuildRequires: pkgconfig(murphy-glib) -BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(json) +%if %{?_with_sphinx:1}%{!?_with_sphinx:0} +BuildRequires: pkgconfig(pocketsphinx) +BuildRequires: pkgconfig(sphinxbase) +%endif +%if %{?_with_festival:1}%{!?_with_festival:0} +BuildRequires: festival-devel +%endif +%if %{?_with_dbus:1}%{!?_with_dbus:0} +BuildRequires: pkgconfig(dbus-1) +%endif Requires: pulseaudio +%if %{?_with_sphinx:1}%{!?_with_sphinx:0} Requires: sphinxbase Requires: pocketsphinx +%endif +%if %{?_with_festival:1}%{!?_with_festival:0} +BuildRequires: festival-devel +Requires: festival +%endif + %description SRS/Winthorpe speech recognition system service. @@ -35,9 +55,41 @@ Documentation for the speech recognition service. %setup -q -n %{name}-%{version} %build +%if %{?_with_debug:1}%{!?_with_debug:0} +export CFLAGS="-O0 -g3" +export CXXFLAGS="-O0 -g3" +V="V=1" +%endif + +CONFIG_OPTIONS="" + +%if %{?_with_sphinx:1}%{!?_with_sphinx:0} +CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-sphinx" +%else +CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-sphinx" +%endif + +%if %{?_with_festival:1}%{!?_with_festival:0} +CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-festival" +%else +CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-festival" +%endif + +%if %{?_with_wrt:1}%{!?_with_wrt:0} +CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-wrt-client" +%else +CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-wrt-client" +%endif + +%if %{?_with_dbus:1}%{!?_with_dbus:0} +CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-dbus" +%else +CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-dbus" +%endif + + ./bootstrap && \ - %configure --disable-gpl --disable-dbus \ - --enable-sphinx --enable-wrt-client && \ + %configure $CONFIG_OPTIONS && \ make %install @@ -73,6 +125,9 @@ ldconfig %files %defattr(-,root,root,-) %{_sbindir}/srs-daemon +%if %{?_with_dbus:1}%{!?_with_dbus:0} +%{_bindir}/srs-client +%endif %{_libdir}/srs %{_sysconfdir}/speech-recognition/speech-recognition.conf %{_sysconfdir}/sysconfig/speech-recognition From 0e02d5a3d58a415f2f043304300061175f152e54 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Wed, 11 Sep 2013 22:29:04 +0300 Subject: [PATCH 04/26] packaging: load festival-voice TTS backend. Change-Id: Icbbb81dcd0aefa392198e59cb20f88aef638971c --- packaging/speech-recognition.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/speech-recognition.env b/packaging/speech-recognition.env index 59fd4cb..eab26d8 100644 --- a/packaging/speech-recognition.env +++ b/packaging/speech-recognition.env @@ -5,7 +5,7 @@ MIKE="-s sphinx.pulsesrc=alsa_input.usb-Logitech_Logitech_USB_Microphone-00-Micr CLIENTS="-s gmainloop=true -L wrt-media-client" # plugins to load -PLUGINS="-L simple-disambiguator -L sphinx-speech" +PLUGINS="-L simple-disambiguator -L sphinx-speech -L festival-loader -L festival-voice" # basic configuration file CONFIG="-c /etc/speech-recognition/speech-recognition.conf -P /usr/lib/srs/plugins" From ab75611220c40b09cc1cc1f3dfe8c5a096b903fa Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Fri, 6 Sep 2013 01:27:59 +0300 Subject: [PATCH 05/26] packaging: updated changelog and bumped version. Change-Id: Ic951b5cc4cc1f6957357cd0c36653425419c237c --- packaging/speech-recognition.changes | 4 ++++ packaging/speech-recognition.spec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index c35cb66..d0ece0c 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,2 +1,6 @@ +* Wed Sep 11 22:41:20 EEST 2013 Krisztian Litkey - 0.0.2 +- Added and enabled voice/TTS support. +- Fix a shutdown code path corruption. + * Thu Aug 22 10:20:28 EEST 2013 Krisztian Litkey - 0.0.1 - Initial packaging/release for Tizen IVI. diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index d15f7b0..e40eb70 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -6,7 +6,7 @@ Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.1 +Version: 0.0.2 Release: 0 License: BSD-3-Clause Group: Base/Utilities From b030eace0465dd8558a02802a5d8cf402ed09079 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Thu, 12 Sep 2013 14:02:23 +0300 Subject: [PATCH 06/26] packaging: enabled D-Bus based activation. Change-Id: I9f15283814311ab61ee9225fa2d622f9946a51d3 --- packaging/org.tizen.srs.service | 3 +++ packaging/speech-recognition.spec | 9 ++++++++- packaging/start-speech-service.sh | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 packaging/org.tizen.srs.service create mode 100755 packaging/start-speech-service.sh diff --git a/packaging/org.tizen.srs.service b/packaging/org.tizen.srs.service new file mode 100644 index 0000000..4324101 --- /dev/null +++ b/packaging/org.tizen.srs.service @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.tizen.srs +Exec=/usr/lib/srs/scripts/start-speech-service.sh diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index e40eb70..44f931a 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -101,7 +101,9 @@ rm -fr $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig \ $RPM_BUILD_ROOT/lib/systemd/user \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition \ - $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo + $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo \ + $RPM_BUILD_ROOT%{_libdir}/srs/scripts \ + $RPM_BUILD_ROOT%{_datadir}/dbus-1/services /usr/bin/install -m 644 packaging/speech-recognition.conf \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition @@ -112,6 +114,10 @@ mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig \ /usr/bin/install -m 644 \ -t $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo \ dictionaries/demo/demo.* +/usr/bin/install -m 755 packaging/start-speech-service.sh \ + $RPM_BUILD_ROOT%{_libdir}/srs/scripts +/usr/bin/install -m 755 packaging/org.tizen.srs.service \ + $RPM_BUILD_ROOT%{_datadir}/dbus-1/services %clean rm -rf $RPM_BUILD_ROOT @@ -133,6 +139,7 @@ ldconfig %{_sysconfdir}/sysconfig/speech-recognition %{_datadir}/speech-recognition/dictionaries /lib/systemd/user/speech-recognition.service +%{_datadir}/dbus-1/services/org.tizen.srs.service %files doc %defattr(-,root,root,-) diff --git a/packaging/start-speech-service.sh b/packaging/start-speech-service.sh new file mode 100755 index 0000000..f50d240 --- /dev/null +++ b/packaging/start-speech-service.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exec systemctl --user start speech-recognition From 1d564346e39ba41a02f566a828fe2daafe27aaf0 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Thu, 12 Sep 2013 14:03:41 +0300 Subject: [PATCH 07/26] packaging: bumped version and updated changelog. Change-Id: I8421c9beb98c618a5b0185d8bf3fbdec2ebdcf53 --- packaging/speech-recognition.changes | 3 +++ packaging/speech-recognition.spec | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index d0ece0c..e233304 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,3 +1,6 @@ +* Thu Sep 12 14:02:21 EEST 2013 Krisztian Litkey - 0.0.3 +- Enabled D-Bus-based activation. + * Wed Sep 11 22:41:20 EEST 2013 Krisztian Litkey - 0.0.2 - Added and enabled voice/TTS support. - Fix a shutdown code path corruption. diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 44f931a..2046b5c 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -6,7 +6,7 @@ Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.2 +Version: 0.0.3 Release: 0 License: BSD-3-Clause Group: Base/Utilities From cb1c78c43ab223b55061325a23e24d044a7f3ae6 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Wed, 8 Jan 2014 18:55:28 +0200 Subject: [PATCH 08/26] packaging: load native client API plugin. Change-Id: I4ae967181c177beceb5760f6b9c3464133ed4953 --- packaging/speech-recognition.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/speech-recognition.env b/packaging/speech-recognition.env index eab26d8..5f3561b 100644 --- a/packaging/speech-recognition.env +++ b/packaging/speech-recognition.env @@ -5,7 +5,7 @@ MIKE="-s sphinx.pulsesrc=alsa_input.usb-Logitech_Logitech_USB_Microphone-00-Micr CLIENTS="-s gmainloop=true -L wrt-media-client" # plugins to load -PLUGINS="-L simple-disambiguator -L sphinx-speech -L festival-loader -L festival-voice" +PLUGINS="-L simple-disambiguator -L sphinx-speech -L festival-loader -L festival-voice -L native-client" # basic configuration file CONFIG="-c /etc/speech-recognition/speech-recognition.conf -P /usr/lib/srs/plugins" From a9fbbdfbd165d7fe318b5df7ee85bc4a2d85152a Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Tue, 7 Jan 2014 21:15:44 +0200 Subject: [PATCH 09/26] packaging: added devel, and test subpackages, bumped version. Change-Id: I63762687325ab54919251b21b04b30d8b2675f8c --- packaging/speech-recognition.changes | 5 +++++ packaging/speech-recognition.spec | 32 +++++++++++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index e233304..c6849b0 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,3 +1,8 @@ +* Tue Jan 07 14:02:21 EEST 2014 Krisztian Litkey - 0.0.4 +- A few crash fixes. +- Added native client library. +- Added devel and tests subpackages. + * Thu Sep 12 14:02:21 EEST 2013 Krisztian Litkey - 0.0.3 - Enabled D-Bus-based activation. diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 2046b5c..df2cfcb 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -6,7 +6,7 @@ Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.3 +Version: 0.0.4 Release: 0 License: BSD-3-Clause Group: Base/Utilities @@ -44,12 +44,21 @@ Requires: festival %description SRS/Winthorpe speech recognition system service. -%package doc -Summary: Documentation -Group: Development/Tools +%package devel +Summary: The header files and libraries needed for SRS/Winthorpe clients +Group: Development/Libraries +Requires: %{name} = %{version} -%description doc -Documentation for the speech recognition service. +%description devel +This package contains header files and libraries necessary for development. + +%package tests +Summary: Various test binaries for SRS/Winthorpe +Group: Development/Debug +Requires: %{name} = %{version} + +%description tests +This package contains various test binaries for SRS/Winthorpe. %prep %setup -q -n %{name}-%{version} @@ -135,12 +144,19 @@ ldconfig %{_bindir}/srs-client %endif %{_libdir}/srs +%{_libdir}/libsrs*.so.* %{_sysconfdir}/speech-recognition/speech-recognition.conf %{_sysconfdir}/sysconfig/speech-recognition %{_datadir}/speech-recognition/dictionaries /lib/systemd/user/speech-recognition.service %{_datadir}/dbus-1/services/org.tizen.srs.service -%files doc +%files devel +%defattr(-,root,root,-) +%{_includedir}/srs +%{_libdir}/libsrs*.so +%{_libdir}/pkgconfig/srs*.pc + +%files tests %defattr(-,root,root,-) -%doc AUTHORS COPYING ChangeLog INSTALL NEWS README +%{_bindir}/srs-native-client From b75823915f24c13bfb3ed53afdce22dc86d92b77 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Fri, 10 Jan 2014 17:35:00 +0200 Subject: [PATCH 10/26] packaging: temporarily switched to explicit package dependencies. Change-Id: Idb76dbf64acf2f5d2f3caa1ed27ac7765da315ca --- packaging/speech-recognition.spec | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index df2cfcb..6d52465 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -14,9 +14,20 @@ URL: https://github.com/otcshare/speech-recognition Source0: %{name}-%{version}.tar.gz BuildRequires: pkgconfig(libpulse) -BuildRequires: pkgconfig(murphy-common) -BuildRequires: pkgconfig(murphy-pulse) -BuildRequires: pkgconfig(murphy-glib) + +# Termporarily had to replace these with explicit package-dependencies, +# because the murphy pkgconfig files lack the correct version (needs to +# be fixed) and now we need a new enough murphy with native-types support. +# Switch these back once this is fixed on the murphy side. + +# BuildRequires: pkgconfig(murphy-common) >= 0.0.42 +# BuildRequires: pkgconfig(murphy-pulse) >= 0.0.42 +# BuildRequires: pkgconfig(murphy-glib) >= 0.0.42 + +BuildRequires: murphy-devel >= 0.0.42 +BuildRequires: murphy-glib-devel >= 0.0.42 +BuildRequires: murphy-pulse-devel >= 0.0.42 + BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(json) %if %{?_with_sphinx:1}%{!?_with_sphinx:0} From 1034cc2d358c799365e048420fba62bebf6da62c Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Thu, 16 Jan 2014 17:27:48 +0200 Subject: [PATCH 11/26] packaging: enable socket-based activation for the native API. Change-Id: Ia383e79257acf5d3c84d49c3c9a7cb23e47f42e7 --- packaging/speech-recognition.changes | 5 ++++ packaging/speech-recognition.conf | 43 +++++++++++++++++++++++++- packaging/speech-recognition.service | 9 +++--- packaging/speech-recognition.socket | 6 ++++ packaging/speech-recognition.spec | 45 +++++++++++++++++++--------- 5 files changed, 88 insertions(+), 20 deletions(-) create mode 100644 packaging/speech-recognition.socket diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index c6849b0..64f2897 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,3 +1,8 @@ +* Wed Jan 15 17:35:21 EEST 2014 Krisztian Litkey - 0.0.5 +- Enable systemd socket-based activation for the native client API. +- TIVI-2079: try autospawning pulseaudio if it is not running. +- Load the wrt-media-client plugin (forgotten in 0.0.4). + * Tue Jan 07 14:02:21 EEST 2014 Krisztian Litkey - 0.0.4 - A few crash fixes. - Added native client library. diff --git a/packaging/speech-recognition.conf b/packaging/speech-recognition.conf index 19d702a..35d0199 100644 --- a/packaging/speech-recognition.conf +++ b/packaging/speech-recognition.conf @@ -1,3 +1,11 @@ +######################### +# plugins we always need (at least currently) +# +load simple-disambiguator + +######################### +# sphinx speech engine +# # default mike input sphinx.pulsesrc = alsa_input.usb-Logitech_Logitech_USB_Microphone-00-Microphone.analog-mono # language models and dictionaries @@ -8,5 +16,38 @@ sphinx.fsg = /usr/share/speech-recognition/dictionaries/demo/demo.fsg sphinx.decoder = general sphinx.general.lm = /usr/share/pocketsphinx/model/lm/en_US/wsj0vp.5000.DMP sphinx.general.dict = /usr/share/pocketsphinx/model/lm/en_US/cmu07a.dic -# search plugin command + +load sphinx-speech + +######################### +# festival voice engine +# +festival.voices = auto + +load festival-loader +load festival-voice + +######################### +# MPRIS2 and bluetooth clients (for iPhone control) +# + +#mpris2.player1 = Anssi_Kostiainen__Intel____s_iPhone_Music + +#load mpris2-client +#load bluetooth-client + +######################### +# dumb search plugin +# search.command = /usr/bin/MiniBrowser "http://google.com/search?q=__url__" + +load search-client + +######################### +# client API plugins +# +load native-client + +# wrt-media-client needs GMainLoop +gmainloop = true +load wrt-media-client diff --git a/packaging/speech-recognition.service b/packaging/speech-recognition.service index 5d0a79d..5ea483b 100644 --- a/packaging/speech-recognition.service +++ b/packaging/speech-recognition.service @@ -1,11 +1,10 @@ [Unit] -Description=Speech recognition service -After=sound.target +Description=Winthorpe Speech Recognition and Synthesis Services [Service] -EnvironmentFile=/etc/sysconfig/speech-recognition -ExecStart=/usr/sbin/srs-daemon -f $CONFIG $MIKE $PLUGINS $CLIENTS Type=simple +ExecStart=/usr/sbin/srs-daemon -P /usr/lib/srs/plugins -c /etc/speech-recognition/speech-recognition.conf -S native.socket -f -vvv +KillSignal=SIGTERM [Install] -WantedBy=tizen-middleware.target +WantedBy=multi-user.target diff --git a/packaging/speech-recognition.socket b/packaging/speech-recognition.socket new file mode 100644 index 0000000..8605fae --- /dev/null +++ b/packaging/speech-recognition.socket @@ -0,0 +1,6 @@ +[Socket] +ListenStream=127.0.0.1:4100 +Accept=false + +[Install] +WantedBy=sockets.target diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 6d52465..23dfdaf 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -3,10 +3,11 @@ %{!?_with_festival:%{!?_without_festival:%define _with_festival 1}} %{!?_with_wrt:%{!?_without_wrt:%define _with_wrt 1}} %{!?_with_dbus:%{!?_without_dbus:%define _without_dbus 1}} +%{!?_with_systemd:%{!?_without_systemd:%define _with_systemd 1}} Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.4 +Version: 0.0.5 Release: 0 License: BSD-3-Clause Group: Base/Utilities @@ -24,9 +25,9 @@ BuildRequires: pkgconfig(libpulse) # BuildRequires: pkgconfig(murphy-pulse) >= 0.0.42 # BuildRequires: pkgconfig(murphy-glib) >= 0.0.42 -BuildRequires: murphy-devel >= 0.0.42 -BuildRequires: murphy-glib-devel >= 0.0.42 -BuildRequires: murphy-pulse-devel >= 0.0.42 +BuildRequires: murphy-devel >= 0.0.43 +BuildRequires: murphy-glib-devel >= 0.0.43 +BuildRequires: murphy-pulse-devel >= 0.0.43 BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(json) @@ -36,22 +37,20 @@ BuildRequires: pkgconfig(sphinxbase) %endif %if %{?_with_festival:1}%{!?_with_festival:0} BuildRequires: festival-devel +Requires: festival %endif %if %{?_with_dbus:1}%{!?_with_dbus:0} BuildRequires: pkgconfig(dbus-1) %endif - Requires: pulseaudio %if %{?_with_sphinx:1}%{!?_with_sphinx:0} Requires: sphinxbase Requires: pocketsphinx %endif -%if %{?_with_festival:1}%{!?_with_festival:0} -BuildRequires: festival-devel -Requires: festival +%if %{?_with_systemd:1}%{!?_with_systemd:0} +BuildRequires: pkgconfig(libsystemd-daemon) %endif - %description SRS/Winthorpe speech recognition system service. @@ -107,6 +106,12 @@ CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-dbus" CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-dbus" %endif +%if %{?_with_systemd:1}%{!?_with_systemd:0} +CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-systemd" +%else +CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-systemd" +%endif + ./bootstrap && \ %configure $CONFIG_OPTIONS && \ @@ -118,19 +123,21 @@ rm -fr $RPM_BUILD_ROOT %make_install # Install dictionaries, configuration and service files. -mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig \ - $RPM_BUILD_ROOT/lib/systemd/user \ +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition \ + $RPM_BUILD_ROOT/lib/systemd/user \ $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo \ $RPM_BUILD_ROOT%{_libdir}/srs/scripts \ $RPM_BUILD_ROOT%{_datadir}/dbus-1/services /usr/bin/install -m 644 packaging/speech-recognition.conf \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition -/usr/bin/install -m 644 packaging/speech-recognition.env \ - $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/speech-recognition /usr/bin/install -m 644 packaging/speech-recognition.service \ $RPM_BUILD_ROOT/lib/systemd/user +%if %{?_with_systemd:1}%{!?_with_systemd:0} +/usr/bin/install -m 644 packaging/speech-recognition.socket \ + $RPM_BUILD_ROOT/lib/systemd/user +%endif /usr/bin/install -m 644 \ -t $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo \ dictionaries/demo/demo.* @@ -144,6 +151,14 @@ rm -rf $RPM_BUILD_ROOT %post ldconfig +%if %{?_with_systemd:1}%{!?_with_systemd:0} +systemctl --user enable speech-recognition.socket +%endif + +%preun +%if %{?_with_systemd:1}%{!?_with_systemd:0} +systemctl --user disable speech-recognition.socket +%endif %postun ldconfig @@ -157,9 +172,11 @@ ldconfig %{_libdir}/srs %{_libdir}/libsrs*.so.* %{_sysconfdir}/speech-recognition/speech-recognition.conf -%{_sysconfdir}/sysconfig/speech-recognition %{_datadir}/speech-recognition/dictionaries /lib/systemd/user/speech-recognition.service +%if %{?_with_systemd:1}%{!?_with_systemd:0} +/lib/systemd/user/speech-recognition.socket +%endif %{_datadir}/dbus-1/services/org.tizen.srs.service %files devel From 86cbe400fedd44b4d9853b185c00735c2e95f6c4 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Fri, 17 Jan 2014 17:53:57 +0200 Subject: [PATCH 12/26] packaging: fix (workaround) socket-based activation to run as app. This patch also takes out all the systemd-related conditionality from the spec file. It seems that conditional inclusion in the %files section stopped working at some point in time. The exact same conditionals work in all other sections but not in %files. So while one can still configure and build the package based on various options, the %files section can't be adjusted dynamically with a simple %if any more because it always evaluates to false. I guess the only option is to generate a file list and use that instead. Not very convenient... Change-Id: I6186209921501b235ad25b77e5db8edd81a3b2b6 --- packaging/speech-recognition.spec | 41 +++++++++++-------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 23dfdaf..3c18133 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -3,7 +3,6 @@ %{!?_with_festival:%{!?_without_festival:%define _with_festival 1}} %{!?_with_wrt:%{!?_without_wrt:%define _with_wrt 1}} %{!?_with_dbus:%{!?_without_dbus:%define _without_dbus 1}} -%{!?_with_systemd:%{!?_without_systemd:%define _with_systemd 1}} Summary: Speech recognition service for Tizen Name: speech-recognition @@ -47,9 +46,7 @@ Requires: pulseaudio Requires: sphinxbase Requires: pocketsphinx %endif -%if %{?_with_systemd:1}%{!?_with_systemd:0} BuildRequires: pkgconfig(libsystemd-daemon) -%endif %description SRS/Winthorpe speech recognition system service. @@ -106,12 +103,7 @@ CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-dbus" CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-dbus" %endif -%if %{?_with_systemd:1}%{!?_with_systemd:0} CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-systemd" -%else -CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-systemd" -%endif - ./bootstrap && \ %configure $CONFIG_OPTIONS && \ @@ -125,7 +117,7 @@ rm -fr $RPM_BUILD_ROOT # Install dictionaries, configuration and service files. mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition \ - $RPM_BUILD_ROOT/lib/systemd/user \ + $RPM_BUILD_ROOT%{_unitdir_user} \ $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo \ $RPM_BUILD_ROOT%{_libdir}/srs/scripts \ $RPM_BUILD_ROOT%{_datadir}/dbus-1/services @@ -133,11 +125,9 @@ mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ /usr/bin/install -m 644 packaging/speech-recognition.conf \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition /usr/bin/install -m 644 packaging/speech-recognition.service \ - $RPM_BUILD_ROOT/lib/systemd/user -%if %{?_with_systemd:1}%{!?_with_systemd:0} + $RPM_BUILD_ROOT%{_unitdir_user} /usr/bin/install -m 644 packaging/speech-recognition.socket \ - $RPM_BUILD_ROOT/lib/systemd/user -%endif + $RPM_BUILD_ROOT%{_unitdir_user} /usr/bin/install -m 644 \ -t $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo \ dictionaries/demo/demo.* @@ -146,37 +136,33 @@ mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ /usr/bin/install -m 755 packaging/org.tizen.srs.service \ $RPM_BUILD_ROOT%{_datadir}/dbus-1/services +%install_service ../user/weston.target.wants speech-recognition.socket + %clean rm -rf $RPM_BUILD_ROOT %post ldconfig -%if %{?_with_systemd:1}%{!?_with_systemd:0} -systemctl --user enable speech-recognition.socket -%endif +%systemd_post speech-recognition.service %preun -%if %{?_with_systemd:1}%{!?_with_systemd:0} -systemctl --user disable speech-recognition.socket -%endif +%systemd_preun speech-recognition.service %postun ldconfig +%systemd_postun speech-recognition.service %files %defattr(-,root,root,-) %{_sbindir}/srs-daemon -%if %{?_with_dbus:1}%{!?_with_dbus:0} -%{_bindir}/srs-client -%endif %{_libdir}/srs %{_libdir}/libsrs*.so.* %{_sysconfdir}/speech-recognition/speech-recognition.conf %{_datadir}/speech-recognition/dictionaries -/lib/systemd/user/speech-recognition.service -%if %{?_with_systemd:1}%{!?_with_systemd:0} -/lib/systemd/user/speech-recognition.socket -%endif +%{_unitdir_user}/speech-recognition.service +%{_unitdir_user}/speech-recognition.socket +%{_unitdir_user}/weston.target.wants/speech-recognition.socket + %{_datadir}/dbus-1/services/org.tizen.srs.service %files devel @@ -188,3 +174,6 @@ ldconfig %files tests %defattr(-,root,root,-) %{_bindir}/srs-native-client +%if %{?_with_dbus:1}%{!?_with_dbus:0} +%{_bindir}/srs-client +%endif From 6fc7f8f856755c1f00ca446741fe8e1437ac35bf Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Tue, 11 Mar 2014 14:12:29 +0200 Subject: [PATCH 13/26] packaging: pulled in latest fixes, bumped version, updated changelog. Change-Id: If01c1f37ef3c9c5eee5a3596a9c7fb56afc5cecf --- packaging/speech-recognition.changes | 6 ++++++ packaging/speech-recognition.spec | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index 64f2897..efe1c3e 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,3 +1,9 @@ +* Tue Mar 11 14:10:15 EEST 2014 Krisztian Litkey - 0.0.6 +- tts: added API support for TTS rate and pitch. +- voice: properly administed cancellation of the active TTS request. +- native-client: return proper request id from srs_render_voice. +- test-client: minor TTS cancellation and help message fixes. + * Wed Jan 15 17:35:21 EEST 2014 Krisztian Litkey - 0.0.5 - Enable systemd socket-based activation for the native client API. - TIVI-2079: try autospawning pulseaudio if it is not running. diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 3c18133..ae29aa1 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -6,7 +6,7 @@ Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.5 +Version: 0.0.6 Release: 0 License: BSD-3-Clause Group: Base/Utilities From 6b099ced54c225ade8acc0699e4a55d3262c4391 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Fri, 23 May 2014 12:57:51 +0300 Subject: [PATCH 14/26] packaging: pulled in latest fixes, bumped version, updated changelog. Change-Id: Iba11d7045bd87b82a474d26b2e50fa0c6a82ca19 --- packaging/speech-recognition.changes | 4 ++++ packaging/speech-recognition.spec | 17 ++++------------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index efe1c3e..3059062 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,3 +1,7 @@ +* Fri May 23 14:10:15 EEST 2014 Krisztian Litkey - 0.0.7 +- client: (re-)request last focus upon resource connection establishment. +- festival: don't leak rendered sample buffers. + * Tue Mar 11 14:10:15 EEST 2014 Krisztian Litkey - 0.0.6 - tts: added API support for TTS rate and pitch. - voice: properly administed cancellation of the active TTS request. diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index ae29aa1..b171861 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -6,7 +6,7 @@ Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.6 +Version: 0.0.7 Release: 0 License: BSD-3-Clause Group: Base/Utilities @@ -15,18 +15,9 @@ Source0: %{name}-%{version}.tar.gz BuildRequires: pkgconfig(libpulse) -# Termporarily had to replace these with explicit package-dependencies, -# because the murphy pkgconfig files lack the correct version (needs to -# be fixed) and now we need a new enough murphy with native-types support. -# Switch these back once this is fixed on the murphy side. - -# BuildRequires: pkgconfig(murphy-common) >= 0.0.42 -# BuildRequires: pkgconfig(murphy-pulse) >= 0.0.42 -# BuildRequires: pkgconfig(murphy-glib) >= 0.0.42 - -BuildRequires: murphy-devel >= 0.0.43 -BuildRequires: murphy-glib-devel >= 0.0.43 -BuildRequires: murphy-pulse-devel >= 0.0.43 +BuildRequires: pkgconfig(murphy-common) +BuildRequires: pkgconfig(murphy-pulse) +BuildRequires: pkgconfig(murphy-glib) BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(json) From 9d609247a0c066e4801596f7190a5cb043cb25b1 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Tue, 28 Oct 2014 16:11:54 +0200 Subject: [PATCH 15/26] packaging: enabled/added w3c-speech plugin, generate service file. Change-Id: I5c2d1756c46d61f790430303f47dd826db8828f8 --- packaging/speech-recognition.conf | 4 ++++ ...on.service => speech-recognition.service.in} | 2 +- packaging/speech-recognition.socket | 4 +++- packaging/speech-recognition.spec | 17 +++++++++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) rename packaging/{speech-recognition.service => speech-recognition.service.in} (50%) diff --git a/packaging/speech-recognition.conf b/packaging/speech-recognition.conf index 35d0199..b2777d1 100644 --- a/packaging/speech-recognition.conf +++ b/packaging/speech-recognition.conf @@ -48,6 +48,10 @@ load search-client # load native-client +w3c-speech.address = unxs:@winthorpe.w3c-speech +w3c-speech.grammars = /etc/speech-recognition/w3c-grammars +load w3c-speech + # wrt-media-client needs GMainLoop gmainloop = true load wrt-media-client diff --git a/packaging/speech-recognition.service b/packaging/speech-recognition.service.in similarity index 50% rename from packaging/speech-recognition.service rename to packaging/speech-recognition.service.in index 5ea483b..0d60773 100644 --- a/packaging/speech-recognition.service +++ b/packaging/speech-recognition.service.in @@ -3,7 +3,7 @@ Description=Winthorpe Speech Recognition and Synthesis Services [Service] Type=simple -ExecStart=/usr/sbin/srs-daemon -P /usr/lib/srs/plugins -c /etc/speech-recognition/speech-recognition.conf -S native.socket -f -vvv +ExecStart=/usr/sbin/srs-daemon -P @LIBDIR@/srs/plugins -c /etc/speech-recognition/speech-recognition.conf -S native.socket,w3c-speech.socket -f -vvv KillSignal=SIGTERM [Install] diff --git a/packaging/speech-recognition.socket b/packaging/speech-recognition.socket index 8605fae..4215b0d 100644 --- a/packaging/speech-recognition.socket +++ b/packaging/speech-recognition.socket @@ -1,5 +1,7 @@ [Socket] -ListenStream=127.0.0.1:4100 +ListenStream=@winthorpe.native +ListenStream=@winthorpe.w3c-speech +Accept=false Accept=false [Install] diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index b171861..87dfe2a 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -94,6 +94,7 @@ CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-dbus" CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-dbus" %endif +CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-w3c-speech" CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-systemd" ./bootstrap && \ @@ -108,6 +109,7 @@ rm -fr $RPM_BUILD_ROOT # Install dictionaries, configuration and service files. mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition \ + $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition/w3c-grammars \ $RPM_BUILD_ROOT%{_unitdir_user} \ $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/demo \ $RPM_BUILD_ROOT%{_libdir}/srs/scripts \ @@ -115,8 +117,19 @@ mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ /usr/bin/install -m 644 packaging/speech-recognition.conf \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition + +case %{_arch} in + *64) LIBDIR=/usr/lib64;; + *) LIBDIR=/usr/lib;; +esac + +cat packaging/speech-recognition.service.in | \ + sed "s#@LIBDIR@#$LIBDIR#g" \ + > packaging/speech-recognition.service + /usr/bin/install -m 644 packaging/speech-recognition.service \ $RPM_BUILD_ROOT%{_unitdir_user} + /usr/bin/install -m 644 packaging/speech-recognition.socket \ $RPM_BUILD_ROOT%{_unitdir_user} /usr/bin/install -m 644 \ @@ -124,6 +137,8 @@ mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ dictionaries/demo/demo.* /usr/bin/install -m 755 packaging/start-speech-service.sh \ $RPM_BUILD_ROOT%{_libdir}/srs/scripts + + /usr/bin/install -m 755 packaging/org.tizen.srs.service \ $RPM_BUILD_ROOT%{_datadir}/dbus-1/services @@ -149,6 +164,7 @@ ldconfig %{_libdir}/srs %{_libdir}/libsrs*.so.* %{_sysconfdir}/speech-recognition/speech-recognition.conf +%dir %{_sysconfdir}/speech-recognition/w3c-grammars %{_datadir}/speech-recognition/dictionaries %{_unitdir_user}/speech-recognition.service %{_unitdir_user}/speech-recognition.socket @@ -165,6 +181,7 @@ ldconfig %files tests %defattr(-,root,root,-) %{_bindir}/srs-native-client +%{_bindir}/srs-w3c-client %if %{?_with_dbus:1}%{!?_with_dbus:0} %{_bindir}/srs-client %endif From 34a533c1633b8bc4f7569c953c25e61590cdaa4c Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Tue, 28 Oct 2014 16:13:50 +0200 Subject: [PATCH 16/26] packaging: bumped version, updated changelog. Change-Id: I4446e2c151eb20a61a7bc3dc06ebbd36cc0194ca --- packaging/speech-recognition.changes | 4 ++++ packaging/speech-recognition.spec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index 3059062..0437607 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,3 +1,7 @@ +* Tue Oct 28 16:12:00 EEST 2014 Krisztian Litkey - 0.0.8 +- packaging: enabled W3C speech plugin. +- synced with latest upstream fixes. + * Fri May 23 14:10:15 EEST 2014 Krisztian Litkey - 0.0.7 - client: (re-)request last focus upon resource connection establishment. - festival: don't leak rendered sample buffers. diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 87dfe2a..1cd3162 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -6,7 +6,7 @@ Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.7 +Version: 0.0.8 Release: 0 License: BSD-3-Clause Group: Base/Utilities From f1fcc4517f157eda8b60702ebe94c437455df50c Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Fri, 7 Nov 2014 15:22:42 +0200 Subject: [PATCH 17/26] packaging: generate config file, added W3C-speech dictionary dir. Change-Id: I4d4057474b56e16e02bb29e6077a07fb6d5b2f48 --- ...recognition.conf => speech-recognition.conf.in} | 2 +- packaging/speech-recognition.spec | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) rename packaging/{speech-recognition.conf => speech-recognition.conf.in} (95%) diff --git a/packaging/speech-recognition.conf b/packaging/speech-recognition.conf.in similarity index 95% rename from packaging/speech-recognition.conf rename to packaging/speech-recognition.conf.in index b2777d1..82e6b51 100644 --- a/packaging/speech-recognition.conf +++ b/packaging/speech-recognition.conf.in @@ -49,7 +49,7 @@ load search-client load native-client w3c-speech.address = unxs:@winthorpe.w3c-speech -w3c-speech.grammars = /etc/speech-recognition/w3c-grammars +w3c-speech.grammars = @DATADIR@/speech-recognition/w3c-grammars load w3c-speech # wrt-media-client needs GMainLoop diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 1cd3162..5c5b8bd 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -118,13 +118,11 @@ mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ /usr/bin/install -m 644 packaging/speech-recognition.conf \ $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition -case %{_arch} in - *64) LIBDIR=/usr/lib64;; - *) LIBDIR=/usr/lib;; -esac - +cat packaging/speech-recognition.conf.in | \ + sed "s#@DATADIR@#%{_datadir}#g" \ + > packaging/speech-recognition.conf cat packaging/speech-recognition.service.in | \ - sed "s#@LIBDIR@#$LIBDIR#g" \ + sed "s#@LIBDIR@#%{_libdir}#g" \ > packaging/speech-recognition.service /usr/bin/install -m 644 packaging/speech-recognition.service \ @@ -138,10 +136,11 @@ cat packaging/speech-recognition.service.in | \ /usr/bin/install -m 755 packaging/start-speech-service.sh \ $RPM_BUILD_ROOT%{_libdir}/srs/scripts - /usr/bin/install -m 755 packaging/org.tizen.srs.service \ $RPM_BUILD_ROOT%{_datadir}/dbus-1/services +mkdir -p $RPM_BUILD_ROOT%{_datadir}/speech-recognition/dictionaries/w3c-speech + %install_service ../user/weston.target.wants speech-recognition.socket %clean @@ -166,6 +165,7 @@ ldconfig %{_sysconfdir}/speech-recognition/speech-recognition.conf %dir %{_sysconfdir}/speech-recognition/w3c-grammars %{_datadir}/speech-recognition/dictionaries +%dir %{_datadir}/speech-recognition/w3c-speech %{_unitdir_user}/speech-recognition.service %{_unitdir_user}/speech-recognition.socket %{_unitdir_user}/weston.target.wants/speech-recognition.socket From 97de11d8cb65619ee2475ca48bc16060e0dc728a Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Fri, 7 Nov 2014 15:26:28 +0200 Subject: [PATCH 18/26] packaging: bumped version, updated changelog. Change-Id: If071d4508e5c6b7b0b162e04164a2f42d87521b4 --- packaging/speech-recognition.changes | 4 ++++ packaging/speech-recognition.spec | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index 0437607..99f3a61 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,3 +1,7 @@ +* Fri Nov 07 15:24:12 EEST 2014 Krisztian Litkey - 0.0.9 +- packaging: generate config file (to get %{_datadir} right). +- synced with latest upstream, including static analysis fixes. + * Tue Oct 28 16:12:00 EEST 2014 Krisztian Litkey - 0.0.8 - packaging: enabled W3C speech plugin. - synced with latest upstream fixes. diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 5c5b8bd..fc10532 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -6,7 +6,7 @@ Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.8 +Version: 0.0.9 Release: 0 License: BSD-3-Clause Group: Base/Utilities @@ -115,9 +115,6 @@ mkdir -p $RPM_BUILD_ROOT%{_sysconfdir} \ $RPM_BUILD_ROOT%{_libdir}/srs/scripts \ $RPM_BUILD_ROOT%{_datadir}/dbus-1/services -/usr/bin/install -m 644 packaging/speech-recognition.conf \ - $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition - cat packaging/speech-recognition.conf.in | \ sed "s#@DATADIR@#%{_datadir}#g" \ > packaging/speech-recognition.conf @@ -125,6 +122,9 @@ cat packaging/speech-recognition.service.in | \ sed "s#@LIBDIR@#%{_libdir}#g" \ > packaging/speech-recognition.service +/usr/bin/install -m 644 packaging/speech-recognition.conf \ + $RPM_BUILD_ROOT%{_sysconfdir}/speech-recognition + /usr/bin/install -m 644 packaging/speech-recognition.service \ $RPM_BUILD_ROOT%{_unitdir_user} @@ -165,7 +165,7 @@ ldconfig %{_sysconfdir}/speech-recognition/speech-recognition.conf %dir %{_sysconfdir}/speech-recognition/w3c-grammars %{_datadir}/speech-recognition/dictionaries -%dir %{_datadir}/speech-recognition/w3c-speech +%dir %{_datadir}/speech-recognition/dictionaries/w3c-speech %{_unitdir_user}/speech-recognition.service %{_unitdir_user}/speech-recognition.socket %{_unitdir_user}/weston.target.wants/speech-recognition.socket From 0579f4223f82059d02da6c3bece591e26145cba2 Mon Sep 17 00:00:00 2001 From: Amarnath Valluri Date: Mon, 17 Nov 2014 12:45:52 +0200 Subject: [PATCH 19/26] initial implementation crosswalk w3c speech extension Change-Id: I9ab54fa26cecf2e14df6f7f0dfbcea17270a3ebb --- configure.ac | 17 + packaging/speech-recognition.spec | 3 + src/Makefile.am | 43 + .../crosswalk-extension/common/XW_Extension.h | 185 +++ .../common/XW_Extension_EntryPoints.h | 48 + .../common/XW_Extension_Permissions.h | 41 + .../common/XW_Extension_Runtime.h | 43 + .../common/XW_Extension_SyncMessage.h | 48 + .../crosswalk-extension/common/extension.cc | 212 ++++ .../crosswalk-extension/common/extension.h | 96 ++ .../crosswalk-extension/common/picojson.h | 1037 +++++++++++++++++ .../crosswalk-extension/common/utils.h | 22 + .../crosswalk-extension/speech_api.js | 570 +++++++++ .../crosswalk-extension/speech_extension.cc | 35 + .../crosswalk-extension/speech_extension.h | 20 + .../crosswalk-extension/speech_instance.cc | 277 +++++ .../crosswalk-extension/speech_instance.h | 46 + .../crosswalk-extension/speech_logs.h | 57 + .../crosswalk-extension/tools/generate_api.py | 19 + 19 files changed, 2819 insertions(+) create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_EntryPoints.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Permissions.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Runtime.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_SyncMessage.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/extension.cc create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/extension.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/picojson.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/common/utils.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.cc create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/speech_logs.h create mode 100755 src/plugins/client-api/w3c-speech/crosswalk-extension/tools/generate_api.py diff --git a/configure.ac b/configure.ac index bf58e35..5dc82a2 100644 --- a/configure.ac +++ b/configure.ac @@ -305,11 +305,25 @@ AC_ARG_ENABLE(w3c-speech, if test "$enable_w3cspeech" = "yes"; then AC_MSG_NOTICE([W3C Speech API plugin is enabled.]) AC_DEFINE([W3C_SPEECH_ENABLED], 1, [Enable W3C Speech API plugin ?]) + + # Check if W3C Speech API crosswalk extenion was enabled + AC_ARG_ENABLE(w3c-speech-xwalk-extension, + [ --enable-w3c-speech-xwalk-extension enable W3C Speech API xwalk extension], + [enable_w3cspeech_xwalk_extension=$enableval], + [enable_w3cspeech_xwalk_extension=$enable_w3cspeech]) + + if test "x$enable_w3cspeech_xwalk_extension" = "xyes"; then + AC_MSG_NOTICE([W3C Speech API crosswalk extensionis enabled.]) + AC_DEFINE([W3C_SPEECH_XWALK_EXTENSION_ENABLED], 1, [Enabe W3c Speec crosswalk extension]) + else + AC_MSG_NOTICE([W3C Speech API crosswalk extension disabled.]) + fi else AC_MSG_NOTICE([W3C Speech API plugin is disabled.]) fi AM_CONDITIONAL(W3C_SPEECH_ENABLED, [test "$enable_w3cspeech" = "yes"]) +AM_CONDITIONAL(W3C_SPEECH_XWALK_EXTENSION_ENABLED, [test "$enable_w3cspeech_xwalk_extension" = "yes"]) # Check if systemd socket-based activation was enabled. AC_ARG_ENABLE(systemd, @@ -358,6 +372,9 @@ echo "Sphinx support: $enable_sphinx" echo "Festival support: $enable_festival" echo "Espeak support: $enable_espeak" echo "W3C Speech API: $enable_w3cspeech" +if test "$enable_w3cspeech";then + echo "W3C Speech API Crosswalk extension: $enable_w3cspeech_xwalk_extension" +fi echo "systemd socket-based activation: $enable_systemd" echo "Bluetooth voice recording support: $enable_bluetooth" echo "Mediaplayer remote interface support: $enable_mpris" diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index fc10532..aa68cf5 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -38,6 +38,7 @@ Requires: sphinxbase Requires: pocketsphinx %endif BuildRequires: pkgconfig(libsystemd-daemon) +BuildRequires: pkgconfig(glib-2.0) %description SRS/Winthorpe speech recognition system service. @@ -162,6 +163,8 @@ ldconfig %{_sbindir}/srs-daemon %{_libdir}/srs %{_libdir}/libsrs*.so.* +# crosswalk speech extension. +%{_libdir}/tizen-extensions-crosswalk/* %{_sysconfdir}/speech-recognition/speech-recognition.conf %dir %{_sysconfdir}/speech-recognition/w3c-grammars %{_datadir}/speech-recognition/dictionaries diff --git a/src/Makefile.am b/src/Makefile.am index 6fc853a..fb8cd71 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -369,6 +369,49 @@ srs_w3c_client_CFLAGS = \ srs_w3c_client_LDADD = \ $(MURPHY_BREEDLINE_LIBS) \ $(MURPHY_COMMON_LIBS) + +# xwalk extension +if W3C_SPEECH_XWALK_EXTENSION_ENABLED + +xwalkextension_LTLIBRARIES = libtizen_w3c_speech.la +xwalkextensiondir=$(libdir)/tizen-extensions-crosswalk + +XWALK_EXTENSION_SRCDIR = plugins/client-api/w3c-speech/crosswalk-extension + +$(XWALK_EXTENSION_SRCDIR)/speech_api_gen.cc : $(XWALK_EXTENSION_SRCDIR)/speech_api.js + python $(XWALK_EXTENSION_SRCDIR)/tools/generate_api.py \ + $(XWALK_EXTENSION_SRCDIR)/speech_api.js \ + kSource_speech_api \ + $(XWALK_EXTENSION_SRCDIR)/speech_api_gen.cc + +libtizen_w3c_speech_la_SOURCES = \ + plugins/client-api/w3c-speech/crosswalk-extension/common/extension.cc \ + plugins/client-api/w3c-speech/crosswalk-extension/common/extension.h \ + plugins/client-api/w3c-speech/crosswalk-extension/common/picojson.h \ + plugins/client-api/w3c-speech/crosswalk-extension/common/utils.h \ + plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension.h \ + plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_EntryPoints.h \ + plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Permissions.h \ + plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Runtime.h \ + plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_SyncMessage.h \ + plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.cc \ + plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.h   \ + plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc \ + plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h \ + plugins/client-api/w3c-speech/crosswalk-extension/speech_logs.h \ + plugins/client-api/w3c-speech/crosswalk-extension/speech_api_gen.cc + +libtizen_w3c_speech_la_CXXFLAGS = \ + -Iplugins/client-api/w3c-speech/crosswalk-extension -std=c++11 \ + $(AM_CFLAGS) \ + $(AM_CXXFLAGS) \ + $(GLIB_CFLAGS) + +libtizen_w3c_speech_la_LIBADD = \ + $(GLIB_LIBS) + +endif # W3C_SPEECH_XWALK_EXTENSION_ENABLED + endif # simple-voice synthesizer plugin diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension.h new file mode 100644 index 0000000..174915a --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension.h @@ -0,0 +1,185 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_ +#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_ + +// Crosswalk Extensions are modules of code loaded by Crosswalk runtime that +// allow extending its capabilities. The extension is expected to define a +// XW_Initialize() function as declared below, get the interfaces it need to +// use and register to whatever callbacks it needs, then return XW_OK. +// +// The Extension is represented by the type XW_Extension. Each extension +// loaded may be used multiple times for different pages, so to each execution +// there will be an associated XW_Instance. A reasonable analogy is that the +// XW_Extension represent a "class", and have concrete instances running. +// +// An interface is a struct with a set of functions, provided by Crosswalk, +// that allow the extension code to interact with the web content. Certain +// functions in an interface are used to register callbacks, so that Crosswalk +// can call the extension at specific situations. +// +// Crosswalk won't call an extension's XW_Initialize() multiple times in the +// same process. + +#ifdef __cplusplus +extern "C" { +#endif + +#if __GNUC__ >= 4 +#define XW_EXPORT __attribute__ ((visibility("default"))) +#elif defined(_MSC_VER) +#define XW_EXPORT __declspec(dllexport) +#endif + +#include + + +// XW_Extension is used to identify your extension when calling functions from +// the API. You should always use the XW_Extension received at XW_Initialize(). +// +// XW_Instance is used to identify different web contents using your +// extension. Each time a new web content is created you can be notified +// registering the XW_CreatedInstanceCallback, that receives the new +// XW_Instance. When interacting with an Instance (for example to post a +// message), you should pass the corresponding XW_Instance. +// +// In both types the zero value is never used by Crosswalk, so can be used to +// initialize variables. +typedef int32_t XW_Extension; +typedef int32_t XW_Instance; + +enum { + XW_OK = 0, + XW_ERROR = -1 +}; + +// Returns a struct containing functions to be used by the extension. Those +// structs can be stored statically and used until the extension is unloaded. +// Extensions should use definitions like XW_CORE_INTERFACE, instead of using +// the versioned definition or the literal string. Returns NULL if the +// interface is not supported. +typedef const void* (*XW_GetInterface)(const char* interface_name); + + +typedef int32_t (*XW_Initialize_Func)(XW_Extension extension, + XW_GetInterface get_interface); + +// XW_Initialize is called after the extension code is loaded. The 'extension' +// value should be used in further calls that expect XW_Extension argument. +// +// The 'get_interface' function should be used to get access to functions that +// interact with the web content. It is only valid during the execution of the +// XW_Initialize() function. +// +// This function should return XW_OK when the extension was succesfully +// loaded, otherwise XW_ERROR. +XW_EXPORT int32_t XW_Initialize(XW_Extension extension, + XW_GetInterface get_interface); + + +// +// XW_CORE_INTERFACE: Basic functionality for Crosswalk Extensions. All +// extensions should use this interface to set at least their name. +// + +#define XW_CORE_INTERFACE_1 "XW_CoreInterface_1" +#define XW_CORE_INTERFACE XW_CORE_INTERFACE_1 + +typedef void (*XW_CreatedInstanceCallback)(XW_Instance instance); +typedef void (*XW_DestroyedInstanceCallback)(XW_Instance instance); +typedef void (*XW_ShutdownCallback)(XW_Extension extension); + +struct XW_CoreInterface_1 { + // Set the name of the extension. It is used as the namespace for the + // JavaScript code exposed by the extension. So extension named + // 'my_extension', will expose its JavaScript functionality inside + // the 'my_extension' namespace. + // + // This function should be called only during XW_Initialize(). + void (*SetExtensionName)(XW_Extension extension, const char* name); + + // Set the JavaScript code loaded in the web content when the extension is + // used. This can be used together with the messaging mechanism to implement + // a higher-level API that posts messages to extensions, see + // XW_MESSAGING_INTERFACE below. + // + // The code will be executed inside a JS function context with the following + // objects available: + // + // - exports: this object should be filled with properties and functions + // that will be exposed in the namespace associated with this + // extension. + // + // - extension.postMessage(): post a string message to the extension native + // code. See below for details. + // - extension.setMessageListener(): allow setting a callback that is called + // when the native code sends a message + // to JavaScript. Callback takes a string. + // + // This function should be called only during XW_Initialize(). + void (*SetJavaScriptAPI)(XW_Extension extension, const char* api); + + // Register callbacks that are called when an instance of this extension + // is created or destroyed. Everytime a new web content is loaded, it will + // get a new associated instance. + // + // This function should be called only during XW_Initialize(). + void (*RegisterInstanceCallbacks)(XW_Extension extension, + XW_CreatedInstanceCallback created, + XW_DestroyedInstanceCallback destroyed); + + // Register a callback to be executed when the extension will be unloaded. + // + // This function should be called only during XW_Initialize(). + void (*RegisterShutdownCallback)(XW_Extension extension, + XW_ShutdownCallback shutdown_callback); + + // These two functions are conveniences used to associated arbitrary data + // with a given XW_Instance. They can be used only with instances that were + // created but not yet completely destroyed. GetInstanceData() can be used + // during the destroyed instance callback. If not instance data was set, + // getting it returns NULL. + void (*SetInstanceData)(XW_Instance instance, void* data); + void* (*GetInstanceData)(XW_Instance instance); +}; + +typedef struct XW_CoreInterface_1 XW_CoreInterface; + + +// +// XW_MESSAGING_INTERFACE: Exchange asynchronous messages with JavaScript +// code provided by extension. +// + +#define XW_MESSAGING_INTERFACE_1 "XW_MessagingInterface_1" +#define XW_MESSAGING_INTERFACE XW_MESSAGING_INTERFACE_1 + +typedef void (*XW_HandleMessageCallback)(XW_Instance instance, + const char* message); + +struct XW_MessagingInterface_1 { + // Register a callback to be called when the JavaScript code associated + // with the extension posts a message. Note that the callback will be called + // with the XW_Instance that posted the message as well as the message + // contents. + void (*Register)(XW_Extension extension, + XW_HandleMessageCallback handle_message); + + // Post a message to the web content associated with the instance. To + // receive this message the extension's JavaScript code should set a + // listener using extension.setMessageListener() function. + // + // This function is thread-safe and can be called until the instance is + // destroyed. + void (*PostMessage)(XW_Instance instance, const char* message); +}; + +typedef struct XW_MessagingInterface_1 XW_MessagingInterface; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_EntryPoints.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_EntryPoints.h new file mode 100644 index 0000000..f48b34b --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_EntryPoints.h @@ -0,0 +1,48 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_ +#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_ + +// NOTE: This file and interfaces marked as internal are not considered stable +// and can be modified in incompatible ways between Crosswalk versions. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_ +#error "You should include XW_Extension.h before this file" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define XW_INTERNAL_ENTRY_POINTS_INTERFACE_1 \ + "XW_Internal_EntryPointsInterface_1" +#define XW_INTERNAL_ENTRY_POINTS_INTERFACE \ + XW_INTERNAL_ENTRY_POINTS_INTERFACE_1 + +// +// XW_INTERNAL_ENTRY_POINTS_INTERFACE: provides a way for extensions to add +// more information about its implementation. For now, allow extensions to +// specify more objects that the access should cause the extension to be +// loaded. +// + +struct XW_Internal_EntryPointsInterface_1 { + // Register extra entry points for this extension. An "extra" entry points + // are objects outside the implicit namespace for which the extension should + // be loaded when they are touched. + // + // This function should be called only during XW_Initialize(). + void (*SetExtraJSEntryPoints)(XW_Extension extension, + const char** entry_points); +}; + +typedef struct XW_Internal_EntryPointsInterface_1 + XW_Internal_EntryPointsInterface; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Permissions.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Permissions.h new file mode 100644 index 0000000..d25484e --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Permissions.h @@ -0,0 +1,41 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_ +#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_ + +// NOTE: This file and interfaces marked as internal are not considered stable +// and can be modified in incompatible ways between Crosswalk versions. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_ +#error "You should include XW_Extension.h before this file" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define XW_INTERNAL_PERMISSIONS_INTERFACE_1 \ + "XW_Internal_PermissionsInterface_1" +#define XW_INTERNAL_PERMISSIONS_INTERFACE \ + XW_INTERNAL_PERMISSIONS_INTERFACE_1 + +// +// XW_INTERNAL_PERMISSIONS_INTERFACE: provides a way for extensions +// check if they have the proper permissions for certain APIs. +// + +struct XW_Internal_PermissionsInterface_1 { + int (*CheckAPIAccessControl)(XW_Extension extension, const char* api_name); + int (*RegisterPermissions)(XW_Extension extension, const char* perm_table); +}; + +typedef struct XW_Internal_PermissionsInterface_1 + XW_Internal_PermissionsInterface; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Runtime.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Runtime.h new file mode 100644 index 0000000..44c15d9 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_Runtime.h @@ -0,0 +1,43 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_ +#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_ + +// NOTE: This file and interfaces marked as internal are not considered stable +// and can be modified in incompatible ways between Crosswalk versions. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_ +#error "You should include XW_Extension.h before this file" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define XW_INTERNAL_RUNTIME_INTERFACE_1 \ + "XW_Internal_RuntimeInterface_1" +#define XW_INTERNAL_RUNTIME_INTERFACE \ + XW_INTERNAL_RUNTIME_INTERFACE_1 + +// +// XW_INTERNAL_RUNTIME_INTERFACE: allow extensions to gather information +// from the runtime. +// + +struct XW_Internal_RuntimeInterface_1 { + void (*GetRuntimeVariableString)(XW_Extension extension, + const char* key, + char* value, + size_t value_len); +}; + +typedef struct XW_Internal_RuntimeInterface_1 + XW_Internal_RuntimeInterface; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_SyncMessage.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_SyncMessage.h new file mode 100644 index 0000000..4eddbf9 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/XW_Extension_SyncMessage.h @@ -0,0 +1,48 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_ +#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_ + +// NOTE: This file and interfaces marked as internal are not considered stable +// and can be modified in incompatible ways between Crosswalk versions. + +#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_ +#error "You should include XW_Extension.h before this file" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// +// XW_INTERNAL_SYNC_MESSAGING_INTERFACE: allow JavaScript code to send a +// synchronous message to extension code and block until response is +// available. The response is made available by calling the SetSyncReply +// function, that can be done from outside the context of the SyncMessage +// handler. +// + +#define XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1 \ + "XW_InternalSyncMessagingInterface_1" +#define XW_INTERNAL_SYNC_MESSAGING_INTERFACE \ + XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1 + +typedef void (*XW_HandleSyncMessageCallback)(XW_Instance instance, + const char* message); + +struct XW_Internal_SyncMessagingInterface_1 { + void (*Register)(XW_Extension extension, + XW_HandleSyncMessageCallback handle_sync_message); + void (*SetSyncReply)(XW_Instance instance, const char* reply); +}; + +typedef struct XW_Internal_SyncMessagingInterface_1 + XW_Internal_SyncMessagingInterface; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/extension.cc b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/extension.cc new file mode 100644 index 0000000..168483a --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/extension.cc @@ -0,0 +1,212 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "common/extension.h" + +#include +#include +#include + +namespace { + +common::Extension* g_extension = NULL; +XW_Extension g_xw_extension = 0; + +const XW_CoreInterface* g_core = NULL; +const XW_MessagingInterface* g_messaging = NULL; +const XW_Internal_SyncMessagingInterface* g_sync_messaging = NULL; +const XW_Internal_EntryPointsInterface* g_entry_points = NULL; +const XW_Internal_RuntimeInterface* g_runtime = NULL; +const XW_Internal_PermissionsInterface* g_permission = NULL; + +bool InitializeInterfaces(XW_GetInterface get_interface) { + g_core = reinterpret_cast( + get_interface(XW_CORE_INTERFACE)); + if (!g_core) { + std::cerr << "Can't initialize extension: error getting Core interface.\n"; + return false; + } + + g_messaging = reinterpret_cast( + get_interface(XW_MESSAGING_INTERFACE)); + if (!g_messaging) { + std::cerr << + "Can't initialize extension: error getting Messaging interface.\n"; + return false; + } + + g_sync_messaging = + reinterpret_cast( + get_interface(XW_INTERNAL_SYNC_MESSAGING_INTERFACE)); + if (!g_sync_messaging) { + std::cerr << + "Can't initialize extension: error getting SyncMessaging interface.\n"; + return false; + } + + g_entry_points = reinterpret_cast( + get_interface(XW_INTERNAL_ENTRY_POINTS_INTERFACE)); + if (!g_entry_points) { + std::cerr << "NOTE: Entry points interface not available in this version " + << "of Crosswalk, ignoring entry point data for extensions.\n"; + } + + g_runtime = reinterpret_cast( + get_interface(XW_INTERNAL_RUNTIME_INTERFACE)); + if (!g_runtime) { + std::cerr << "NOTE: runtime interface not available in this version " + << "of Crosswalk, ignoring runtime variables for extensions.\n"; + } + + g_permission = reinterpret_cast( + get_interface(XW_INTERNAL_PERMISSIONS_INTERFACE)); + if (!g_permission) { + std::cerr << "NOTE: permission interface not available in this version " + << "of Crosswalk, ignoring permission for extensions.\n"; + } + + return true; +} + +} // namespace + +int32_t XW_Initialize(XW_Extension extension, XW_GetInterface get_interface) { + assert(extension); + g_xw_extension = extension; + + if (!InitializeInterfaces(get_interface)) + return XW_ERROR; + + g_extension = CreateExtension(); + if (!g_extension) { + std::cerr << "Can't initialize extension: " + << "create extension returned NULL.\n"; + return XW_ERROR; + } + + using common::Extension; + g_core->RegisterShutdownCallback(g_xw_extension, Extension::OnShutdown); + g_core->RegisterInstanceCallbacks( + g_xw_extension, Extension::OnInstanceCreated, + Extension::OnInstanceDestroyed); + g_messaging->Register(g_xw_extension, Extension::HandleMessage); + g_sync_messaging->Register(g_xw_extension, Extension::HandleSyncMessage); + return XW_OK; +} + +namespace common { + +Extension::Extension() {} + +Extension::~Extension() {} + +void Extension::SetExtensionName(const char* name) { + g_core->SetExtensionName(g_xw_extension, name); +} + +void Extension::SetJavaScriptAPI(const char* api) { + g_core->SetJavaScriptAPI(g_xw_extension, api); +} + +void Extension::SetExtraJSEntryPoints(const char** entry_points) { + if (g_entry_points) + g_entry_points->SetExtraJSEntryPoints(g_xw_extension, entry_points); +} + +bool Extension::RegisterPermissions(const char* perm_table) { + if (g_permission) + return g_permission->RegisterPermissions(g_xw_extension, perm_table); + return false; +} + +bool Extension::CheckAPIAccessControl(const char* api_name) { + if (g_permission) + return g_permission->CheckAPIAccessControl(g_xw_extension, api_name); + return false; +} + +Instance* Extension::CreateInstance() { + return NULL; +} + +std::string Extension::GetRuntimeVariable(const char* var_name, unsigned len) { + if (!g_runtime) + return ""; + + std::vector res(len + 1, 0); + g_runtime->GetRuntimeVariableString(g_xw_extension, var_name, &res[0], len); + return std::string(res.begin(), res.end()); +} + +// static +void Extension::OnShutdown(XW_Extension) { + delete g_extension; + g_extension = NULL; +} + +// static +void Extension::OnInstanceCreated(XW_Instance xw_instance) { + assert(!g_core->GetInstanceData(xw_instance)); + Instance* instance = g_extension->CreateInstance(); + if (!instance) + return; + instance->xw_instance_ = xw_instance; + g_core->SetInstanceData(xw_instance, instance); + instance->Initialize(); +} + +// static +void Extension::OnInstanceDestroyed(XW_Instance xw_instance) { + Instance* instance = + reinterpret_cast(g_core->GetInstanceData(xw_instance)); + if (!instance) + return; + instance->xw_instance_ = 0; + delete instance; +} + +// static +void Extension::HandleMessage(XW_Instance xw_instance, const char* msg) { + Instance* instance = + reinterpret_cast(g_core->GetInstanceData(xw_instance)); + if (!instance) + return; + instance->HandleMessage(msg); +} + +// static +void Extension::HandleSyncMessage(XW_Instance xw_instance, const char* msg) { + Instance* instance = + reinterpret_cast(g_core->GetInstanceData(xw_instance)); + if (!instance) + return; + instance->HandleSyncMessage(msg); +} + +Instance::Instance() + : xw_instance_(0) {} + +Instance::~Instance() { + assert(xw_instance_ == 0); +} + +void Instance::PostMessage(const char* msg) { + if (!xw_instance_) { + std::cerr << "Ignoring PostMessage() in the constructor or after the " + << "instance was destroyed."; + return; + } + g_messaging->PostMessage(xw_instance_, msg); +} + +void Instance::SendSyncReply(const char* reply) { + if (!xw_instance_) { + std::cerr << "Ignoring SendSyncReply() in the constructor or after the " + << "instance was destroyed."; + return; + } + g_sync_messaging->SetSyncReply(xw_instance_, reply); +} + +} // namespace common diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/extension.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/extension.h new file mode 100644 index 0000000..feee062 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/extension.h @@ -0,0 +1,96 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMMON_EXTENSION_H_ +#define COMMON_EXTENSION_H_ + +// This is a C++ wrapper over Crosswalk Extension C API. It implements once the +// boilerplate for the common case of mapping XW_Extension and XW_Instance to +// objects of their own. The wrapper deals automatically with creating and +// destroying the objects. +// +// Extension object lives during the lifetime of the extension, and when the +// extension process is properly shutdown, it's destructor will be +// called. Instance objects (there can be many) live during the lifetime of a +// script context associated with a frame in the page. These objects serves as +// storage points for extension specific objects, use them for that. + +#include + +#include + +#include "common/XW_Extension.h" +#include "common/XW_Extension_EntryPoints.h" +#include "common/XW_Extension_Permissions.h" +#include "common/XW_Extension_Runtime.h" +#include "common/XW_Extension_SyncMessage.h" + +namespace common { + +class Instance; +class Extension; + +} // namespace common + + +// This function should be implemented by each extension and should return +// an appropriate Extension subclass. +common::Extension* CreateExtension(); + + +namespace common { + +class Extension { + public: + Extension(); + virtual ~Extension(); + + // These should be called in the subclass constructor. + void SetExtensionName(const char* name); + void SetJavaScriptAPI(const char* api); + void SetExtraJSEntryPoints(const char** entry_points); + bool RegisterPermissions(const char* perm_table); + + // This API should be called in the message handler of extension + bool CheckAPIAccessControl(const char* api_name); + + virtual Instance* CreateInstance(); + + static std::string GetRuntimeVariable(const char* var_name, unsigned len); + + private: + friend int32_t ::XW_Initialize(XW_Extension extension, + XW_GetInterface get_interface); + + // XW_Extension callbacks. + static void OnShutdown(XW_Extension xw_extension); + static void OnInstanceCreated(XW_Instance xw_instance); + static void OnInstanceDestroyed(XW_Instance xw_instance); + static void HandleMessage(XW_Instance xw_instance, const char* msg); + static void HandleSyncMessage(XW_Instance xw_instance, const char* msg); +}; + +class Instance { + public: + Instance(); + virtual ~Instance(); + + void PostMessage(const char* msg); + void SendSyncReply(const char* reply); + + virtual void Initialize() {} + virtual void HandleMessage(const char* msg) = 0; + virtual void HandleSyncMessage(const char*) {} + + XW_Instance xw_instance() const { return xw_instance_; } + + private: + friend class Extension; + + XW_Instance xw_instance_; +}; + +} // namespace common + +#endif // COMMON_EXTENSION_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/picojson.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/picojson.h new file mode 100644 index 0000000..c465a74 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/picojson.h @@ -0,0 +1,1037 @@ +/* + * Copyright 2009-2010 Cybozu Labs, Inc. + * Copyright 2011 Kazuho Oku + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CYBOZU LABS, INC. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL CYBOZU LABS, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Cybozu Labs, Inc. + * + */ +#ifndef picojson_h +#define picojson_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + #define SNPRINTF _snprintf_s + #pragma warning(push) + #pragma warning(disable : 4244) // conversion from int to char +#else + #define SNPRINTF snprintf +#endif + +namespace picojson { + + enum { + null_type, + boolean_type, + number_type, + string_type, + array_type, + object_type + }; + + struct null {}; + + class value { + public: + typedef std::vector array; + typedef std::map object; + union _storage { + bool boolean_; + double number_; + std::string* string_; + array* array_; + object* object_; + }; + protected: + int type_; + _storage u_; + public: + value(); + value(int type, bool); + explicit value(bool b); + explicit value(double n); + explicit value(const std::string& s); + explicit value(const array& a); + explicit value(const object& o); + explicit value(const char* s); + value(const char* s, size_t len); + ~value(); + value(const value& x); + value& operator=(const value& x); + void swap(value& x); + template bool is() const; + template const T& get() const; + template T& get(); + bool evaluate_as_boolean() const; + const value& get(size_t idx) const; + const value& get(const std::string& key) const; + bool contains(size_t idx) const; + bool contains(const std::string& key) const; + std::string to_str() const; + template void serialize(Iter os) const; + std::string serialize() const; + private: + template value(const T*); // intentionally defined to block implicit conversion of pointer to bool + }; + + typedef value::array array; + typedef value::object object; + + inline value::value() : type_(null_type) {} + + inline value::value(int type, bool) : type_(type) { + switch (type) { +#define INIT(p, v) case p##type: u_.p = v; break + INIT(boolean_, false); + INIT(number_, 0.0); + INIT(string_, new std::string()); + INIT(array_, new array()); + INIT(object_, new object()); +#undef INIT + default: break; + } + } + + inline value::value(bool b) : type_(boolean_type) { + u_.boolean_ = b; + } + + inline value::value(double n) : type_(number_type) { + u_.number_ = n; + } + + inline value::value(const std::string& s) : type_(string_type) { + u_.string_ = new std::string(s); + } + + inline value::value(const array& a) : type_(array_type) { + u_.array_ = new array(a); + } + + inline value::value(const object& o) : type_(object_type) { + u_.object_ = new object(o); + } + + inline value::value(const char* s) : type_(string_type) { + u_.string_ = new std::string(s); + } + + inline value::value(const char* s, size_t len) : type_(string_type) { + u_.string_ = new std::string(s, len); + } + + inline value::~value() { + switch (type_) { +#define DEINIT(p) case p##type: delete u_.p; break + DEINIT(string_); + DEINIT(array_); + DEINIT(object_); +#undef DEINIT + default: break; + } + } + + inline value::value(const value& x) : type_(x.type_) { + switch (type_) { +#define INIT(p, v) case p##type: u_.p = v; break + INIT(string_, new std::string(*x.u_.string_)); + INIT(array_, new array(*x.u_.array_)); + INIT(object_, new object(*x.u_.object_)); +#undef INIT + default: + u_ = x.u_; + break; + } + } + + inline value& value::operator=(const value& x) { + if (this != &x) { + this->~value(); + new (this) value(x); + } + return *this; + } + + inline void value::swap(value& x) { + std::swap(type_, x.type_); + std::swap(u_, x.u_); + } + +#define IS(ctype, jtype) \ + template <> inline bool value::is() const { \ + return type_ == jtype##_type; \ + } + IS(null, null) + IS(bool, boolean) + IS(int, number) + IS(double, number) + IS(std::string, string) + IS(array, array) + IS(object, object) +#undef IS + +#define GET(ctype, var) \ + template <> inline const ctype& value::get() const { \ + assert("type mismatch! call vis() before get()" \ + && is()); \ + return var; \ + } \ + template <> inline ctype& value::get() { \ + assert("type mismatch! call is() before get()" \ + && is()); \ + return var; \ + } + GET(bool, u_.boolean_) + GET(double, u_.number_) + GET(std::string, *u_.string_) + GET(array, *u_.array_) + GET(object, *u_.object_) +#undef GET + + inline bool value::evaluate_as_boolean() const { + switch (type_) { + case null_type: + return false; + case boolean_type: + return u_.boolean_; + case number_type: + return u_.number_ != 0; + case string_type: + return ! u_.string_->empty(); + default: + return true; + } + } + + inline const value& value::get(size_t idx) const { + static value s_null; + assert(is()); + return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; + } + + inline const value& value::get(const std::string& key) const { + static value s_null; + assert(is()); + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end() ? i->second : s_null; + } + + inline bool value::contains(size_t idx) const { + assert(is()); + return idx < u_.array_->size(); + } + + inline bool value::contains(const std::string& key) const { + assert(is()); + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end(); + } + + inline std::string value::to_str() const { + switch (type_) { + case null_type: return "null"; + case boolean_type: return u_.boolean_ ? "true" : "false"; + case number_type: { + char buf[256]; + double tmp; + SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); + return buf; + } + case string_type: return *u_.string_; + case array_type: return "array"; + case object_type: return "object"; + default: assert(0); +#ifdef _MSC_VER + __assume(0); +#endif + } + return std::string(); + } + + template void copy(const std::string& s, Iter oi) { + std::copy(s.begin(), s.end(), oi); + } + + template void serialize_str(const std::string& s, Iter oi) { + *oi++ = '"'; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { + switch (*i) { +#define MAP(val, sym) case val: copy(sym, oi); break + MAP('"', "\\\""); + MAP('\\', "\\\\"); + MAP('/', "\\/"); + MAP('\b', "\\b"); + MAP('\f', "\\f"); + MAP('\n', "\\n"); + MAP('\r', "\\r"); + MAP('\t', "\\t"); +#undef MAP + default: + if ((unsigned char)*i < 0x20 || *i == 0x7f) { + char buf[7]; + SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff); + copy(buf, buf + 6, oi); + } else { + *oi++ = *i; + } + break; + } + } + *oi++ = '"'; + } + + template void value::serialize(Iter oi) const { + switch (type_) { + case string_type: + serialize_str(*u_.string_, oi); + break; + case array_type: { + *oi++ = '['; + for (array::const_iterator i = u_.array_->begin(); + i != u_.array_->end(); + ++i) { + if (i != u_.array_->begin()) { + *oi++ = ','; + } + i->serialize(oi); + } + *oi++ = ']'; + break; + } + case object_type: { + *oi++ = '{'; + for (object::const_iterator i = u_.object_->begin(); + i != u_.object_->end(); + ++i) { + if (i != u_.object_->begin()) { + *oi++ = ','; + } + serialize_str(i->first, oi); + *oi++ = ':'; + i->second.serialize(oi); + } + *oi++ = '}'; + break; + } + default: + copy(to_str(), oi); + break; + } + } + + inline std::string value::serialize() const { + std::string s; + serialize(std::back_inserter(s)); + return s; + } + + template class input { + protected: + Iter cur_, end_; + int last_ch_; + bool ungot_; + int line_; + public: + input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {} + int getc() { + if (ungot_) { + ungot_ = false; + return last_ch_; + } + if (cur_ == end_) { + last_ch_ = -1; + return -1; + } + if (last_ch_ == '\n') { + line_++; + } + last_ch_ = *cur_++ & 0xff; + return last_ch_; + } + void ungetc() { + if (last_ch_ != -1) { + assert(! ungot_); + ungot_ = true; + } + } + Iter cur() const { return cur_; } + int line() const { return line_; } + void skip_ws() { + while (1) { + int ch = getc(); + if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { + ungetc(); + break; + } + } + } + bool expect(int expect) { + skip_ws(); + if (getc() != expect) { + ungetc(); + return false; + } + return true; + } + bool match(const std::string& pattern) { + for (std::string::const_iterator pi(pattern.begin()); + pi != pattern.end(); + ++pi) { + if (getc() != *pi) { + ungetc(); + return false; + } + } + return true; + } + }; + + template inline int _parse_quadhex(input &in) { + int uni_ch = 0, hex; + for (int i = 0; i < 4; i++) { + if ((hex = in.getc()) == -1) { + return -1; + } + if ('0' <= hex && hex <= '9') { + hex -= '0'; + } else if ('A' <= hex && hex <= 'F') { + hex -= 'A' - 0xa; + } else if ('a' <= hex && hex <= 'f') { + hex -= 'a' - 0xa; + } else { + in.ungetc(); + return -1; + } + uni_ch = uni_ch * 16 + hex; + } + return uni_ch; + } + + template inline bool _parse_codepoint(String& out, input& in) { + int uni_ch; + if ((uni_ch = _parse_quadhex(in)) == -1) { + return false; + } + if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { + if (0xdc00 <= uni_ch) { + // a second 16-bit of a surrogate pair appeared + return false; + } + // first 16-bit of surrogate pair, get the next one + if (in.getc() != '\\' || in.getc() != 'u') { + in.ungetc(); + return false; + } + int second = _parse_quadhex(in); + if (! (0xdc00 <= second && second <= 0xdfff)) { + return false; + } + uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); + uni_ch += 0x10000; + } + if (uni_ch < 0x80) { + out.push_back(uni_ch); + } else { + if (uni_ch < 0x800) { + out.push_back(0xc0 | (uni_ch >> 6)); + } else { + if (uni_ch < 0x10000) { + out.push_back(0xe0 | (uni_ch >> 12)); + } else { + out.push_back(0xf0 | (uni_ch >> 18)); + out.push_back(0x80 | ((uni_ch >> 12) & 0x3f)); + } + out.push_back(0x80 | ((uni_ch >> 6) & 0x3f)); + } + out.push_back(0x80 | (uni_ch & 0x3f)); + } + return true; + } + + template inline bool _parse_string(String& out, input& in) { + while (1) { + int ch = in.getc(); + if (ch < ' ') { + in.ungetc(); + return false; + } else if (ch == '"') { + return true; + } else if (ch == '\\') { + if ((ch = in.getc()) == -1) { + return false; + } + switch (ch) { +#define MAP(sym, val) case sym: out.push_back(val); break + MAP('"', '\"'); + MAP('\\', '\\'); + MAP('/', '/'); + MAP('b', '\b'); + MAP('f', '\f'); + MAP('n', '\n'); + MAP('r', '\r'); + MAP('t', '\t'); +#undef MAP + case 'u': + if (! _parse_codepoint(out, in)) { + return false; + } + break; + default: + return false; + } + } else { + out.push_back(ch); + } + } + return false; + } + + template inline bool _parse_array(Context& ctx, input& in) { + if (! ctx.parse_array_start()) { + return false; + } + if (in.expect(']')) { + return true; + } + size_t idx = 0; + do { + if (! ctx.parse_array_item(in, idx)) { + return false; + } + idx++; + } while (in.expect(',')); + return in.expect(']'); + } + + template inline bool _parse_object(Context& ctx, input& in) { + if (! ctx.parse_object_start()) { + return false; + } + if (in.expect('}')) { + return true; + } + do { + std::string key; + if (! in.expect('"') + || ! _parse_string(key, in) + || ! in.expect(':')) { + return false; + } + if (! ctx.parse_object_item(in, key)) { + return false; + } + } while (in.expect(',')); + return in.expect('}'); + } + + template inline bool _parse_number(double& out, input& in) { + std::string num_str; + while (1) { + int ch = in.getc(); + if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == '.' + || ch == 'e' || ch == 'E') { + num_str.push_back(ch); + } else { + in.ungetc(); + break; + } + } + char* endp; + out = strtod(num_str.c_str(), &endp); + return endp == num_str.c_str() + num_str.size(); + } + + template inline bool _parse(Context& ctx, input& in) { + in.skip_ws(); + int ch = in.getc(); + switch (ch) { +#define IS(ch, text, op) case ch: \ + if (in.match(text) && op) { \ + return true; \ + } else { \ + return false; \ + } + IS('n', "ull", ctx.set_null()); + IS('f', "alse", ctx.set_bool(false)); + IS('t', "rue", ctx.set_bool(true)); +#undef IS + case '"': + return ctx.parse_string(in); + case '[': + return _parse_array(ctx, in); + case '{': + return _parse_object(ctx, in); + default: + if (('0' <= ch && ch <= '9') || ch == '-') { + in.ungetc(); + double f; + if (_parse_number(f, in)) { + ctx.set_number(f); + return true; + } else { + return false; + } + } + break; + } + in.ungetc(); + return false; + } + + class deny_parse_context { + public: + bool set_null() { return false; } + bool set_bool(bool) { return false; } + bool set_number(double) { return false; } + template bool parse_string(input&) { return false; } + bool parse_array_start() { return false; } + template bool parse_array_item(input&, size_t) { + return false; + } + bool parse_object_start() { return false; } + template bool parse_object_item(input&, const std::string&) { + return false; + } + }; + + class default_parse_context { + protected: + value* out_; + public: + default_parse_context(value* out) : out_(out) {} + bool set_null() { + *out_ = value(); + return true; + } + bool set_bool(bool b) { + *out_ = value(b); + return true; + } + bool set_number(double f) { + *out_ = value(f); + return true; + } + template bool parse_string(input& in) { + *out_ = value(string_type, false); + return _parse_string(out_->get(), in); + } + bool parse_array_start() { + *out_ = value(array_type, false); + return true; + } + template bool parse_array_item(input& in, size_t) { + array& a = out_->get(); + a.push_back(value()); + default_parse_context ctx(&a.back()); + return _parse(ctx, in); + } + bool parse_object_start() { + *out_ = value(object_type, false); + return true; + } + template bool parse_object_item(input& in, const std::string& key) { + object& o = out_->get(); + default_parse_context ctx(&o[key]); + return _parse(ctx, in); + } + private: + default_parse_context(const default_parse_context&); + default_parse_context& operator=(const default_parse_context&); + }; + + class null_parse_context { + public: + struct dummy_str { + void push_back(int) {} + }; + public: + null_parse_context() {} + bool set_null() { return true; } + bool set_bool(bool) { return true; } + bool set_number(double) { return true; } + template bool parse_string(input& in) { + dummy_str s; + return _parse_string(s, in); + } + bool parse_array_start() { return true; } + template bool parse_array_item(input& in, size_t) { + return _parse(*this, in); + } + bool parse_object_start() { return true; } + template bool parse_object_item(input& in, const std::string&) { + return _parse(*this, in); + } + private: + null_parse_context(const null_parse_context&); + null_parse_context& operator=(const null_parse_context&); + }; + + // obsolete, use the version below + template inline std::string parse(value& out, Iter& pos, const Iter& last) { + std::string err; + pos = parse(out, pos, last, &err); + return err; + } + + template inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) { + input in(first, last); + if (! _parse(ctx, in) && err != NULL) { + char buf[64]; + SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); + *err = buf; + while (1) { + int ch = in.getc(); + if (ch == -1 || ch == '\n') { + break; + } else if (ch >= ' ') { + err->push_back(ch); + } + } + } + return in.cur(); + } + + template inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) { + default_parse_context ctx(&out); + return _parse(ctx, first, last, err); + } + + inline std::string parse(value& out, std::istream& is) { + std::string err; + parse(out, std::istreambuf_iterator(is.rdbuf()), + std::istreambuf_iterator(), &err); + return err; + } + + template struct last_error_t { + static std::string s; + }; + template std::string last_error_t::s; + + inline void set_last_error(const std::string& s) { + last_error_t::s = s; + } + + inline const std::string& get_last_error() { + return last_error_t::s; + } + + inline bool operator==(const value& x, const value& y) { + if (x.is()) + return y.is(); +#define PICOJSON_CMP(type) \ + if (x.is()) \ + return y.is() && x.get() == y.get() + PICOJSON_CMP(bool); + PICOJSON_CMP(double); + PICOJSON_CMP(std::string); + PICOJSON_CMP(array); + PICOJSON_CMP(object); +#undef PICOJSON_CMP + assert(0); +#ifdef _MSC_VER + __assume(0); +#endif + return false; + } + + inline bool operator!=(const value& x, const value& y) { + return ! (x == y); + } +} + +namespace std { + template<> inline void swap(picojson::value& x, picojson::value& y) + { + x.swap(y); + } +} + +inline std::istream& operator>>(std::istream& is, picojson::value& x) +{ + picojson::set_last_error(std::string()); + std::string err = picojson::parse(x, is); + if (! err.empty()) { + picojson::set_last_error(err); + is.setstate(std::ios::failbit); + } + return is; +} + +inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) +{ + x.serialize(std::ostream_iterator(os)); + return os; +} +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif +#ifdef TEST_PICOJSON +#ifdef _MSC_VER + #pragma warning(disable : 4127) // conditional expression is constant +#endif + +using namespace std; + +static void plan(int num) +{ + printf("1..%d\n", num); +} + +static bool success = true; + +static void ok(bool b, const char* name = "") +{ + static int n = 1; + if (! b) + success = false; + printf("%s %d - %s\n", b ? "ok" : "ng", n++, name); +} + +template void is(const T& x, const T& y, const char* name = "") +{ + if (x == y) { + ok(true, name); + } else { + ok(false, name); + } +} + +#include +#include +#include +#include + +int main(void) +{ + plan(85); + + // constructors +#define TEST(expr, expected) \ + is(picojson::value expr .serialize(), string(expected), "picojson::value" #expr) + + TEST( (true), "true"); + TEST( (false), "false"); + TEST( (42.0), "42"); + TEST( (string("hello")), "\"hello\""); + TEST( ("hello"), "\"hello\""); + TEST( ("hello", 4), "\"hell\""); + + { + double a = 1; + for (int i = 0; i < 1024; i++) { + picojson::value vi(a); + std::stringstream ss; + ss << vi; + picojson::value vo; + ss >> vo; + double b = vo.get(); + if ((i < 53 && a != b) || fabs(a - b) / b > 1e-8) { + printf("ng i=%d a=%.18e b=%.18e\n", i, a, b); + } + a *= 2; + } + } + +#undef TEST + +#define TEST(in, type, cmp, serialize_test) { \ + picojson::value v; \ + const char* s = in; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + ok(err.empty(), in " no error"); \ + ok(v.is(), in " check type"); \ + is(v.get(), cmp, in " correct output"); \ + is(*s, '\0', in " read to eof"); \ + if (serialize_test) { \ + is(v.serialize(), string(in), in " serialize"); \ + } \ + } + TEST("false", bool, false, true); + TEST("true", bool, true, true); + TEST("90.5", double, 90.5, false); + TEST("1.7976931348623157e+308", double, DBL_MAX, false); + TEST("\"hello\"", string, string("hello"), true); + TEST("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"", string, string("\"\\/\b\f\n\r\t"), + true); + TEST("\"\\u0061\\u30af\\u30ea\\u30b9\"", string, + string("a\xe3\x82\xaf\xe3\x83\xaa\xe3\x82\xb9"), false); + TEST("\"\\ud840\\udc0b\"", string, string("\xf0\xa0\x80\x8b"), false); +#undef TEST + +#define TEST(type, expr) { \ + picojson::value v; \ + const char *s = expr; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + ok(err.empty(), "empty " #type " no error"); \ + ok(v.is(), "empty " #type " check type"); \ + ok(v.get().empty(), "check " #type " array size"); \ + } + TEST(array, "[]"); + TEST(object, "{}"); +#undef TEST + + { + picojson::value v; + const char *s = "[1,true,\"hello\"]"; + string err = picojson::parse(v, s, s + strlen(s)); + ok(err.empty(), "array no error"); + ok(v.is(), "array check type"); + is(v.get().size(), size_t(3), "check array size"); + ok(v.contains(0), "check contains array[0]"); + ok(v.get(0).is(), "check array[0] type"); + is(v.get(0).get(), 1.0, "check array[0] value"); + ok(v.contains(1), "check contains array[1]"); + ok(v.get(1).is(), "check array[1] type"); + ok(v.get(1).get(), "check array[1] value"); + ok(v.contains(2), "check contains array[2]"); + ok(v.get(2).is(), "check array[2] type"); + is(v.get(2).get(), string("hello"), "check array[2] value"); + ok(!v.contains(3), "check not contains array[3]"); + } + + { + picojson::value v; + const char *s = "{ \"a\": true }"; + string err = picojson::parse(v, s, s + strlen(s)); + ok(err.empty(), "object no error"); + ok(v.is(), "object check type"); + is(v.get().size(), size_t(1), "check object size"); + ok(v.contains("a"), "check contains property"); + ok(v.get("a").is(), "check bool property exists"); + is(v.get("a").get(), true, "check bool property value"); + is(v.serialize(), string("{\"a\":true}"), "serialize object"); + ok(!v.contains("z"), "check not contains property"); + } + +#define TEST(json, msg) do { \ + picojson::value v; \ + const char *s = json; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + is(err, string("syntax error at line " msg), msg); \ + } while (0) + TEST("falsoa", "1 near: oa"); + TEST("{]", "1 near: ]"); + TEST("\n\bbell", "2 near: bell"); + TEST("\"abc\nd\"", "1 near: "); +#undef TEST + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"b\": true, \"a\": [1,2,\"three\"] }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 == v2), "check == operator in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"a\": [1,\"three\"], \"b\": true }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 != v2), "check != operator for array in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"a\": [1,2,\"three\"], \"b\": false }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 != v2), "check != operator for object in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + picojson::object& o = v1.get(); + o.erase("b"); + picojson::array& a = o["a"].get(); + picojson::array::iterator i; + i = std::remove(a.begin(), a.end(), picojson::value(std::string("three"))); + a.erase(i, a.end()); + s = "{ \"a\": [1,2], \"d\": 2 }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 == v2), "check erase()"); + } + + ok(picojson::value(3.0).serialize() == "3", + "integral number should be serialized as a integer"); + + { + const char* s = "{ \"a\": [1,2], \"d\": 2 }"; + picojson::null_parse_context ctx; + string err; + picojson::_parse(ctx, s, s + strlen(s), &err); + ok(err.empty(), "null_parse_context"); + } + + { + picojson::value v1, v2; + v1 = picojson::value(true); + swap(v1, v2); + ok(v1.is(), "swap (null)"); + ok(v2.get() == true, "swap (bool)"); + + v1 = picojson::value("a"); + v2 = picojson::value(1.0); + swap(v1, v2); + ok(v1.get() == 1.0, "swap (dobule)"); + ok(v2.get() == "a", "swap (string)"); + + v1 = picojson::value(picojson::object()); + v2 = picojson::value(picojson::array()); + swap(v1, v2); + ok(v1.is(), "swap (array)"); + ok(v2.is(), "swap (object)"); + } + + return success ? 0 : 1; +} + +#endif diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/common/utils.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/utils.h new file mode 100644 index 0000000..ea866c4 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/common/utils.h @@ -0,0 +1,22 @@ +// Copyright (c) 2013 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMMON_UTILS_H_ +#define COMMON_UTILS_H_ + +// Put this in the private: declarations for a class to be uncopyable. +#define DISALLOW_COPY(TypeName) \ + TypeName(const TypeName&) + +// Put this in the private: declarations for a class to be unassignable. +#define DISALLOW_ASSIGN(TypeName) \ + void operator=(const TypeName&) + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#endif // COMMON_UTILS_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js new file mode 100644 index 0000000..7b6d845 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js @@ -0,0 +1,570 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +(function() { + var _listener = null; + var _callbacks = new Array(256); + var _dynamic_objects = []; + var _v8tools = requireNative('v8tools'); + + function DBG(msg) { + console.log('W3CSpeech-D:' + msg); + } + + function WARN(msg) { + console.log('W3CSpeech-W:' + msg); + } + + function ERR(msg) { + console.log('W3CSpeech-E:' + msg); + } + + function _getNextReqId() { + // find the next available request number + for (var i = 0, max = _callbacks.length; i < max; i++) { + if (_callbacks[i] == null || _callbacks[i] == undefined) + return i; + } + return i; + } + + function _sendSyncMessage(msg) { + return JSON.parse(extension.internal.sendSyncMessage(JSON.stringify(msg))); + }; + + function _postMessage(msg, handler) { + msg.reqno = _getNextReqId(); + _callbacks[msg.reqno] = handler; + + DBG('Posting message : ' + JSON.stringify(msg)); + + try { + extension.postMessage(JSON.stringify(msg)); + } catch (e) { + ERR('PostMessage Exception : ' + e); + } + }; + + function _addConstProperty(obj, prop, propValue) { + Object.defineProperty(obj, prop, { + value: propValue == undefined ? null : propValue, + writable: false, + configurable: true, + enumerable: true + }); + } + + function _addProperty(obj, prop, propValue) { + Object.defineProperty(obj, prop, { + value: propValue == undefined ? null : propValue, + writable: true, + configurable: true, + enumerable: true + }); + } + + extension.setMessageListener(function(json) { + var msg = JSON.parse(json); + var reqno = msg.reqno; + + if (msg.type == 'event') { + DBG('Got Event : ' + json); + var args = { }; + var obj = _dynamic_objects[msg.id]; + if (!obj) { + if (msg.event == 'pending') { + _v8tools.forceSetProperty(exports, 'pending', msg.state); + } else if (msg.event == 'speaking') { + _v8tools.forceSetProperty(exports, 'speaking', msg.state); + } else if (msg.event == 'paused') { + _v8tools.forceSetProperty(exports, 'paused', msg.state); + } else { + WARN("Unhandled event '" + msg.event + "'"); + } + return; + } else if (msg.event == 'audiostart') { + } else if (msg.event == 'audioend') { + } else if (msg.event == 'soundstrat') { + } else if (msg.event == 'soundend') { + } else if (msg.event == 'speechstart') { + } else if (msg.event == 'speechend') { + } else if (msg.event == 'result') { + // currently we support only signle shot result + args.resultIndex = 0; + args.results = msg.results; + args.interpretation = null; + args.emma = null; + } else if (msg.event == 'nomatch') { + args.resultIndex = 0; + args.results = null; + args.interpretation = null; + args.emma = null; + } else if (msg.event == 'error') { + args.error = msg.error; + args.message = msg.message; + } else if (msg.event == 'start') { + if (obj.type = 'recognizer') { + } else { + args.charIndex = msg.charIndex; + args.elapsedTime = msg.elapsedTime; + args.name = null; + } + } else if (msg.event == 'end') { + if (obj.type == 'recognizer') { + } else { + args.charIndex = msg.charIndex; + args.elapsedTime = msg.elapsedTime; + args.name = null; + } + } else if (msg.event == 'pause') { + args.charIndex = msg.charIndex; + args.elapsedTime = msg.elapsedTime; + args.name = null; + } else if (msg.event == 'resume') { + args.charIndex = msg.charIndex; + args.elapsedTime = msg.elapsedTime; + args.name = null; + } else if (msg.event == 'mark') { + args.charIndex = msg.charIndex; + args.elapsedTime = msg.elapsedTime; + args.name = args.name; + } else if (msg.event == 'boundary') { + args.charIndex = msg.charIndex; + args.elapsedTime = msg.elapsedTime; + args.name = args.name; + } + + _dispatchEvent(obj, msg.event, args); + } else if (reqno != undefined && reqno != null) { + DBG('Got reply for request no ' + reqno + ': ' + json); + var handler = _callbacks[reqno]; + delete msg.reqno; + if (handler != undefined && handler != null) { + handler(msg); + } + return; + } + }); + + function _GetActiveEventListenerList(et) { + var a = []; + + for (var type in et._event_listeners) { + if (et._event_listeners[type].length || et['on' + type]) { + a.push(type); + } + } + + DBG('Events:' + JSON.stringify(a)); + + return a; + } + + function EventTarget(event_types) { + var _event_listeners = { }; + var _event_handlers = { }; + var self = this; + + if (event_types != null) { + // initialize event listeners + event_types.forEach(function(type) { + _event_listeners[type] = []; + // Setup a Event Handler + _addProperty(self, 'on' + type, null); + }); + } + + this._event_listeners = _event_listeners; + } + + EventTarget.prototype.isValidEventType = function(type) { + return type in this._event_listeners; + }; + + EventTarget.prototype.addEventListener = function(type, listener) { + if (this.isValidEventType(type) && + listener != null && typeof listener === 'function' && + this._event_listeners[type].indexOf(listener) == -1) { + this._event_listeners[type].push(listener); + return; + } + ERR('Invalid event type \'' + type + '\', ignoring.' + + 'avaliable event types : ' + this._event_listeners); + }; + + EventTarget.prototype.removeEventListener = function(type, listener) { + if (!type || !listener) + return; + + var index = this._event_listeners[type].indexOf(listener); + if (index == -1) + return; + + this._event_listeners[type].splice(index, 1); + }; + + EventTarget.prototype.dispatchEvent = function(ev) { + var handled = true; + var listeners = null; + if (typeof ev === 'object' && + this.isValidEventType(ev.type) && + (listeners = this._event_listeners[ev.type]) != null) { + listeners.forEach(function(listener) { + if (!listener(ev) && handled) + handled = false; + }); + } + return handled; + }; + + function _dispatchEvent(obj, type, args) { + var ev_args = args || { }; + var ev = new CustomEvent(type); + for (var key in ev_args) + _addConstProperty(ev, key, ev_args[key]); + + obj.dispatchEvent(ev); + + // call EventHandler attached, if any + var handler = obj['on' + type]; + if (handler !== null && typeof handler === 'function') { + handler(ev); + } + } + + // + // SpeechRecognition : Speech -> Text + // + function SpeechRecognitionResult(is_final) { + this.push.apply(this, arguments); + + _addConstProperty(this, 'final', is_final || true); + + this.item = function(index) { + return this[index]; + }; + + this.toJSON = function() { + var a = []; + for (var i = 0; i < this.length; i++) { + a[i] = this[i]; + } + return a; + }; + + } + SpeechRecognitionResult.prototype = Object.create(Array.prototype); + SpeechRecognitionResult.prototype.construrctor = SpeechRecognitionResult; + + function SpeechGrammerList() { + this.push.apply(this, arguments); + + this.item = function(index) { + return this[index]; + }; + + this.toJSON = function() { + var a = []; + for (var i = 0; i < this.length; i++) { + a[i] = this[i]; + } + return a; + }; + this.addFromURI = function(src, weight) { + var w = weight || 1.0; + this.push({ src: src, weight: w }); + }; + + this.addFromString = function(src, weight) { + this.addFromURI(src, weight); + }; + } + SpeechGrammerList.prototype = Object.create(Array.prototype); + SpeechGrammerList.prototype.constructor = SpeechGrammerList; + + function SpeechRecognition() { + this._id = -1; + this._is_active = 0; + + EventTarget.call(this, [ + 'audiostart', + 'soundstart', + 'speechstart', + 'speechend', + 'soundend', + 'audioend', + 'result', + 'nomatch', + 'error', + 'start', + 'end']); + _addProperty(this, 'grammars', new SpeechGrammerList()); + _addProperty(this, 'lang'); + _addProperty(this, 'continuous', false); + _addProperty(this, 'interimResults', 1); + _addProperty(this, 'maxAlternatives', 1); + _addProperty(this, 'serviceURI'); + + var sr = this; + + this.start = function() { + if (!this.onresult && !this._event_listeners['result'].length) { + WARN("No handler 'result' handler set!!! no use of this recognizeri" + + 'unless handle the results'); + } + + DBG('About to start speech recognition with lang ' + this.lang + + ', grammars: ' + this.grammars); + + var reply = _sendSyncMessage({ + 'reqno': _getNextReqId(), + 'type': 'create', + 'object': 'recognizer', + 'set': { + 'grammars': this.grammars, + 'lang': this.lang, + 'continuous': this.continuous, + 'events': _GetActiveEventListenerList(this) + } + }); + + if (reply.error) { + WARN('Failed to create speech recognizer: ' + reply.message); + _dispatchEvent(sr, 'error', { + 'error': reply.error, + 'message': reply.message + }); + return; + } + this._id = reply.id; + _dynamic_objects[reply.id] = this; + + DBG('Rcognizer ID : ' + this._id); + _postMessage({ + 'type': 'invoke', + 'method': 'start', + 'id': this._id + }, function(reply) { + if (reply.error) { + sr._is_active = false; + WARN('Failed to start speech recognizer: ' + reply.message); + _dispatchEvent(sr, 'error', { + 'error': reply.error, + 'message': reply.message + }); + } else + DBG('Start method status: ' + reply.status); + }); + + this._is_active = true; + }; + + this.stop = function() { + if (!this._is_active) { + DBG('Recognition has not yet started.'); + return; + } + + _postMessage({ + 'type': 'invoke', + 'method': 'stop', + 'id': this._id + }, function(reply) { + if (reply.error) { + sr._is_active = true; + WARN('Failed to stop speech recognizer: ' + reply.message); + _dispatchEvent(sr, 'error', { + 'error': reply.error, + 'message': reply.message + }); + } + }); + + this._is_active = false; + }; + + this.abort = function() { + if (!this._is_active) + return; + + _postMessage({ + 'type': 'invoke', + 'method': 'abort', + 'id': this._id + }, function(reply) { + if (reply.error) { + WARN('Failed to abort: ' + reply.message); + } + }); + + this._is_active = false; + }; + } + SpeechRecognition.prototype = Object.create(EventTarget.prototype); + // + // SpeechSynthesis (Text -> Speech) + // + + function SpeechSynthesisUtterance(text) { + this._id = -1; + + EventTarget.call(this, [ + 'start', + 'end', + 'error', + 'pause', + 'resume', + 'mark', + 'boundary' + ]); + + _addProperty(this, 'text', text); + _addProperty(this, 'lang', 'english'); + _addProperty(this, 'voiceURI'); + _addProperty(this, 'volume', 1.0); + _addProperty(this, 'rate', 0, 0); + _addProperty(this, 'pitch', 1.0); + + var ut = this; + + this.addEventListener('end', function() { + _dynamic_objects[ut._id] = null; + }); + this.addEventListener('error', function() { + _dynamic_objects[ut._id] = null; + }); + + var reply = _sendSyncMessage({ + 'reqno': _getNextReqId(), + 'type': 'create', + 'object': 'utterance', + 'set': { + 'text': this.text, + 'pitch': this.pitch, + 'rate': this.rate, + 'volume': this.volume, + 'lang': this.lang, + 'events': _GetActiveEventListenerList(this) + } + }); + if (reply.error) { + WARN('Failed to create uttenrance object: ' + reply.message); + return; + } + + DBG("Utterance id : " + reply.id); + + this._id = reply.id; + _dynamic_objects[reply.id] = this; + } + SpeechSynthesisUtterance.prototype = Object.create(EventTarget.prototype); + SpeechSynthesisUtterance.prototype.constructor = SpeechSynthesisUtterance; + + function SpeechSynthesisVoice(info) { + _addConstProperty('voiceURI', info.uri); + _addConstProperty('name', info.name); + _addConstProperty('lang', info.lang); + _addConstProperty('localService', info.locaService); + _addConstProperty('default', info.default); + } + + function SpeechSynthesisVoiceList() { + this.push.apply(this, arguments); + + this.item = function(index) { + return this[index]; + }; + + this.toJSON = function() { + var a = []; + for (var i = 0; i < this.length; i++) { + a[i] = this[i]; + } + return a; + }; + } + SpeechSynthesisVoiceList.prototype = Object.create(Array.prototype); + SpeechSynthesisVoiceList.prototype.construrctor = SpeechSynthesisVoiceList; + + function SpeechSynthesis() { + _addConstProperty(this, 'pending', false); + _addConstProperty(this, 'speaking', false); + _addConstProperty(this, 'paused', false); + + var sy = this; + + this.speak = function(utterance) { + if (utterance._id == -1) { + WARN('Not a valid utternace'); + _dispatchEvent(utterance, 'error', { + 'error': 'network', + 'message': 'failed to connect to server' + }); + return; + } + + DBG("Sending speak request for text '" + utterance.text + "'"); + _postMessage({ + 'id': 0, + 'type': 'invoke', + 'method': 'speak', + 'utterance': utterance._id + }, function(reply) { + if (reply.error) { + WARN('Failed to synthesize, error: ' + reply.message); + _dispatchEvent(utterance, 'error', { + 'error': reply.error, + 'message': reply.message + }); + return; + } + }); + }; + + this.cancel = function() { + _postMessage({ + 'id': 0, + 'type': 'invoke', + 'method': 'cancel' + }, function(reply) { + }); + }; + + this.pause = function() { + _postMessage({ + 'id': 0, + 'type': 'invoke', + 'method': 'pause' + }, function(reply) { + }); + }; + + this.resume = function() { + _postMessage({ + 'id': 0, + 'type': 'invoke', + 'method': 'resume' + }, function(reply) { + }); + }; + + this.getVoices = function() { + var voices = new SpeechSynthesisVoiceList(); + var reply = _sendSyncMessage({ + 'id': 0, + 'type': 'invoke', + 'method': 'getVoices' + }); + + if (reply.status != 0) { + WARN('Failed to get voices, error: ' + reply.message); + } else { + } + + return voices; + }; + } + tizen.SpeechRecognition = SpeechRecognition; + tizen.SpeechSynthesisUtterance = SpeechSynthesisUtterance; + exports = new SpeechSynthesis(); +})(); diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.cc b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.cc new file mode 100644 index 0000000..0888bc2 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "speech_extension.h" + +#include "speech_logs.h" +#include "speech_instance.h" + +std::ofstream _s_f_log; + +common::Extension* CreateExtension() { + return new SpeechExtension(); +} + +// This will be generated from speech_api.js +extern const char kSource_speech_api[]; + +SpeechExtension::SpeechExtension() { + SetExtensionName("tizen.speechSynthesis"); + SetJavaScriptAPI(kSource_speech_api); + const char *entry_pointer[] = { + "tizen.SpeechRecognition", + "tizen.SpeechSynthesisUtterance", + NULL + }; + SetExtraJSEntryPoints(entry_pointer); +} + +SpeechExtension::~SpeechExtension() {} + +common::Instance* SpeechExtension::CreateInstance() { + LOG_INIT(); + return new SpeechInstance; +} diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.h new file mode 100644 index 0000000..bf157d8 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_extension.h @@ -0,0 +1,20 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SPEECH_SPEECH_EXTENSION_H_ +#define SPEECH_SPEECH_EXTENSION_H_ + +#include "common/extension.h" + +class SpeechExtension : public common::Extension { + public: + SpeechExtension(); + virtual ~SpeechExtension(); + + private: + // common::Extension implementation + virtual common::Instance* CreateInstance(); +}; + +#endif // SPEECH_SPEECH_EXTENSION_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc new file mode 100644 index 0000000..65424c4 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc @@ -0,0 +1,277 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "speech_instance.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "speech_logs.h" + +#define WINTHORP_SERVER_SOCKET "@winthorpe.w3c-speech" + +SpeechInstance::SpeechInstance() + : fd_(-1) + , channel_(NULL) + , watcher_id_(0) + , pending_request_timer_(0) + , pending_reply_timer_(0) { + if ((fd_ = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + ERR("Failed to create socket: %s", strerror(errno)); + fd_ = -1; + return; + } + + struct sockaddr_un server; + memset(&server, 0, sizeof(server)); + server.sun_family = AF_UNIX, + strncpy(server.sun_path, WINTHORP_SERVER_SOCKET, sizeof(server.sun_path) - 1); + + int len = SUN_LEN(&server); + DBG("Socket path : %s", server.sun_path + 1); + server.sun_path[0] = 0; + + if (connect(fd_, (struct sockaddr *)&server, len)) { + ERR("Failed to connect to server : %s", strerror(errno)); + close(fd_); + fd_ = -1; + return; + } + + channel_ = g_io_channel_unix_new(fd_); + GIOCondition flags = GIOCondition(G_IO_IN | G_IO_ERR | G_IO_HUP); + watcher_id_ = g_io_add_watch(channel_, flags, IOWatchCb, this); + + DBG("Connected to server"); +} + +SpeechInstance::~SpeechInstance() { + if (watcher_id_) + g_source_remove(watcher_id_); + if (pending_reply_timer_) + g_source_remove(pending_reply_timer_); + if (pending_request_timer_) + g_source_remove(pending_request_timer_); + if (fd_) + close(fd_); + if (channel_) + g_io_channel_unref(channel_); +} + +// static +gboolean SpeechInstance::IOWatchCb(GIOChannel *c, + GIOCondition cond, + gpointer userdata) { + SpeechInstance *self = reinterpret_cast(userdata); + + (void)c; + + DBG("IO Event on socket : %d", cond); + + switch (cond) { + case G_IO_HUP: + case G_IO_ERR: + // TODO(avalluri): raise error and close the connection + break; + case G_IO_IN: { + char *reply = NULL; + uint32_t size = 0; + + if ((size = self->ReadReply(&reply))) { + self->PostMessage(reply); + free(reply); + } + + break; + } + default: + break; + } + + return TRUE; +} + +bool SpeechInstance::SendRequest(const char *message) { + uint32_t size = ((uint32_t)strlen(message)); + uint32_t size_be = htobe32(size); + + if (fd_ == -1) { + ERR("Socket not connected!"); + return false; + } + + if (send(fd_, static_cast(&size_be), sizeof(size_be), 0) < 0) { + WARN("Failed to send message size: %s", strerror(errno)); + return false; + } + + void *buf = const_cast(static_cast(message)); + ssize_t len = 0; + while (size && (len = send(fd_, buf, size, 0)) < size) { + if (len < 0) { + WARN("Failed to send message to server: %s", strerror(errno)); + return false; + } + size -= len; + buf = static_cast(buf) + len; + } + + return true; +} + +uint32_t SpeechInstance::ReadReply(char **reply) { + uint32_t size_be = 0; + + if (recv(fd_, static_cast(&size_be), + sizeof(size_be), MSG_WAITALL) < 0) { + WARN("Failed read server reply: %s", strerror(errno)); + return 0; + } + + uint32_t size = be32toh(size_be); + DBG("Received message size : %u", size); + char *message = static_cast(malloc(size + 1)); + memset(message, 0, size); + + // FIXME: loop through till complete read + if (recv(fd_, message, size, MSG_WAITALL) < 0) { + WARN("Failed to read server message with size '%u': %s", + size, strerror(errno)); + free(message); + return 0; + } + message[size] = '\0'; + + DBG("Recived message : %s", message); + + if (reply) *reply = message; + + return size; +} + +// static +gboolean SpeechInstance::ProcessPendingRepliesCb(gpointer data) { + SpeechInstance *self = reinterpret_cast(data); + + if (!self) { + WARN("asset(self)"); + return FALSE; + } + + std::string reply = self->pending_replies_.front(); + self->PostMessage(reply.c_str()); + self->pending_replies_.pop(); + + if (self->pending_replies_.empty()) { + self->pending_reply_timer_ = 0; + return FALSE; + } + + return TRUE; +} + +// static +gboolean SpeechInstance::ProcessPendingRequestsCb(gpointer data) { + SpeechInstance *self = reinterpret_cast(data); + if (!self) { + WARN("assert(self)"); + return FALSE; + } + + std::string &request = self->pending_requests_.front(); + if (!self->SendRequest(request.data())) { + picojson::value in; + picojson::object out; + + picojson::parse(in, request.data(), request.data() + request.size(), NULL); + out["reqno"] = in.get("reqno"); + out["error"] = picojson::value("network"); + out["message"] = picojson::value("failed to connect to server"); + + self->QueueReply(picojson::value(out).serialize()); + } + self->pending_requests_.pop(); + + if (self->pending_requests_.empty()) { + self->pending_request_timer_ = 0; + return FALSE; + } + + return TRUE; +} + +void SpeechInstance::QueueReply(const std::string& reply) { + pending_replies_.push(reply); + if (!pending_reply_timer_) { + pending_reply_timer_ = g_idle_add(ProcessPendingRepliesCb, + static_cast(this)); + } +} + +void SpeechInstance::QueueRequest(const std::string& req) { + pending_requests_.push(req); + if (!pending_request_timer_) { + pending_request_timer_ = g_idle_add(ProcessPendingRequestsCb, + static_cast(this)); + } +} + +void SpeechInstance::HandleSyncMessage(const char* message) { + picojson::value v, out; + std::string err; + + DBG("Message: %s", message); + + if (!SendRequest(message)) { + picojson::object obj; + + obj["error"] = picojson::value("network"); + obj["message"] = picojson::value("server connection failure"); + out = picojson::value(obj); + } else { + picojson::parse(v, message, message + strlen(message), &err); + if (!err.empty()) + return; + + const std::string& req_id = v.get("reqno").to_str(); + + do { + char *reply = NULL; + uint32_t size; + + if ((size = ReadReply(&reply)) != 0) { + picojson::parse(out, reply, reply + size, &err); + free(reply); + if (!err.empty()) { + WARN("Failed to read server reply: %s", strerror(errno)); + // TODO(avalluri): fill error details in out + break; + } else if (out.get("reqno").to_str() == req_id) { + break; + } else { + QueueReply(out.serialize()); + } + } + } while (0); + } + + SendSyncReply(out.serialize().c_str()); +} + +void SpeechInstance::HandleMessage(const char* message) { + if (!message) + return; + + QueueRequest(message); +} diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h new file mode 100644 index 0000000..065be50 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h @@ -0,0 +1,46 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SPEECH_SPEECH_INSTANCE_H_ +#define SPEECH_SPEECH_INSTANCE_H_ + +#include +#include // NOLINT +#include +#include + +#include "common/extension.h" +#include "common/picojson.h" + +class SpeechInstance : public common::Instance { + public: + SpeechInstance(); + virtual ~SpeechInstance(); + + protected: + static gboolean IOWatchCb(GIOChannel* source, + GIOCondition condition, + gpointer data); + static gboolean ProcessPendingRequestsCb(gpointer data); + static gboolean ProcessPendingRepliesCb(gpointer data); + + private: + // common::Instance implementation. + virtual void HandleMessage(const char* msg); + virtual void HandleSyncMessage(const char* message); + + void QueueReply(const std::string& reply); + void QueueRequest(const std::string& req); + bool SendRequest(const char* message); + uint32_t ReadReply(char** reply); + + std::queue pending_replies_; + std::queue pending_requests_; + int fd_; + GIOChannel *channel_; + guint watcher_id_; + guint pending_request_timer_; + guint pending_reply_timer_; +}; +#endif // SPEECH_SPEECH_INSTANCE_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_logs.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_logs.h new file mode 100644 index 0000000..07269a6 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_logs.h @@ -0,0 +1,57 @@ +// Copyright (c) 2014 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SPEECH_SPEECH_LOGS_H_ +#define SPEECH_SPEECH_LOGS_H_ + +#ifndef NDEBUG + +#include +#include + +#define ENABLE_DEBUG 1 + +// file logging +extern std::ofstream _s_f_log; + +#define LOG_INIT() _s_f_log.open("xwalk_ext_speech.log") +#define LOG_CLOSE() _s_f_log.close() + +#define LOG(type, msg, args...) \ +{\ + char exp_msg[2048]; \ + time_t t; \ + struct tm lt; \ + char str_time[64]; \ + time(&t); \ + localtime_r(&t, <); \ + strftime(str_time, sizeof(str_time) - 1, "%b %d %H:%M:%S ", <); \ + snprintf(exp_msg, sizeof(exp_msg), msg, ##args); \ + _s_f_log << str_time << "[" << type << "] " \ + << __PRETTY_FUNCTION__ << ":" \ + << __LINE__ << ": " << exp_msg << "\n"; \ + _s_f_log.flush(); \ +} + +#define ERR(msg, args...) LOG("Error", msg, ##args) +#define WARN(msg, args...) LOG("Warning", msg, ##args) + +#ifdef ENABLE_DEBUG +# define DBG(msg, args...) LOG("Debug", msg, ##args) +#else +# define DBG do { } while (0) +#endif // ENABLE_DEBUG + +#else // NDEBUG + +#define ERR(msg, args...) do { } while (0) +#define WARN(msg, args...) do { } while (0) +#define DBG(msg, args...) do { } while (0) + +#define LOG_INIT() do { } while (0) +#define LOG_CLOSE() do { } while (0) + +#endif // NDEBUG + +#endif // SPEECH_SPEECH_LOGS_H_ diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/tools/generate_api.py b/src/plugins/client-api/w3c-speech/crosswalk-extension/tools/generate_api.py new file mode 100755 index 0000000..cef97b7 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/tools/generate_api.py @@ -0,0 +1,19 @@ +# Copyright (c) 2013 Intel Corporation. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +TEMPLATE = """\ +extern const char %s[]; +const char %s[] = { %s, 0 }; +""" + +js_code = sys.argv[1] +lines = file(js_code).read() +c_code = ', '.join(str(ord(c)) for c in lines) + +symbol_name = sys.argv[2] +output = open(sys.argv[3], "w") +output.write(TEMPLATE % (symbol_name, symbol_name, c_code)) +output.close() From 68778e94b2a9ff416898155adac99da9554f2bf4 Mon Sep 17 00:00:00 2001 From: Amarnath Valluri Date: Tue, 18 Nov 2014 16:26:10 +0200 Subject: [PATCH 20/26] packaging: use %config macro for installing configuration file Change-Id: I12821dd907eab20dfee916fc28cd89f103190c7e --- packaging/speech-recognition.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index aa68cf5..47fb666 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -165,7 +165,7 @@ ldconfig %{_libdir}/libsrs*.so.* # crosswalk speech extension. %{_libdir}/tizen-extensions-crosswalk/* -%{_sysconfdir}/speech-recognition/speech-recognition.conf +%config %{_sysconfdir}/speech-recognition/speech-recognition.conf %dir %{_sysconfdir}/speech-recognition/w3c-grammars %{_datadir}/speech-recognition/dictionaries %dir %{_datadir}/speech-recognition/dictionaries/w3c-speech From b841a531b15a8ef425de5e8b584a7f6211ed5ba3 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Sun, 23 Nov 2014 19:04:35 +0200 Subject: [PATCH 21/26] packaging: bumped version, updated changelog. Change-Id: If0663c48a40e8903672117fb699f159fa4474764 --- packaging/speech-recognition.changes | 3 +++ packaging/speech-recognition.spec | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packaging/speech-recognition.changes b/packaging/speech-recognition.changes index 99f3a61..a8c631c 100644 --- a/packaging/speech-recognition.changes +++ b/packaging/speech-recognition.changes @@ -1,3 +1,6 @@ +* Fri Nov 23 15:24:12 EEST 2014 Krisztian Litkey - 0.0.10 +- w3c-speech: added crosswalk W3C Speech API extension. + * Fri Nov 07 15:24:12 EEST 2014 Krisztian Litkey - 0.0.9 - packaging: generate config file (to get %{_datadir} right). - synced with latest upstream, including static analysis fixes. diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index 47fb666..b6da917 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -6,7 +6,7 @@ Summary: Speech recognition service for Tizen Name: speech-recognition -Version: 0.0.9 +Version: 0.0.10 Release: 0 License: BSD-3-Clause Group: Base/Utilities From 8ad5ea439823e98cf8cb94d6c8dfe73253124782 Mon Sep 17 00:00:00 2001 From: Amarnath Valluri Date: Tue, 25 Nov 2014 10:59:59 +0200 Subject: [PATCH 22/26] [Speech API]: running mainloop in crosswalk extension Change-Id: I8975e7e5cd1fadf1dad493ee63d61d8f3f94862a --- packaging/speech-recognition.spec | 1 + .../crosswalk-extension/speech_instance.cc | 24 ++++++++++++++++++- .../crosswalk-extension/speech_instance.h | 5 ++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packaging/speech-recognition.spec b/packaging/speech-recognition.spec index b6da917..41d23f7 100644 --- a/packaging/speech-recognition.spec +++ b/packaging/speech-recognition.spec @@ -68,6 +68,7 @@ export CFLAGS="-O0 -g3" export CXXFLAGS="-O0 -g3" V="V=1" %endif +export CXXFLAGS=$CXXFLAGS" -DTIZEN=1" CONFIG_OPTIONS="" diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc index 65424c4..0428d5c 100644 --- a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc @@ -20,13 +20,30 @@ #include "speech_logs.h" #define WINTHORP_SERVER_SOCKET "@winthorpe.w3c-speech" +#ifdef TIZEN +// static +void SpeechInstance::SetupMainloop(void* data) { + SpeechInstance* self = reinterpret_cast(data); + + g_main_loop_run(self->main_loop_); +} +#endif // TIZEN SpeechInstance::SpeechInstance() - : fd_(-1) +#ifdef TIZEN + : main_loop_(g_main_loop_new(0, FALSE)) + , thread_(SpeechInstance::SetupMainloop, this) + , fd_(-1) +#else + : fd_(-1) +#endif // TIZEN , channel_(NULL) , watcher_id_(0) , pending_request_timer_(0) , pending_reply_timer_(0) { +#ifdef TIZEN + thread_.detach(); +#endif // TIZEN if ((fd_ = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { ERR("Failed to create socket: %s", strerror(errno)); fd_ = -1; @@ -67,6 +84,11 @@ SpeechInstance::~SpeechInstance() { close(fd_); if (channel_) g_io_channel_unref(channel_); + +#ifdef TIZEN + g_main_loop_quit(main_loop_); + g_main_loop_unref(main_loop_); +#endif // TIZEN } // static diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h index 065be50..c15b813 100644 --- a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h @@ -34,6 +34,11 @@ class SpeechInstance : public common::Instance { void QueueRequest(const std::string& req); bool SendRequest(const char* message); uint32_t ReadReply(char** reply); +#ifdef TIZEN + static void SetupMainloop(void *data); + GMainLoop* main_loop_; + std::thread thread_; +#endif // TIZEN std::queue pending_replies_; std::queue pending_requests_; From 8e3ade40c159e69006a8c04ea30c7f658a307561 Mon Sep 17 00:00:00 2001 From: Amarnath Valluri Date: Tue, 25 Nov 2014 14:55:04 +0200 Subject: [PATCH 23/26] [W3C-Speech]: Fix issues found in extension code - Timing issues in object registraion(sync calls) - Result event generation Change-Id: I8142f9aa2acb9336c4bc1b638d2f4f4414306150 --- .../crosswalk-extension/speech_api.js | 26 +++++++++---------- .../crosswalk-extension/speech_instance.cc | 10 ++++++- .../crosswalk-extension/speech_instance.h | 1 + 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js index 7b6d845..9a80407 100644 --- a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js @@ -72,6 +72,7 @@ var args = { }; var obj = _dynamic_objects[msg.id]; if (!obj) { + /* Synthesizer events */ if (msg.event == 'pending') { _v8tools.forceSetProperty(exports, 'pending', msg.state); } else if (msg.event == 'speaking') { @@ -91,9 +92,17 @@ } else if (msg.event == 'result') { // currently we support only signle shot result args.resultIndex = 0; - args.results = msg.results; args.interpretation = null; args.emma = null; + var res = new SpeechRecognitionResult(msg.final); + msg.results.forEach(function(alternative) { + res.push({ + 'transcript': alternative.transcript, + 'confidence': alternative.confidence + }); + }); + DBG('Result Length: ' + res.length); + args.results = [ res ]; } else if (msg.event == 'nomatch') { args.resultIndex = 0; args.results = null; @@ -110,7 +119,7 @@ args.name = null; } } else if (msg.event == 'end') { - if (obj.type == 'recognizer') { + if (obj._type == 'recognizer') { } else { args.charIndex = msg.charIndex; args.elapsedTime = msg.elapsedTime; @@ -236,22 +245,12 @@ // SpeechRecognition : Speech -> Text // function SpeechRecognitionResult(is_final) { - this.push.apply(this, arguments); - _addConstProperty(this, 'final', is_final || true); + _addConstProperty(this, 'isFinal', is_final || true); this.item = function(index) { return this[index]; }; - - this.toJSON = function() { - var a = []; - for (var i = 0; i < this.length; i++) { - a[i] = this[i]; - } - return a; - }; - } SpeechRecognitionResult.prototype = Object.create(Array.prototype); SpeechRecognitionResult.prototype.construrctor = SpeechRecognitionResult; @@ -285,6 +284,7 @@ function SpeechRecognition() { this._id = -1; this._is_active = 0; + this._type = 'recognizer'; EventTarget.call(this, [ 'audiostart', diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc index 0428d5c..8934942 100644 --- a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.cc @@ -25,6 +25,8 @@ void SpeechInstance::SetupMainloop(void* data) { SpeechInstance* self = reinterpret_cast(data); + DBG("Running Mainloop..."); + g_main_loop_run(self->main_loop_); } #endif // TIZEN @@ -40,7 +42,8 @@ SpeechInstance::SpeechInstance() , channel_(NULL) , watcher_id_(0) , pending_request_timer_(0) - , pending_reply_timer_(0) { + , pending_reply_timer_(0) + , is_waiting_for_reply_(false) { #ifdef TIZEN thread_.detach(); #endif // TIZEN @@ -110,6 +113,8 @@ gboolean SpeechInstance::IOWatchCb(GIOChannel *c, char *reply = NULL; uint32_t size = 0; + if (self->is_waiting_for_reply_) break; + if ((size = self->ReadReply(&reply))) { self->PostMessage(reply); free(reply); @@ -262,6 +267,8 @@ void SpeechInstance::HandleSyncMessage(const char* message) { obj["message"] = picojson::value("server connection failure"); out = picojson::value(obj); } else { + + is_waiting_for_reply_ = true; picojson::parse(v, message, message + strlen(message), &err); if (!err.empty()) return; @@ -273,6 +280,7 @@ void SpeechInstance::HandleSyncMessage(const char* message) { uint32_t size; if ((size = ReadReply(&reply)) != 0) { + is_waiting_for_reply_ = false; picojson::parse(out, reply, reply + size, &err); free(reply); if (!err.empty()) { diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h index c15b813..28b66e2 100644 --- a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_instance.h @@ -47,5 +47,6 @@ class SpeechInstance : public common::Instance { guint watcher_id_; guint pending_request_timer_; guint pending_reply_timer_; + bool is_waiting_for_reply_; }; #endif // SPEECH_SPEECH_INSTANCE_H_ From 552998ab1fd543008f6a5d790a98dc10cbff3b4e Mon Sep 17 00:00:00 2001 From: Amarnath Valluri Date: Tue, 25 Nov 2014 15:11:59 +0200 Subject: [PATCH 24/26] [W3C-Speech]: sample html page to test crosswalk extension Change-Id: Ie3f759fa0c4d1321a519319dbd64af22bd9d4740 --- .../example/w3c_speech.html | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/plugins/client-api/w3c-speech/crosswalk-extension/example/w3c_speech.html diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/example/w3c_speech.html b/src/plugins/client-api/w3c-speech/crosswalk-extension/example/w3c_speech.html new file mode 100644 index 0000000..0ba87b5 --- /dev/null +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/example/w3c_speech.html @@ -0,0 +1,128 @@ + + +Speech Example + + + + +
+
Type in your text here:
+
+ +
+ + + + +
+
+ +
+ +
+
+ + From 4ba73b34c4bc8fd8da5a1c2ca50c3267ce10a093 Mon Sep 17 00:00:00 2001 From: Amarnath Valluri Date: Tue, 25 Nov 2014 15:26:59 +0200 Subject: [PATCH 25/26] [W3C-Speech]: Fix configuration entry of dictionaries entry Change-Id: I2394b6b5a294505ab5bcd7489be7608abfa561a1 --- packaging/speech-recognition.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/speech-recognition.conf.in b/packaging/speech-recognition.conf.in index 82e6b51..278e062 100644 --- a/packaging/speech-recognition.conf.in +++ b/packaging/speech-recognition.conf.in @@ -49,7 +49,7 @@ load search-client load native-client w3c-speech.address = unxs:@winthorpe.w3c-speech -w3c-speech.grammars = @DATADIR@/speech-recognition/w3c-grammars +w3c-speech.grammars = @DATADIR@/speech-recognition/dictionaries/w3c-speech load w3c-speech # wrt-media-client needs GMainLoop From 7003ae1d22d9b3087caee5ccf0a30751cac6dea3 Mon Sep 17 00:00:00 2001 From: Amarnath Valluri Date: Tue, 9 Dec 2014 15:13:30 +0200 Subject: [PATCH 26/26] Bug Fix TC-661: SpeechSynthesisUtterance.text has no effect Change-Id: Ib81bbe9931df993d5c3768705a76edb9b7b49b61 --- .../crosswalk-extension/speech_api.js | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js index 9a80407..05215ca 100644 --- a/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js +++ b/src/plugins/client-api/w3c-speech/crosswalk-extension/speech_api.js @@ -63,6 +63,15 @@ }); } + function _addPropertyWithSetterGetter(obj, prop, setter, getter) { + Object.defineProperty(obj, prop, { + set: setter, + get: getter, + configurable: true, + enumerable: true + }); + } + extension.setMessageListener(function(json) { var msg = JSON.parse(json); var reqno = msg.reqno; @@ -407,6 +416,12 @@ function SpeechSynthesisUtterance(text) { this._id = -1; + var _text = text ? text: ''; + var _lang = 'english'; + var _voiceURI = null; + var _volume = 1.0; + var _rate = 0.0; + var _pitch = 1.0; EventTarget.call(this, [ 'start', @@ -418,15 +433,49 @@ 'boundary' ]); - _addProperty(this, 'text', text); - _addProperty(this, 'lang', 'english'); - _addProperty(this, 'voiceURI'); - _addProperty(this, 'volume', 1.0); - _addProperty(this, 'rate', 0, 0); - _addProperty(this, 'pitch', 1.0); - var ut = this; + function setAttribute(name, value) { + if (ut._id == -1) { + DBG('Ignoring setAttribute request as no id preset for this object'); + return; + } + var set = {}; + set[name] = value; + _sendSyncMessage({ + 'reqno': _getNextReqId(), + 'type': 'set', + 'id': ut._id, + 'set': set + }); + } + + _addPropertyWithSetterGetter(this, 'text', function(text) { + _text = text; + setAttribute('text', _text); + }, function() { return _text; }); + _addPropertyWithSetterGetter(this, 'lang', function(lang) { + _lang = lang; + setAttribute('lang', lang); + }, function() { return _lang; }); + _addPropertyWithSetterGetter(this, 'voiceURI', function(uri) { + _voiceURI = uri; + setAttribute('voiceURI', uri); + }, function() { return _voiceURI; }); + _addPropertyWithSetterGetter(this, 'volume', function(volume) { + _volume = volume; + setAttribute('volume', volume); + }, function() { return _volume; }); + _addPropertyWithSetterGetter(this, 'rate', function(rate) { + _rate = rate; + setAttribute('rate', rate); + }, function() { return _rate; }); + _addPropertyWithSetterGetter(this, 'pitch', function(pitch) { + _pitch = pitch; + setAttribute('pitch', pitch); + }, function() { return _pitch; }); + + this.addEventListener('end', function() { _dynamic_objects[ut._id] = null; });