summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-08-09 15:05:52 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-08-09 15:05:52 (GMT)
commit6dce9df7f483f3229bb2f34f0386ce37a1551e07 (patch)
tree1d3acaec4612d74ee3234c808df7ae5fa3b4ef9f
parent01f8198f8b548e3f28cad1a441ceb8af6ea850a4 (diff)
downloaduscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.zip
uscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.tar.gz
uscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.tar.bz2
Extended Java bindings and OpenAL invoker
-rw-r--r--CMakeLists.txt28
-rw-r--r--apps/uscxml-browser.cpp3
-rw-r--r--config.h.in3
-rw-r--r--contrib/cmake/FINDOpenALKCAT.cmake31
-rw-r--r--contrib/cmake/FindLIBSNDFILE.cmake45
-rw-r--r--src/bindings/swig/java/JavaInvoker.cpp8
-rw-r--r--src/bindings/swig/java/JavaInvoker.h39
-rw-r--r--src/bindings/swig/java/stl_list.i49
-rw-r--r--src/bindings/swig/java/stl_set.i73
-rw-r--r--src/bindings/swig/java/uscxml.i29
-rw-r--r--src/bindings/swig/php/uscxmlNativePHP.php14
-rw-r--r--src/uscxml/Factory.cpp27
-rw-r--r--src/uscxml/Factory.h5
-rw-r--r--src/uscxml/Interpreter.cpp18
-rw-r--r--src/uscxml/Message.h10
-rw-r--r--src/uscxml/URL.cpp18
-rw-r--r--src/uscxml/URL.h6
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.cpp2
-rw-r--r--src/uscxml/plugins/datamodel/CMakeLists.txt2
-rw-r--r--src/uscxml/plugins/invoker/CMakeLists.txt35
-rw-r--r--src/uscxml/plugins/invoker/audio/AudioToolbox.h32
-rw-r--r--src/uscxml/plugins/invoker/audio/AudioToolbox.mm152
-rw-r--r--src/uscxml/plugins/invoker/audio/LibSoundFile.cpp50
-rw-r--r--src/uscxml/plugins/invoker/audio/LibSoundFile.h27
-rw-r--r--src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp362
-rw-r--r--src/uscxml/plugins/invoker/audio/OpenALInvoker.h88
-rw-r--r--src/uscxml/plugins/invoker/audio/OpenALPlayer.cpp523
-rw-r--r--src/uscxml/plugins/invoker/audio/OpenALPlayer.h106
-rw-r--r--src/uscxml/plugins/invoker/audio/PCMConverter.h30
-rw-r--r--src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp2
-rw-r--r--src/uscxml/server/HTTPServer.cpp2
-rw-r--r--test/samples/uscxml/test-java-invoker.scxml39
-rw-r--r--test/samples/uscxml/test-openal.scxml55
-rw-r--r--test/samples/uscxml/test-predicates.scxml4
-rw-r--r--test/src/test-predicates.cpp10
35 files changed, 1909 insertions, 18 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 685c010..eb8bf46 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -462,6 +462,9 @@ list (APPEND USCXML_CORE_LIBS ${EVENT_LIBRARY})
# Optional libraries
if (APPLE OR IOS)
+ find_library(AUDIOTOOLBOX AudioToolbox REQUIRED)
+ list (APPEND USCXML_OPT_LIBS ${AUDIOTOOLBOX})
+
find_library(JSC_LIBRARY JavaScriptCore)
if (IOS)
find_library(WTF_LIBRARY WTF)
@@ -475,6 +478,7 @@ if (APPLE OR IOS)
list (APPEND USCXML_OPT_LIBS ${JSC_LIBRARY})
endif()
set(JSC_FOUND ON)
+ set(AUDIOTOOLBOX_FOUND ON)
else()
find_package(V8)
if (V8_FOUND)
@@ -550,6 +554,30 @@ if (UNIX)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_STATIC})
endif()
+set(CMAKE_FIND_FRAMEWORK "FIRST")
+find_package(OpenAL)
+if (OPENAL_FOUND)
+ list (APPEND USCXML_INCLUDE_DIRS ${OPENAL_INCLUDE_DIR})
+ list (APPEND USCXML_OPT_LIBS ${OPENAL_LIBRARY})
+else()
+ find_package(OpenALKCAT)
+ if (OPENAL_FOUND)
+ list (APPEND USCXML_INCLUDE_DIRS ${OPENAL_INCLUDE_DIR})
+ list (APPEND USCXML_OPT_LIBS ${OPENAL_LIBRARY})
+ endif()
+endif()
+set(CMAKE_FIND_FRAMEWORK "LAST")
+
+if (NOT AUDIOTOOLBOX_FOUND)
+ set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SHARED})
+ find_package(LIBSNDFILE)
+ if (LIBSNDFILE_FOUND)
+ list (APPEND USCXML_INCLUDE_DIRS ${LIBSNDFILE_INCLUDE_DIR})
+ list (APPEND USCXML_OPT_LIBS ${LIBSNDFILE_LIBRARY})
+ endif()
+ set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_STATIC})
+endif()
+
find_package(MILES)
if (MILES_FOUND)
# openal is only needed for miles
diff --git a/apps/uscxml-browser.cpp b/apps/uscxml-browser.cpp
index 39f81d7..48c1875 100644
--- a/apps/uscxml-browser.cpp
+++ b/apps/uscxml-browser.cpp
@@ -87,6 +87,9 @@ void customTerminate() {
} catch (const std::exception &e) {
std::cerr << __FUNCTION__ << " caught unhandled exception. what(): "
<< e.what() << std::endl;
+ } catch (const std::runtime_error &e) {
+ std::cerr << __FUNCTION__ << " caught unhandled exception. what(): "
+ << e.what() << std::endl;
} catch (const uscxml::Event &e) {
std::cerr << __FUNCTION__ << " caught unhandled exception. Event: "
<< e << std::endl;
diff --git a/config.h.in b/config.h.in
index 52cfcdc..af772c6 100644
--- a/config.h.in
+++ b/config.h.in
@@ -57,6 +57,9 @@
/** Optional libraries we found */
#cmakedefine UMUNDO_FOUND
+#cmakedefine OPENAL_FOUND
+#cmakedefine LIBSNDFILE_FOUND
+#cmakedefine AUDIOTOOLBOX_FOUND
#cmakedefine MILES_FOUND
#cmakedefine V8_FOUND
#cmakedefine JSC_FOUND
diff --git a/contrib/cmake/FINDOpenALKCAT.cmake b/contrib/cmake/FINDOpenALKCAT.cmake
new file mode 100644
index 0000000..982468d
--- /dev/null
+++ b/contrib/cmake/FINDOpenALKCAT.cmake
@@ -0,0 +1,31 @@
+SET(WIN_DIRECTORIES "")
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ list(APPEND WIN_DIRECTORIES "C:/Program Files/openal-soft-1.15.1-bin/lib/Win64")
+else()
+ list(APPEND WIN_DIRECTORIES "C:/Program Files/openal-soft-1.15.1-bin/lib/Win32")
+endif()
+
+find_path(OPENAL_INCLUDE_DIR al.h
+ HINTS
+ ENV OPENALDIR
+ PATH_SUFFIXES include/AL include/OpenAL include
+ PATHS
+ "C:/Program Files/openal-soft-1.15.1-bin"
+)
+
+find_library(OPENAL_LIBRARY
+ NAMES OpenAL libOpenAL32.dll libOpenAL64.dll
+ HINTS
+ ENV OPENALDIR
+ PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64
+ PATHS
+ ${WIN_DIRECTORIES}
+)
+
+
+# handle the QUIETLY and REQUIRED arguments and set OPENAL_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenAL DEFAULT_MSG OPENAL_LIBRARY OPENAL_INCLUDE_DIR)
+
+mark_as_advanced(OPENAL_LIBRARY OPENAL_INCLUDE_DIR)
diff --git a/contrib/cmake/FindLIBSNDFILE.cmake b/contrib/cmake/FindLIBSNDFILE.cmake
new file mode 100644
index 0000000..2219aaf
--- /dev/null
+++ b/contrib/cmake/FindLIBSNDFILE.cmake
@@ -0,0 +1,45 @@
+SET(WIN_DIRECTORIES "")
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ list(APPEND WIN_DIRECTORIES "C:/Program Files/Mega-Nerd/libsndfile")
+else()
+ list(APPEND WIN_DIRECTORIES "C:/Program Files (x86)/Mega-Nerd/libsndfile")
+endif()
+
+FIND_PATH(LIBSNDFILE_INCLUDE_DIR sndfile.h
+ PATH_SUFFIXES include src
+ PATHS
+ /usr/local
+ /usr
+ /sw # Fink
+ /opt/local # DarwinPorts
+ /opt/csw # Blastwave
+ /opt
+ ${WIN_DIRECTORIES}
+ HINTS $ENV{LIBSNDFILE_SRC}
+)
+
+FIND_LIBRARY(LIBSNDFILE_LIBRARY_RELEASE
+ PATH_SUFFIXES bin lib
+ NAMES sndfile libsndfile libsndfile-1
+ PATHS ${WIN_DIRECTORIES}
+)
+if (LIBSNDFILE_LIBRARY_RELEASE)
+ list(APPEND LIBSNDFILE_LIBRARY optimized ${LIBSNDFILE_LIBRARY_RELEASE})
+endif()
+
+FIND_LIBRARY(LIBSNDFILE_LIBRARY_DEBUG
+ PATH_SUFFIXES bin lib
+ NAMES sndfile_d libsndfile_d libsndfile-1_d
+ PATHS ${WIN_DIRECTORIES}
+)
+if (LIBSNDFILE_LIBRARY_DEBUG)
+ list(APPEND LIBSNDFILE_LIBRARY debug ${LIBSNDFILE_LIBRARY_DEBUG})
+else()
+# if (UNIX)
+ list(APPEND LIBSNDFILE_LIBRARY debug ${LIBSNDFILE_LIBRARY_RELEASE})
+# endif()
+endif()
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBSNDFILE DEFAULT_MSG LIBSNDFILE_LIBRARY LIBSNDFILE_INCLUDE_DIR)
+MARK_AS_ADVANCED(LIBSNDFILE_LIBRARY LIBSNDFILE_INCLUDE_DIR)
diff --git a/src/bindings/swig/java/JavaInvoker.cpp b/src/bindings/swig/java/JavaInvoker.cpp
new file mode 100644
index 0000000..c10ae6d
--- /dev/null
+++ b/src/bindings/swig/java/JavaInvoker.cpp
@@ -0,0 +1,8 @@
+#include "JavaInvoker.h"
+
+namespace uscxml {
+
+JavaInvoker::JavaInvoker() {}
+JavaInvoker::~JavaInvoker() {}
+
+} \ No newline at end of file
diff --git a/src/bindings/swig/java/JavaInvoker.h b/src/bindings/swig/java/JavaInvoker.h
new file mode 100644
index 0000000..0a13176
--- /dev/null
+++ b/src/bindings/swig/java/JavaInvoker.h
@@ -0,0 +1,39 @@
+#ifndef JAVAINVOKER_H_WDV9B5F6
+#define JAVAINVOKER_H_WDV9B5F6
+
+#include "../../../uscxml/Message.h"
+#include "../../../uscxml/Factory.h"
+#include "../../../uscxml/Interpreter.h"
+
+namespace uscxml {
+
+class JavaInvoker : public InvokerImpl {
+public:
+ JavaInvoker();
+ virtual ~JavaInvoker();
+
+ virtual std::set<std::string> getNames() {
+ return std::set<std::string>();
+ };
+
+ virtual Data getDataModelVariables() {
+ Data data;
+ return data;
+ }
+
+ virtual void send(const SendRequest& req) {}
+ virtual void invoke(const InvokeRequest& req) {}
+
+ virtual JavaInvoker* create(Interpreter interpreter) {
+ return new JavaInvoker();
+ }
+
+ virtual boost::shared_ptr<InvokerImpl> create(InterpreterImpl* interpreter) {
+ return boost::shared_ptr<InvokerImpl>(create(interpreter->shared_from_this()));
+ }
+
+};
+
+}
+
+#endif /* end of include guard: JAVAINVOKER_H_WDV9B5F6 */
diff --git a/src/bindings/swig/java/stl_list.i b/src/bindings/swig/java/stl_list.i
new file mode 100644
index 0000000..aabd448
--- /dev/null
+++ b/src/bindings/swig/java/stl_list.i
@@ -0,0 +1,49 @@
+/* -----------------------------------------------------------------------------
+ * See the LICENSE file for information on copyright, usage and redistribution
+ * of SWIG, and the README file for authors - http://www.swig.org/release.html.
+ *
+ * std_list.i
+ * ----------------------------------------------------------------------------- */
+
+%include <std_common.i>
+
+%{
+#include <list>
+#include <stdexcept>
+%}
+
+namespace std {
+
+ template<class T> class list {
+ public:
+ typedef size_t size_type;
+ typedef T value_type;
+ typedef const value_type& const_reference;
+ list();
+ size_type size() const;
+ %rename(isEmpty) empty;
+ bool empty() const;
+ void clear();
+ %rename(add) push_back;
+ void push_back(const value_type& x);
+ %extend {
+ const_reference get(int i) throw (std::out_of_range) {
+ int size = int(self->size());
+ int j;
+ if (i>=0 && i<size) {
+ std::list<T>::const_iterator p;
+ p=self->begin();
+ for (j=0; j<i; j++) {p++;}
+ return (*p);
+ }
+ else
+ throw std::out_of_range("list index out of range");
+ }
+ }
+ };
+}
+
+%define specialize_std_list(T)
+#warning "specialize_std_list - specialization for type T no longer needed"
+%enddef
+
diff --git a/src/bindings/swig/java/stl_set.i b/src/bindings/swig/java/stl_set.i
new file mode 100644
index 0000000..d009a7b
--- /dev/null
+++ b/src/bindings/swig/java/stl_set.i
@@ -0,0 +1,73 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/* -----------------------------------------------------------------------------
+ * std_set.i
+ *
+ * SWIG typemaps for std::set
+ * ----------------------------------------------------------------------------- */
+
+%include <std_common.i>
+
+// ------------------------------------------------------------------------
+// std::set
+// ------------------------------------------------------------------------
+
+%{
+#include <set>
+#include <algorithm>
+#include <stdexcept>
+%}
+
+// exported class
+
+namespace std {
+
+ template<class V> class set {
+ // add typemaps here
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef V value_type;
+ set();
+ set(const set<V> &);
+
+ unsigned int size() const;
+ bool empty() const;
+ void clear();
+ %extend {
+ const V& get(const V& key) throw (std::out_of_range) {
+ std::set<V>::iterator i = self->find(key);
+ if (i != self->end())
+ return *i;
+ else
+ throw std::out_of_range("key not found");
+ }
+ void insert(const V& key) { // Do NOT call this function 'set' !
+ self->insert(key);
+ }
+ void del(const V& key) throw (std::out_of_range) {
+ std::set<V>::iterator i = self->find(key);
+ if (i != self->end())
+ self->erase(i);
+ else
+ throw std::out_of_range("key not found");
+ }
+ bool has_key(const V& key) {
+ std::set<V>::iterator i = self->find(key);
+ return i != self->end();
+ }
+ }
+ };
+
+} \ No newline at end of file
diff --git a/src/bindings/swig/java/uscxml.i b/src/bindings/swig/java/uscxml.i
index ee7133c..af979d0 100644
--- a/src/bindings/swig/java/uscxml.i
+++ b/src/bindings/swig/java/uscxml.i
@@ -3,8 +3,21 @@
// import swig typemaps
//%include <arrays_java.i>
//%include <inttypes.i>
+
+%include <stl.i>
+%include <std_map.i>
+%include <inttypes.i>
+%include "stl_set.i"
+%include "stl_list.i"
+
%include <boost_shared_ptr.i>
+
+typedef uscxml::Data Data;
+typedef uscxml::Event Event;
+typedef uscxml::InvokeRequest InvokeRequest;
+typedef uscxml::SendRequest SendRequest;
+
// disable warning related to unknown base class
#pragma SWIG nowarn=401
//%ignore boost::enable_shared_from_this;
@@ -32,10 +45,14 @@
%{
#include "../../../uscxml/Message.h"
+#include "../../../uscxml/Factory.h"
#include "../../../uscxml/Interpreter.h"
+#include "JavaInvoker.h"
using namespace uscxml;
+#include "JavaInvoker.cpp"
+
%}
%rename(toString) operator<<;
@@ -46,10 +63,22 @@ using namespace uscxml;
%ignore uscxml::Interpreter::getDelayQueue();
+%ignore uscxml::JavaInvoker::create(InterpreterImpl*);
+
+%template(DataMap) std::map<std::string, uscxml::Data>;
+%template(DataList) std::list<uscxml::Data>;
+%template(StringSet) std::set<std::string>;
+
+
+%feature("director") uscxml::JavaInvoker;
+
//***********************************************
// Parse the header file to generate wrappers
//***********************************************
+#define SWIGIMPORTED 1
+%include "../../../uscxml/Factory.h"
%include "../../../uscxml/Message.h"
%include "../../../uscxml/Interpreter.h"
+%include "JavaInvoker.h"
diff --git a/src/bindings/swig/php/uscxmlNativePHP.php b/src/bindings/swig/php/uscxmlNativePHP.php
index ef37b94..08fc515 100644
--- a/src/bindings/swig/php/uscxmlNativePHP.php
+++ b/src/bindings/swig/php/uscxmlNativePHP.php
@@ -306,8 +306,8 @@ class Data {
return Data_toXMLString($this->_cPtr);
}
- function getCompund() {
- return Data_getCompund($this->_cPtr);
+ function getCompound() {
+ return Data_getCompound($this->_cPtr);
}
function setCompound($compound) {
@@ -357,8 +357,8 @@ class Event {
}
function __get($var) {
- if ($var === 'data') return new Data(Event_data_get($this->_cPtr));
if ($var === 'namelist') return new StringMap(Event_namelist_get($this->_cPtr));
+ if ($var === 'data') return new Data(Event_data_get($this->_cPtr));
$func = 'Event_'.$var.'_get';
if (function_exists($func)) return call_user_func($func,$this->_cPtr);
if ($var === 'thisown') return swig_uscxmlNativePHP_get_newobject($this->_cPtr);
@@ -447,6 +447,14 @@ class Event {
Event_setContent($this->_cPtr,$content);
}
+ function getXML() {
+ return Event_getXML($this->_cPtr);
+ }
+
+ function setXML($xml) {
+ Event_setXML($this->_cPtr,$xml);
+ }
+
function getSendId() {
return Event_getSendId($this->_cPtr);
}
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp
index b8bf60a..9b6a84a 100644
--- a/src/uscxml/Factory.cpp
+++ b/src/uscxml/Factory.cpp
@@ -49,6 +49,10 @@
# include "uscxml/plugins/invoker/calendar/CalendarInvoker.h"
# endif
+#ifdef OPENAL_FOUND
+# include "uscxml/plugins/invoker/audio/OpenALInvoker.h"
+#endif
+
# ifdef CORELOCATION_FOUND
# include "uscxml/plugins/invoker/location/CoreLocation/LocationInvoker.h"
# endif
@@ -162,6 +166,13 @@ Factory::Factory() {
}
#endif
+#if (defined OPENAL_FOUND && (defined LIBSNDFILE_FOUND || defined AUDIOTOOLBOX_FOUND))
+ {
+ OpenALInvoker* invoker = new OpenALInvoker();
+ registerInvoker(invoker);
+ }
+#endif
+
#ifdef OPENSCENEGRAPH_FOUND
{
OSGInvoker* invoker = new OSGInvoker();
@@ -472,6 +483,22 @@ Factory* Factory::getInstance() {
return _instance;
}
+void EventHandlerImpl::returnErrorExecution(const std::string& cause) {
+ Event exceptionEvent;
+ exceptionEvent.data.compound["exception"] = Data(cause, Data::VERBATIM);
+ exceptionEvent.name = "error.execution";
+ exceptionEvent.type = Event::PLATFORM;
+ returnEvent(exceptionEvent);
+}
+
+void EventHandlerImpl::returnErrorPlatform(const std::string& cause) {
+ Event exceptionEvent;
+ exceptionEvent.data.compound["exception"] = Data(cause, Data::VERBATIM);
+ exceptionEvent.name = "error.platform";
+ exceptionEvent.type = Event::PLATFORM;
+ returnEvent(exceptionEvent);
+}
+
void EventHandlerImpl::returnEvent(Event& event) {
if (event.invokeid.length() == 0)
event.invokeid = _invokeId;
diff --git a/src/uscxml/Factory.h b/src/uscxml/Factory.h
index 3ddd9ad..74cb2a4 100644
--- a/src/uscxml/Factory.h
+++ b/src/uscxml/Factory.h
@@ -111,6 +111,8 @@ protected:
class EventHandlerImpl {
public:
+ virtual ~EventHandlerImpl() {}
+
virtual std::set<std::string> getNames() = 0;
virtual void setInterpreter(InterpreterImpl* interpreter) {
@@ -128,6 +130,8 @@ public:
virtual void runOnMainThread() {};
void returnEvent(Event& event);
+ void returnErrorExecution(const std::string&);
+ void returnErrorPlatform(const std::string&);
protected:
InterpreterImpl* _interpreter;
@@ -211,6 +215,7 @@ protected:
class InvokerImpl : public EventHandlerImpl {
public:
+ virtual ~InvokerImpl() {}
virtual void invoke(const InvokeRequest& req) = 0;
virtual boost::shared_ptr<InvokerImpl> create(InterpreterImpl* interpreter) = 0;
};
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 77c6805..b6266e2 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -709,6 +709,13 @@ void InterpreterImpl::send(const Arabica::DOM::Node<std::string>& element) {
return;
}
+ if (sendReq.dom) {
+ std::stringstream ss;
+ ss << sendReq.dom;
+ sendReq.xml = ss.str();
+ _dataModel.replaceExpressions(sendReq.xml);
+ }
+
assert(_sendIds.find(sendReq.sendid) == _sendIds.end());
_sendIds[sendReq.sendid] = std::make_pair(this, sendReq);
if (sendReq.delayMs > 0) {
@@ -839,6 +846,13 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node<std::string>& element) {
return;
}
+ if (invokeReq.dom) {
+ std::stringstream ss;
+ ss << invokeReq.dom;
+ invokeReq.xml = ss.str();
+ _dataModel.replaceExpressions(invokeReq.xml);
+ }
+
// test 422
if (invokeReq.type.size() == 0)
invokeReq.type = "http://www.w3.org/TR/scxml/";
@@ -1330,11 +1344,11 @@ Arabica::XPath::NodeSet<std::string> InterpreterImpl::getStates(const std::vecto
Arabica::DOM::Node<std::string> InterpreterImpl::getState(const std::string& stateId) {
- if (_cachedStates.find(stateId) != _cachedStates.end() && false) {
+ if (_cachedStates.find(stateId) != _cachedStates.end()) {
return _cachedStates[stateId];
}
- // first try atomic and compund states
+ // first try atomic and compound states
NodeSet<std::string> target = _xpath.evaluate("//" + _xpathPrefix + "state[@id='" + stateId + "']", _scxml).asNodeSet();
if (target.size() > 0)
goto FOUND;
diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h
index dda2279..ed481e2 100644
--- a/src/uscxml/Message.h
+++ b/src/uscxml/Message.h
@@ -72,7 +72,7 @@ public:
return ss.str();
}
- std::map<std::string, Data> getCompund() {
+ std::map<std::string, Data> getCompound() {
return compound;
}
void setCompound(const std::map<std::string, Data>& compound) {
@@ -187,6 +187,13 @@ public:
this->content = content;
}
+ std::string getXML() {
+ return xml;
+ }
+ void setXML(const std::string& xml) {
+ this->xml = xml;
+ }
+
std::string getSendId() {
return sendid;
}
@@ -270,6 +277,7 @@ protected:
#endif
std::string raw;
+ std::string xml;
std::string name;
Type type;
std::string origin;
diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp
index ac947d7..65cd5ee 100644
--- a/src/uscxml/URL.cpp
+++ b/src/uscxml/URL.cpp
@@ -64,6 +64,24 @@ URLImpl::~URLImpl() {
curl_easy_cleanup(_handle);
}
+URLImpl::operator Data() {
+ Data data;
+ data.compound["url"] = Data(asString(), Data::VERBATIM);
+ data.compound["host"] = Data(_uri.host(), Data::VERBATIM);
+ data.compound["scheme"] = Data(_uri.scheme(), Data::VERBATIM);
+ data.compound["path"] = Data(_uri.path(), Data::VERBATIM);
+ data.compound["port"] = Data(_uri.port());
+ data.compound["isAbsolute"] = Data(_uri.is_absolute() ? "true" : "false");
+
+ std::vector<std::string>::iterator pathIter = _pathComponents.begin();
+ while(pathIter != _pathComponents.end()) {
+ data.compound["pathComponent"].array.push_back(Data(*pathIter, Data::VERBATIM));
+ pathIter++;
+ }
+
+ return data;
+}
+
CURL* URLImpl::getCurlHandle() {
if (_handle == NULL) {
_handle = curl_easy_init();
diff --git a/src/uscxml/URL.h b/src/uscxml/URL.h
index bb33a7c..646ea3f 100644
--- a/src/uscxml/URL.h
+++ b/src/uscxml/URL.h
@@ -8,6 +8,7 @@
#include <map>
#include <vector>
#include <set>
+#include "Message.h"
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <sys/stat.h>
@@ -89,6 +90,7 @@ public:
bool downloadFailed() {
return _hasFailed;
}
+ operator Data();
friend class URLFetcher;
@@ -225,6 +227,10 @@ public:
friend class URLFetcher;
friend std::ostream & operator<<(std::ostream &stream, const URL& p);
+ operator Data() {
+ return _impl->operator Data();
+ }
+
protected:
void downloadStarted() {
return _impl->downloadStarted();
diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp
index 9140d17..0291ab1 100644
--- a/src/uscxml/interpreter/InterpreterDraft6.cpp
+++ b/src/uscxml/interpreter/InterpreterDraft6.cpp
@@ -882,7 +882,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
executeContent(onEntryElems);
if (isMember(stateElem, statesForDefaultEntry)) {
- // execute initial transition content for compund states
+ // execute initial transition content for compound states
Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", stateElem).asNodeSet();
for (int j = 0; j < transitions.size(); j++) {
executeContent(transitions[j]);
diff --git a/src/uscxml/plugins/datamodel/CMakeLists.txt b/src/uscxml/plugins/datamodel/CMakeLists.txt
index 01f8279..731e316 100644
--- a/src/uscxml/plugins/datamodel/CMakeLists.txt
+++ b/src/uscxml/plugins/datamodel/CMakeLists.txt
@@ -1,4 +1,4 @@
-if (APPLE OR IOS)
+if (JSC_FOUND AND BUILD_DM_ECMA)
# JavaScriptCore ecmascript datamodel
file(GLOB JSC_DATAMODEL
ecmascript/JavaScriptCore/*.cpp
diff --git a/src/uscxml/plugins/invoker/CMakeLists.txt b/src/uscxml/plugins/invoker/CMakeLists.txt
index 4f13aaf..e23d65e 100644
--- a/src/uscxml/plugins/invoker/CMakeLists.txt
+++ b/src/uscxml/plugins/invoker/CMakeLists.txt
@@ -3,6 +3,7 @@ file(GLOB_RECURSE SAMPLE_INVOKER
sample/*.cpp
sample/*.h
)
+
source_group("Invoker\\sample" FILES ${SAMPLE_INVOKER})
if (BUILD_AS_PLUGINS)
add_library(
@@ -14,7 +15,6 @@ else()
list (APPEND USCXML_FILES ${SAMPLE_INVOKER})
endif()
-
# DirMon invoker to watch for filesystem changes
file(GLOB_RECURSE DIRMON_INVOKER
@@ -281,7 +281,40 @@ if (OPENSCENEGRAPH_FOUND AND OPENGL_FOUND)
else()
list (APPEND USCXML_FILES ${OPENSCENEGRAPH_CONVERTER_INVOKER})
endif()
+endif()
+
+
+# OpenAL modality components
+
+if (OPENAL_FOUND AND (LIBSNDFILE_FOUND OR AUDIOTOOLBOX_FOUND))
+ file(GLOB_RECURSE OPENAL_INVOKER
+ audio/OpenAL*.cpp
+ audio/OpenAL*.h)
+ if (LIBSNDFILE_FOUND)
+ file(GLOB_RECURSE LIBSNDFILE_WRAPPER
+ audio/LibSoundFile*.cpp
+ audio/LibSoundFile*.h)
+ LIST(APPEND OPENAL_INVOKER ${LIBSNDFILE_WRAPPER})
+ elseif(AUDIOTOOLBOX_FOUND)
+ file(GLOB_RECURSE AUDIOTOOLBOX_WRAPPER
+ audio/AudioToolbox*.mm
+ audio/AudioToolbox*.h)
+ LIST(APPEND OPENAL_INVOKER ${AUDIOTOOLBOX_WRAPPER})
+ endif()
+ source_group("Invoker\\audio" FILES ${OPENAL_INVOKER})
+# message("MILES_INVOKER ${MILES_INVOKER}")
+ if (BUILD_AS_PLUGINS)
+ add_library(
+ invoker_openal SHARED
+ ${OPENAL_INVOKER})
+ target_link_libraries(invoker_openal
+ ${OPENAL_LIBRARY}
+ uscxml)
+ set_target_properties(invoker_openal PROPERTIES FOLDER "Plugin Invoker")
+ else()
+ list (APPEND USCXML_FILES ${OPENAL_INVOKER})
+ endif()
endif()
diff --git a/src/uscxml/plugins/invoker/audio/AudioToolbox.h b/src/uscxml/plugins/invoker/audio/AudioToolbox.h
new file mode 100644
index 0000000..3e04d8f
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/AudioToolbox.h
@@ -0,0 +1,32 @@
+#ifndef AUDIOTOOLBOX_H_GX4SW17C
+#define AUDIOTOOLBOX_H_GX4SW17C
+
+#include "PCMConverter.h"
+#include <AudioToolbox/AudioToolbox.h>
+
+namespace uscxml {
+
+class AudioToolbox : public PCMConverter {
+public:
+ AudioToolbox(const std::string filename);
+ virtual ~AudioToolbox();
+ virtual void seek(unsigned int pos);
+ virtual int read(char* buffer, unsigned int size);
+
+ virtual void setOutFormat(const PCMFormat& format);
+ virtual PCMFormat getInFormat();
+
+protected:
+ ExtAudioFileRef _afId;
+ AudioStreamBasicDescription _outputFormat;
+ AudioStreamBasicDescription _inputFormat;
+
+ ALenum formatToALEnum(AudioStreamBasicDescription);
+ bool alEnumToFormat(AudioStreamBasicDescription&, ALenum);
+};
+
+}
+
+#endif /* end of include guard: AUDIOTOOLBOX_H_GX4SW17C */
+
+
diff --git a/src/uscxml/plugins/invoker/audio/AudioToolbox.mm b/src/uscxml/plugins/invoker/audio/AudioToolbox.mm
new file mode 100644
index 0000000..44720b1
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/AudioToolbox.mm
@@ -0,0 +1,152 @@
+#include "AudioToolbox.h"
+#include <glog/logging.h>
+
+#import <Foundation/Foundation.h>
+#import <Foundation/NSURL.h>
+
+namespace uscxml {
+
+AudioToolbox::AudioToolbox(const std::string filename) {
+ @autoreleasepool {
+ _afId = 0;
+ NSString* filePath = [NSString stringWithCString:filename.c_str() encoding:NSASCIIStringEncoding];
+ NSURL* afUrl = [NSURL fileURLWithPath:filePath];
+
+ OSStatus result = noErr;
+
+ result = ExtAudioFileOpenURL((CFURLRef)afUrl, &_afId);
+
+ if (result != noErr) {
+ LOG(WARNING) << "Cannot open audio file " << filename;
+ return;
+ }
+ UInt32 thePropertySize = sizeof(_inputFormat);
+ result = ExtAudioFileGetProperty(_afId, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &_inputFormat);
+ if (result != noErr) {
+ LOG(WARNING) << "Cannot determine input format of " << filename;
+ return;
+ }
+
+ // output format is input format
+ memcpy(&_outputFormat, &_inputFormat, sizeof(_inputFormat));
+
+ // except for things that make no sense for open al
+ _outputFormat.mFormatID = kAudioFormatLinearPCM;
+ _outputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
+
+ ALenum bestFormat = formatToALEnum(_outputFormat);
+ alEnumToFormat(_outputFormat, bestFormat);
+
+ result = ExtAudioFileSetProperty(_afId, kExtAudioFileProperty_ClientDataFormat, sizeof(_outputFormat), &_outputFormat);
+
+ if (result != noErr) {
+ LOG(WARNING) << "Cannot set audio format file " << filename;
+ return;
+ }
+
+ }
+}
+
+AudioToolbox::~AudioToolbox() {
+ if (_afId)
+ ExtAudioFileDispose(_afId); //close the file
+}
+
+void AudioToolbox::seek(unsigned int pos) {
+ ExtAudioFileSeek(_afId, pos);
+}
+
+int AudioToolbox::read(char* buffer, unsigned int size) {
+ UInt32 read = size / _outputFormat.mBytesPerFrame;
+ OSStatus result = noErr;
+
+ SInt64 theFileLengthInFrames = 0;
+ UInt32 thePropertySize = sizeof(theFileLengthInFrames);
+ result = ExtAudioFileGetProperty(_afId, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames);
+
+ read = (theFileLengthInFrames < read ? theFileLengthInFrames : read);
+
+ AudioBufferList dataBuffer;
+ dataBuffer.mNumberBuffers = 1;
+ dataBuffer.mBuffers[0].mDataByteSize = size;
+ dataBuffer.mBuffers[0].mNumberChannels = _outputFormat.mChannelsPerFrame;
+ dataBuffer.mBuffers[0].mData = buffer;
+
+ result = ExtAudioFileRead(_afId, &read, &dataBuffer);
+ if (result != noErr) {
+ LOG(WARNING) << "Cannot read data";
+ return 0;
+ }
+
+ return read * _outputFormat.mBytesPerFrame;
+}
+
+ALenum AudioToolbox::formatToALEnum(AudioStreamBasicDescription asbd) {
+ if (asbd.mBitsPerChannel < 16) {
+ if (asbd.mChannelsPerFrame == 1) {
+ return AL_FORMAT_MONO8;
+ } else {
+ return AL_FORMAT_STEREO8;
+ }
+ } else {
+ if (asbd.mChannelsPerFrame == 1) {
+ return AL_FORMAT_MONO16;
+ } else {
+ return AL_FORMAT_STEREO16;
+ }
+ }
+}
+
+bool AudioToolbox::alEnumToFormat(AudioStreamBasicDescription& asbd, ALenum format) {
+ switch (format) {
+ case AL_FORMAT_MONO8:
+ asbd.mBitsPerChannel = 8;
+ asbd.mBytesPerFrame = 1;
+ asbd.mBytesPerPacket = 1;
+ asbd.mChannelsPerFrame = 1;
+ break;
+ case AL_FORMAT_MONO16:
+ asbd.mBitsPerChannel = 16;
+ asbd.mBytesPerFrame = 2;
+ asbd.mBytesPerPacket = 2;
+ asbd.mChannelsPerFrame = 1;
+ break;
+ case AL_FORMAT_STEREO8:
+ asbd.mBitsPerChannel = 8;
+ asbd.mBytesPerFrame = 2;
+ asbd.mBytesPerPacket = 2;
+ asbd.mChannelsPerFrame = 2;
+ break;
+ case AL_FORMAT_STEREO16:
+ asbd.mBitsPerChannel = 16;
+ asbd.mBytesPerFrame = 4;
+ asbd.mBytesPerPacket = 4;
+ asbd.mChannelsPerFrame = 2;
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+void AudioToolbox::setOutFormat(const PCMFormat& format) {
+
+ alEnumToFormat(_outputFormat, format.alFormat);
+ _outputFormat.mSampleRate = format.sampleRate;
+
+ OSStatus result = ExtAudioFileSetProperty(_afId, kExtAudioFileProperty_ClientDataFormat, sizeof(_outputFormat), &_outputFormat);
+ if (result != noErr) {
+ LOG(WARNING) << "Cannot set audio format";
+ return;
+ }
+
+}
+
+PCMFormat AudioToolbox::getInFormat() {
+ PCMFormat format;
+ format.sampleRate = _inputFormat.mSampleRate;
+ format.alFormat = formatToALEnum(_inputFormat);
+ return format;
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/audio/LibSoundFile.cpp b/src/uscxml/plugins/invoker/audio/LibSoundFile.cpp
new file mode 100644
index 0000000..1340140
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/LibSoundFile.cpp
@@ -0,0 +1,50 @@
+#include "LibSoundFile.h"
+
+namespace uscxml {
+
+LibSoundFile::LibSoundFile(const std::string filename) : PCMConverter() {
+ _filename = filename;
+ _handle = SndfileHandle(_filename, SFM_READ, SF_FORMAT_PCM_16, 1, 44100);
+ _format.sampleRate = _handle.samplerate();
+ _format.alFormat = AL_FORMAT_MONO16;
+
+}
+
+LibSoundFile::~LibSoundFile() {
+
+}
+
+void LibSoundFile::seek(unsigned int pos) {
+ _handle.seek(pos, 0);
+}
+
+int LibSoundFile::read(char* buffer, unsigned int size) {
+ return _handle.readRaw(buffer, size);
+}
+
+void LibSoundFile::setOutFormat(const PCMFormat& format) {
+ _format = format;
+ switch (_format.alFormat) {
+ case AL_FORMAT_MONO8:
+ _handle = SndfileHandle(_filename, SFM_READ, SF_FORMAT_PCM_S8, 1, _format.sampleRate);
+ break;
+ case AL_FORMAT_MONO16:
+ _handle = SndfileHandle(_filename, SFM_READ, SF_FORMAT_PCM_16, 1, _format.sampleRate);
+ break;
+ case AL_FORMAT_STEREO8:
+ _handle = SndfileHandle(_filename, SFM_READ, SF_FORMAT_PCM_S8, 2, _format.sampleRate);
+ break;
+ case AL_FORMAT_STEREO16:
+ _handle = SndfileHandle(_filename, SFM_READ, SF_FORMAT_PCM_16, 2, _format.sampleRate);
+ break;
+
+ default:
+ break;
+ }
+}
+
+PCMFormat LibSoundFile::getInFormat() {
+ return _format;
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/audio/LibSoundFile.h b/src/uscxml/plugins/invoker/audio/LibSoundFile.h
new file mode 100644
index 0000000..5491609
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/LibSoundFile.h
@@ -0,0 +1,27 @@
+#ifndef LIBSOUNDFILE_H_Q97OEKGG
+#define LIBSOUNDFILE_H_Q97OEKGG
+
+#include "PCMConverter.h"
+#include <sndfile.hh>
+
+namespace uscxml {
+
+class LibSoundFile : public PCMConverter {
+public:
+ LibSoundFile(const std::string filename);
+ virtual ~LibSoundFile();
+ void seek(unsigned int pos);
+ int read(char* buffer, unsigned int size);
+
+ virtual void setOutFormat(const PCMFormat& format);
+ virtual PCMFormat getInFormat();
+
+protected:
+ std::string _filename;
+ SndfileHandle _handle;
+ PCMFormat _format;
+};
+
+}
+
+#endif /* end of include guard: LIBSOUNDFILE_H_Q97OEKGG */
diff --git a/src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp b/src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp
new file mode 100644
index 0000000..cf9d15a
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp
@@ -0,0 +1,362 @@
+// see http://stackoverflow.com/questions/6563810/m-pi-works-with-math-h-but-not-with-cmath-in-visual-studio
+#define _USE_MATH_DEFINES
+#include <cmath>
+
+#include "OpenALInvoker.h"
+#include <uscxml/config.h>
+#include <glog/logging.h>
+#include <limits.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+namespace uscxml {
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool connect(pluma::Host& host) {
+ host.add( new OpenALInvokerProvider() );
+ return true;
+}
+#endif
+
+// see http://stackoverflow.com/questions/1904635/warning-c4003-and-errors-c2589-and-c2059-on-x-stdnumeric-limitsintmax
+#undef max
+
+OpenALInvoker::OpenALInvoker() {
+ _isStarted = false;
+ _isRunning = false;
+ _alContext = NULL;
+ _alDevice = NULL;
+ _thread = NULL;
+}
+
+OpenALInvoker::~OpenALInvoker() {
+ if (_thread) {
+ _isRunning = false;
+ _thread->join();
+ delete(_thread);
+ }
+ if (_alContext) {
+ alcCloseDevice(alcGetContextsDevice(_alContext));
+ alcDestroyContext(_alContext);
+ }
+};
+
+boost::shared_ptr<InvokerImpl> OpenALInvoker::create(InterpreterImpl* interpreter) {
+ boost::shared_ptr<OpenALInvoker> invoker = boost::shared_ptr<OpenALInvoker>(new OpenALInvoker());
+ invoker->_interpreter = interpreter;
+ return invoker;
+}
+
+Data OpenALInvoker::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void OpenALInvoker::send(const SendRequest& req) {
+ tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
+
+ if (!_isStarted)
+ start();
+
+ if (boost::iequals(req.name, "play")) {
+ if (req.params.find("src") == req.params.end()) {
+ LOG(ERROR) << "Sent event play with no src URL";
+ }
+
+ URL srcURL = req.params.find("src")->second;
+ if (!srcURL.toAbsolute(_interpreter->getBaseURI())) {
+ LOG(ERROR) << "src URL " << req.params.find("src")->second << " is relative with no base URI set for interpreter";
+ return;
+ }
+
+ _sources[req.sendid] = new OpenALSource();
+ _sources[req.sendid]->loop = req.params.find("loop") != req.params.end() && boost::iequals(req.params.find("loop")->second, "true");
+ _sources[req.sendid]->file = srcURL;
+#ifdef LIBSNDFILE_FOUND
+ _sources[req.sendid]->transform = new LibSoundFile(srcURL.asLocalFile(".audio"));
+#else
+# ifdef AUDIOTOOLBOX_FOUND
+ _sources[req.sendid]->transform = new AudioToolbox(srcURL.asLocalFile(".audio"));
+# endif
+#endif
+ if (_sources[req.sendid]->transform == NULL) {
+ LOG(ERROR) << "No transcoder for input file known - install libsndfile or AudioToolbox";
+ _sources.erase(req.sendid);
+ return;
+ }
+
+ // force mono format to ensure actual spatial audio
+ PCMFormat format = _sources[req.sendid]->transform->getInFormat();
+ format.alFormat = AL_FORMAT_MONO16;
+ _sources[req.sendid]->transform->setOutFormat(format);
+
+ try {
+ _sources[req.sendid]->player = new OpenALPlayer(_alContext, NULL, format.alFormat, format.sampleRate);
+ } catch (std::exception ex) {
+ returnErrorExecution(ex.what());
+ return;
+ }
+
+ getPosFromParams(req.params, _sources[req.sendid]->pos);
+
+ try {
+ _sources[req.sendid]->player->setPosition(_sources[req.sendid]->pos);
+ } catch (std::exception ex) {
+ returnErrorExecution(ex.what());
+ }
+
+
+ _sourcesAvailable.notify_all();
+ }
+
+ if (boost::iequals(req.name, "move.source")) {
+ std::string sourceId;
+ if (req.params.find("source") == req.params.end()) {
+ LOG(WARNING) << "Cannot move source with no source given in parameters";
+ return;
+ }
+ sourceId = req.params.find("source")->second;
+
+ if (_sources.find(sourceId) == _sources.end()) {
+ LOG(WARNING) << "Given source '" << sourceId << "' not active or not existing";
+ return;
+ }
+
+ getPosFromParams(req.params, _sources[sourceId]->pos);
+ try {
+ _sources[sourceId]->player->setPosition(_sources[sourceId]->pos);
+ } catch (std::exception ex) {
+ returnErrorExecution(ex.what());
+ }
+ }
+}
+
+void OpenALInvoker::start() {
+ _isStarted = true;
+ _thread = new tthread::thread(&OpenALInvoker::fillBuffers, this);
+}
+
+void OpenALInvoker::fillBuffers(void* userdata) {
+ OpenALInvoker* INST = (OpenALInvoker*)userdata;
+ while(INST->_isStarted) {
+ // do nothing until we have at least one source
+ int waitMs = std::numeric_limits<int>::max();
+ INST->_mutex.lock();
+ while (INST->_sources.size() == 0) {
+ INST->_sourcesAvailable.wait(INST->_mutex);
+ }
+ // here we are with at least one source and a locked mutex
+ assert(INST->_sources.size() > 0);
+
+ std::map<std::string, OpenALSource*>::iterator srcIter = INST->_sources.begin();
+ while(srcIter != INST->_sources.end()) {
+ OpenALSource* src = srcIter->second;
+ int wait = std::numeric_limits<int>::max();
+
+ if (src->finished) {
+ // source has already finished playing, feed no more samples to it
+ try {
+ wait = src->player->isPlaying();
+ if (wait == 0) {
+ // source stopped playing, delete it
+ INST->notifyOfEnd(src);
+ delete src;
+ INST->_sources.erase(srcIter++);
+ continue;
+ } else {
+ // source returned time when to repoll
+ assert(wait > 0);
+ }
+ } catch (std::exception ex) {
+ INST->returnErrorExecution(ex.what());
+ delete src;
+ INST->_sources.erase(srcIter++);
+ continue;
+ }
+ } else {
+ // source still needs more samples or play existing buffer
+ if (src->written == src->read) {
+ // all read samples have been written, read some more
+ src->written = 0;
+ src->read = src->transform->read(src->buffer, ALPLAY_AUDIO_BUFFER_SIZE);
+ if (src->read < ALPLAY_AUDIO_BUFFER_SIZE) {
+ if (src->loop) {
+ INST->notifyOfLoop(src);
+ while (src->read < ALPLAY_AUDIO_BUFFER_SIZE) {
+ src->transform->seek(0);
+ src->read += src->transform->read(src->buffer + src->read, ALPLAY_AUDIO_BUFFER_SIZE - src->read);
+ }
+ } else {
+ src->finished = true;
+ memset(src->buffer + src->read, 0, ALPLAY_AUDIO_BUFFER_SIZE - src->read);
+ }
+ }
+ }
+
+ // there are unwritten samples in the buffer
+ if (src->read != src->written) {
+ try {
+ int written = src->player->write(src->buffer, ALPLAY_AUDIO_BUFFER_SIZE, &wait);
+ if (written >=0 ) {
+ src->written += written;
+ }
+ } catch (std::exception ex) {
+ INST->returnErrorExecution(ex.what());
+ src->finished = true;
+ }
+ } else {
+ assert(src->finished);
+ }
+ }
+
+ waitMs = (wait < waitMs ? wait : waitMs);
+ srcIter++;
+ }
+
+// std::cout << "W" << waitMs << ".";
+
+ INST->_mutex.unlock();
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(waitMs));
+ }
+}
+
+void OpenALInvoker::cancel(const std::string sendId) {
+ tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
+
+}
+
+void OpenALInvoker::invoke(const InvokeRequest& req) {
+ _alDevice = alcOpenDevice(NULL);
+ if (_alDevice == NULL) {
+ throw std::string("__FILE__ __LINE__ openal error opening device");
+ }
+
+ // create new context with device
+ _alContext = alcCreateContext (_alDevice, NULL);
+ if (_alContext == NULL) {
+ alcCloseDevice (_alDevice);
+ throw std::string("openal error create context");
+ }
+
+// alcMakeContextCurrent(_alContext);
+// float listener[3] = {0,0,0};
+// alListenerfv(AL_POSITION, listener);
+//
+// float orientation[6] = {
+// 0.0, 0.0, -1.0, // direction
+// 0.0, 1.0, 0.0 }; //up
+// alListenerfv(AL_ORIENTATION, orientation);
+//
+// float velocity[3] = { 0.0, 0.0, 0.0}; //up
+// alListenerfv(AL_VELOCITY, velocity);
+//
+// alListenerf(AL_GAIN, 0.5);
+
+ start();
+}
+
+void OpenALInvoker::notifyOfEnd(OpenALSource* src) {
+ Event ev;
+ ev.name = "audio.end";
+ ev.data.compound["file"] = src->file;
+ returnEvent(ev);
+}
+
+void OpenALInvoker::notifyOfLoop(OpenALSource* src) {
+ Event ev;
+ ev.name = "audio.loop";
+ ev.data.compound["file"] = src->file;
+ returnEvent(ev);
+}
+
+void OpenALInvoker::getPosFromParams(const std::multimap<std::string, std::string>& params, float* position) {
+ // vector explicitly given
+ try {
+ if (params.find("x") != params.end())
+ position[0] = boost::lexical_cast<float>(params.find("x")->second);
+ if (params.find("y") != params.end())
+ position[1] = boost::lexical_cast<float>(params.find("y")->second);
+ if (params.find("z") != params.end())
+ position[2] = boost::lexical_cast<float>(params.find("z")->second);
+ } catch (boost::bad_lexical_cast& e) {
+ LOG(ERROR) << "Cannot interpret x, y or z as float value in params: " << e.what();
+ }
+
+ try {
+ // right is an alias for x
+ if (params.find("right") != params.end())
+ position[0] = boost::lexical_cast<float>(params.find("right")->second);
+ // height is an alias for y
+ if (params.find("height") != params.end())
+ position[1] = boost::lexical_cast<float>(params.find("height")->second);
+ // front is an alias for z
+ if (params.find("front") != params.end())
+ position[2] = boost::lexical_cast<float>(params.find("front")->second);
+ } catch (boost::bad_lexical_cast& e) {
+ LOG(ERROR) << "Cannot interpret right, height or front as float value in params: " << e.what();
+ }
+
+ // do we have a position on a circle?
+ try {
+ if (params.find("circle") != params.end()) {
+ float rad = posToRadian(params.find("circle")->second);
+ position[0] = cosf(rad);
+ position[2] = -1 * sinf(rad); // z axis increases to front
+ position[0] *= 150;
+ position[2] *= 150;
+
+ }
+ } catch (boost::bad_lexical_cast& e) {
+ LOG(ERROR) << "Cannot interpret circle as float value in params: " << e.what();
+ }
+
+// position[0] = position[0] / _maxPos[0];
+// position[1] = position[1] / _maxPos[1];
+// position[2] = position[2] / _maxPos[2];
+ // std::cout << _pos[0] << ":" << _pos[1] << ":" << _pos[2] << std::endl;
+
+}
+
+float OpenALInvoker::posToRadian(const std::string& pos) {
+
+ std::string trimmedPos = boost::trim_copy(pos);
+ float rad = 0;
+
+ if (trimmedPos.size() > 3 && boost::iequals("deg", trimmedPos.substr(trimmedPos.length() - 3, 3))) {
+ rad = boost::lexical_cast<float>(trimmedPos.substr(0, trimmedPos.size() - 3));
+ rad = fmodf(rad, 360); // into range [0-360]
+ rad /= 180; // into range [0-2]
+ rad *= M_PI; // into range [0-2PI]
+ rad -= M_PI_2; // 0 to top;
+ rad *= -1; // make clockwise
+ rad += 2 * M_PI; // make positive
+ } else if (trimmedPos.size() > 3 && boost::iequals("rad", trimmedPos.substr(trimmedPos.length() - 3, 3))) {
+ rad = boost::lexical_cast<float>(trimmedPos.substr(0, trimmedPos.size() - 3));
+ rad = fmodf(rad, M_PI * 2); // into range [0-2*PI]
+ } else {
+ LOG(ERROR) << "Cannot make sense of position value " << trimmedPos << ": does not end in 'deg', 'rad'";
+ }
+ return rad;
+}
+
+OpenALSource::OpenALSource() {
+ pos[0] = pos[1] = pos[2] = 0;
+ player = NULL;
+ loop = false;
+ finished = false;
+ transform = NULL;
+ read = written = 0;
+ memset(buffer, 0, ALPLAY_AUDIO_BUFFER_SIZE);
+}
+
+OpenALSource::~OpenALSource() {
+ if (player)
+ delete player;
+ if (transform)
+ delete transform;
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/audio/OpenALInvoker.h b/src/uscxml/plugins/invoker/audio/OpenALInvoker.h
new file mode 100644
index 0000000..c0ed842
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/OpenALInvoker.h
@@ -0,0 +1,88 @@
+#ifndef OPENALINVOKER_H_W09J90F0
+#define OPENALINVOKER_H_W09J90F0
+
+#include "uscxml/config.h"
+#include <uscxml/Interpreter.h>
+#include "OpenALPlayer.h"
+
+#include "PCMConverter.h"
+#ifdef LIBSNDFILE_FOUND
+# include "LibSoundFile.h"
+#else
+# ifdef AUDIOTOOLBOX_FOUND
+# include "AudioToolbox.h"
+# endif
+#endif
+
+#ifdef BUILD_AS_PLUGINS
+#include "uscxml/plugins/Plugins.h"
+#endif
+
+namespace uscxml {
+
+class OpenALSource {
+public:
+ OpenALSource();
+ ~OpenALSource();
+
+ OpenALPlayer* player;
+ char buffer[ALPLAY_AUDIO_BUFFER_SIZE];
+ bool loop;
+ bool finished;
+ int read;
+ int written;
+ float pos[3];
+ URL file;
+ PCMConverter* transform;
+};
+
+class OpenALInvoker : public InvokerImpl {
+public:
+ OpenALInvoker();
+ virtual ~OpenALInvoker();
+ virtual boost::shared_ptr<InvokerImpl> create(InterpreterImpl* interpreter);
+
+ virtual std::set<std::string> getNames() {
+ std::set<std::string> names;
+ names.insert("openal");
+ names.insert("spatial-audio");
+ names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#openal");
+ return names;
+ }
+
+ virtual Data getDataModelVariables();
+ virtual void send(const SendRequest& req);
+ virtual void cancel(const std::string sendId);
+ virtual void invoke(const InvokeRequest& req);
+
+protected:
+ std::map<std::string, OpenALSource*> _sources;
+ ALCcontext* _alContext;
+ ALCdevice* _alDevice;
+
+ tthread::recursive_mutex _mutex;
+ tthread::thread* _thread;
+ tthread::condition_variable _sourcesAvailable;
+
+ bool _isStarted;
+ bool _isRunning;
+
+ static void fillBuffers(void* userdata);
+ void start();
+
+ void notifyOfEnd(OpenALSource*);
+ void notifyOfLoop(OpenALSource*);
+
+ float posToRadian(const std::string& pos);
+ void getPosFromParams(const std::multimap<std::string, std::string>& params, float* position);
+
+};
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_INHERIT_PROVIDER(OpenALInvoker, InvokerImpl);
+#endif
+
+}
+
+
+#endif /* end of include guard: OPENALINVOKER_H_W09J90F0 */
diff --git a/src/uscxml/plugins/invoker/audio/OpenALPlayer.cpp b/src/uscxml/plugins/invoker/audio/OpenALPlayer.cpp
new file mode 100644
index 0000000..4266f79
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/OpenALPlayer.cpp
@@ -0,0 +1,523 @@
+#include "OpenALPlayer.h"
+#include <assert.h>
+#include <stdexcept>
+
+tthread::recursive_mutex OpenALPlayer::_alMutex;
+
+/**
+* Create a new OpenAL stream source
+*/
+OpenALPlayer::OpenALPlayer(ALCcontext* context, OpenALPlayerCallback* audioCallback, ALenum format, ALsizei freq) {
+
+ _isInitialized = false;
+ _audioCallback = audioCallback;
+ _freq = freq;
+ _format = format;
+ _bufferSize = 0;
+ _nrBuffers = 0;
+ _thread = NULL;
+ _alId = 0;
+
+ _position[0] = _position[1] = _position[2] = 0;
+ _velocity[0] = _velocity[1] = _velocity[2] = 0;
+ _direction[0] = _direction[1] = _direction[2] = 0;
+
+ tthread::lock_guard<tthread::recursive_mutex> lock(_alMutex);
+ if (context == NULL) {
+ // this is in essence alutInit() from freealut.
+
+ // use the current context if there is one
+ _context = alcGetCurrentContext();
+
+ if (_context == NULL) {
+// std::cout << "\tnew context" << std::endl;
+ // create a new context if none was given and no is current
+
+ // get default device
+ ALCdevice* device = alcOpenDevice(NULL);
+ if (device == NULL) {
+ throw std::runtime_error("__FILE__ __LINE__ openal error opening device");
+ }
+
+ // create new context with device
+ _context = alcCreateContext (device, NULL);
+ if (_context == NULL) {
+ alcCloseDevice (device);
+ throw std::runtime_error("openal error create context");
+ }
+
+ // make context current
+ if (!alcMakeContextCurrent (_context)) {
+ alcDestroyContext (_context);
+ alcCloseDevice (device);
+ throw std::runtime_error("openal error make context current");
+ }
+ } else {
+// std::cout << "\texisting context" << std::endl;
+ }
+ } else {
+// std::cout << "\tgiven context" << std::endl;
+ _context = context;
+ }
+}
+
+OpenALPlayer::~OpenALPlayer() {
+ tthread::lock_guard<tthread::recursive_mutex> lock(_alMutex);
+ if (isPlaying()) {
+ alSourceStop(_alId);
+ }
+ if (_thread) {
+ stop();
+ _thread->join();
+ delete(_thread);
+ }
+
+ if (_isInitialized) {
+ alDeleteSources(1, &_alId);
+ if (alIsSource(_alId)) {
+ throw std::runtime_error("openal source id still valid");
+ }
+ for (int i = 0; i < _nrBuffers; i++) {
+ assert(alIsBuffer(_bufferIds[i]));
+ free(_buffers[i]);
+ }
+ alDeleteBuffers(_nrBuffers, _bufferIds);
+ for (int i = 0; i < _nrBuffers; i++) {
+ assert(!alIsBuffer(_bufferIds[i]));
+ }
+ free(_buffers);
+ free(_bufferIds);
+ }
+ // clear errors and begone
+ alGetError();
+
+}
+
+/**
+* Allocate; data and set defaults
+*/
+void OpenALPlayer::init() {
+ _userData = NULL;
+ _pitch = 0;
+ _gain = 0;
+ _referenceDistance = 1.0;
+ _isLooping = false;
+
+ // no one set a buffer size yet
+ if (_bufferSize <= 0)
+ _bufferSize = ALPLAY_AUDIO_BUFFER_SIZE;
+
+ // no one set the number of buffers yet
+ if (_nrBuffers <= 0)
+ _nrBuffers = ALPLAY_NR_AUDIO_BUFFERS;
+
+ _isInitialized = true;
+
+ _buffers = (char**)malloc(_nrBuffers * sizeof(char*));
+ _bufferIds = (ALuint*)malloc(_nrBuffers * sizeof(ALuint));
+ for (int i = 0; i < _nrBuffers; i++) {
+ _buffers[i] = 0; //(char*)malloc(_bufferSize);
+ }
+
+ // there are other formats as well and this will have to be extended
+ int bytesPerSample = 2;
+ switch(_format) {
+ case AL_FORMAT_MONO8:
+ case AL_FORMAT_STEREO8:
+ bytesPerSample = 1;
+ break;
+
+ case AL_FORMAT_MONO16:
+ case AL_FORMAT_STEREO16:
+ bytesPerSample = 2;
+ break;
+ }
+
+ // how many ms audio is in one buffer?
+ _msForBuffer = (int)(((float)_bufferSize / (float)bytesPerSample) / ((float)_freq / 1000.0f));
+ _initialSleep = (_msForBuffer - (int)(0.6 * _msForBuffer)) * _nrBuffers;
+ _bufferSleep = _msForBuffer - (int)(0.4 * _msForBuffer);
+ _repollSleep = _msForBuffer - (int)(0.7 * _msForBuffer);
+
+// std::cout << _msForBuffer << "ms in one buffer" << std::endl;
+
+ // get available buffer ids
+ tthread::lock_guard<tthread::recursive_mutex> lock(_alMutex);
+
+ if (!alcMakeContextCurrent (_context)) {
+ throw std::runtime_error("openal error make context current");
+ }
+
+ alGenBuffers(_nrBuffers, _bufferIds);
+ checkOpenALError(__LINE__);
+
+ // get new source id from openAL
+ alGenSources(1, &_alId);
+
+ checkOpenALError(__LINE__);
+ if (!alIsSource(_alId)) {
+ throw std::runtime_error("openal source id not valid");
+ }
+
+ // set our position and various flags to meaningful defaults
+ alSourcei(_alId, AL_LOOPING, AL_FALSE);
+ checkOpenALError(__LINE__);
+ alSourcefv(_alId, AL_POSITION, _position);
+ checkOpenALError(__LINE__);
+ alSourcef(_alId,AL_REFERENCE_DISTANCE, 5.0f);
+ checkOpenALError(__LINE__);
+// alDistanceModel(AL_LINEAR_DISTANCE);
+// checkOpenALError(__LINE__);
+// alSourcefv(_alId, AL_VELOCITY, _velocity);
+// checkOpenALError(__LINE__);
+// alSourcefv(_alId, AL_DIRECTION, _direction);
+// checkOpenALError(__LINE__);
+// alSourcef (_alId, AL_ROLLOFF_FACTOR, 1.0);
+// checkOpenALError(__LINE__);
+ alSourcef(_alId,AL_REFERENCE_DISTANCE, 5.0f);
+ checkOpenALError(__LINE__);
+// float listener[] = { 0.0, 0.0, 0.0 };
+// alListenerfv(AL_POSITION, listener);
+// checkOpenALError(__LINE__);
+
+ alSourcei (_alId, AL_SOURCE_RELATIVE, AL_TRUE);
+ checkOpenALError(__LINE__);
+}
+
+/**
+* Start the sound source.
+*
+* This will trigger continuous calls top the audio callback.
+*/
+void OpenALPlayer::start() {
+ if (!_isInitialized)
+ init();
+
+ if (_audioCallback == NULL)
+ throw std::runtime_error("cannot start without an audio callback");
+
+ _isStarted = true;
+
+ // prime the buffers with some initial data and register for buffer ids
+ tthread::lock_guard<tthread::recursive_mutex> lock(_alMutex);
+ for (ALuint i = 0; i < (unsigned int)_nrBuffers; i++) {
+ _buffers[i] = (char*)malloc(_bufferSize);
+ _audioCallback->getSamples(_buffers[i], _bufferSize, this);
+ alBufferData(_bufferIds[i], _format, _buffers[i], _bufferSize, _freq);
+ checkOpenALError(__LINE__);
+ }
+ // enqueue all buffers
+ alSourceQueueBuffers(_alId, _nrBuffers, _bufferIds);
+ checkOpenALError(__LINE__);
+
+ // start thread
+ if (_audioCallback != NULL) {
+ _thread = new tthread::thread(&OpenALPlayer::updateBuffersWrapper, this);
+ }
+
+ // tell openAL to start rendering the buffers
+ alSourcePlay(_alId);
+ checkOpenALError(__LINE__);
+}
+
+// find bufferId in _bufferIds to get bufferIndex into _buffers - messy
+int OpenALPlayer::bufferIndex(int bufferId) {
+ int bufferIndex = 0;
+ for (; bufferIndex < _nrBuffers; bufferIndex++) {
+ if (_bufferIds[bufferIndex] == (unsigned int)bufferId)
+ break;
+ }
+ if (bufferIndex >= _nrBuffers)
+ throw std::runtime_error("could not find dequeued bufferId in ids");
+ return bufferIndex;
+}
+
+/**
+* Write a buffer (blocking).
+*
+* This allows for a pushing model, whereas the callback allows for a polling model.
+*/
+int OpenALPlayer::write(char* buffer, int size, int* repollAt, bool blocking) {
+ tthread::lock_guard<tthread::recursive_mutex> lock(_alMutex);
+
+ if (!_isInitialized)
+ init();
+
+ if (_audioCallback != NULL) {
+ throw std::runtime_error("you cannot use the write interface with an audio callback");
+ }
+
+ if (size != _bufferSize) {
+ throw std::runtime_error("buffersize does not match");
+ }
+
+ if (!alcMakeContextCurrent (_context)) {
+ throw std::runtime_error("openal error make context current");
+ }
+
+ // try to enqueue the given buffer data
+ for (;;) {
+ // do we have an empty buffer in the OpenAL queue?
+ int processed;
+ alGetSourcei(_alId, AL_BUFFERS_PROCESSED, &processed);
+ checkOpenALError(__LINE__);
+
+// if (!isPlaying())
+// std::cout << "-";
+
+ if (processed > 0) {
+// std::cout << "P" << processed;
+ ALuint bufferId = 0;
+ alSourceUnqueueBuffers(_alId, 1, &bufferId);
+ checkOpenALError(__LINE__);
+
+ int bufferIdx = bufferIndex(bufferId);
+
+ // fill the buffer with the given data
+ memcpy(_buffers[bufferIdx], buffer, _bufferSize);
+ alBufferData(bufferId, _format, _buffers[bufferIdx], _bufferSize, _freq);
+ checkOpenALError(__LINE__);
+
+ // enqueue
+ alSourceQueueBuffers(_alId, 1, &bufferId);
+ checkOpenALError(__LINE__);
+
+ // some buffers were processed
+ if (repollAt)
+ *repollAt = _repollSleep;
+ break;
+
+ } else {
+ // no buffer processed - is there an uninitialized buffer left?
+ int nextBuffer = 0;
+ for(; nextBuffer < _nrBuffers; nextBuffer++) {
+ if (_buffers[nextBuffer] == 0) {
+ break;
+ }
+ }
+ if (nextBuffer < _nrBuffers) {
+// std::cout << "N";
+ _buffers[nextBuffer] = (char*)malloc(_bufferSize);
+ memcpy(_buffers[nextBuffer], buffer, _bufferSize);
+
+ alBufferData(_bufferIds[nextBuffer], _format, _buffers[nextBuffer], _bufferSize, _freq);
+ checkOpenALError(__LINE__);
+
+ alSourceQueueBuffers(_alId, 1, &_bufferIds[nextBuffer]);
+ checkOpenALError(__LINE__);
+ // there was a free buffer, repoll immediately to try to write more
+ if (repollAt)
+ *repollAt = 0;
+
+ break;
+ } else {
+// std::cout << "X";
+ // no processed, no new buffer, wait until we processed one
+ if (blocking) {
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(_repollSleep));
+ } else {
+ if (repollAt)
+ *repollAt = _repollSleep;
+ return -1;
+ }
+ }
+ }
+ }
+
+ // we have at least one buffer queued, start playing
+ if (!_isStarted || !isPlaying()) {
+ alSourcePlay(_alId);
+ checkOpenALError(__LINE__);
+ _isStarted = true;
+ }
+
+ return size;
+}
+
+
+/**
+* Dequeue, refill and re-enqueue buffers.
+*/
+void OpenALPlayer::updateBuffers() {
+ int processed;
+// int queued;
+
+// std::cout << "Initial sleep: " << initialSleep << "ms" << std::endl;
+// std::cout << "Buffer sleep: " << bufferSleep << "ms" << std::endl;
+// std::cout << "Repoll sleep: " << repollSleep << "ms" << std::endl;
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(_bufferSleep * _initialSleep));
+
+
+ while(_isStarted) {
+
+ // how many buffers have been rendered already?
+ tthread::lock_guard<tthread::recursive_mutex> lock(_alMutex);
+ alGetSourcei(_alId, AL_BUFFERS_PROCESSED, &processed);
+ checkOpenALError(__LINE__);
+ //std::cout << processed << std::flush;
+
+ if (processed == 0) {
+ // avoid busy wait by sleeping
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(_bufferSleep * _initialSleep));
+ } else {
+ // dequeue buffers and get ids
+ // see http://stackoverflow.com/questions/1900665/c-compiler-differences-vs2008-and-g
+ ALuint bufferIds[ALPLAY_NR_AUDIO_BUFFERS];
+ alSourceUnqueueBuffers(_alId, processed, bufferIds);
+ checkOpenALError(__LINE__);
+
+ for (int id = 0; id < processed; id++) {
+ int bufferIdx = bufferIndex(bufferIds[id]);
+
+ // refill the buffer with data from the callback
+ _audioCallback->getSamples(_buffers[bufferIdx], _bufferSize, this);
+ alBufferData(bufferIds[id], _format, _buffers[bufferIdx], _bufferSize, _freq);
+ checkOpenALError(__LINE__);
+
+ }
+ // re-enqueue
+ alSourceQueueBuffers(_alId, processed, bufferIds);
+ checkOpenALError(__LINE__);
+
+ // restart if we are not running anymore
+ if (!isPlaying()) {
+ alSourcePlay(_alId);
+ checkOpenALError(__LINE__);
+ }
+
+ // sleep a bit less than the duration of one buffer
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(_bufferSleep * processed));
+ }
+ }
+}
+
+/**
+* TODO
+*/
+void OpenALPlayer::stop() {
+ _isStarted = false;
+ _thread->join();
+}
+
+void OpenALPlayer::checkOpenALError(int line) {
+ int error = alGetError();
+ if(error != AL_NO_ERROR) {
+ std::stringstream out;
+ out << "OpenALError:" << line << ":";
+
+ switch (error) {
+ case AL_INVALID_NAME:
+ out << "OpenAL invalid name.";
+ break;
+ case AL_INVALID_ENUM:
+ out << "OpenAL invalid enum.";
+ break;
+ case AL_INVALID_VALUE:
+ out << "OpenAL invalid value.";
+ break;
+ case AL_INVALID_OPERATION:
+ out << "OpenAL invalid operation.";
+ break;
+ case AL_OUT_OF_MEMORY:
+ out << "OpenAL out of memory.";
+ break;
+
+ default:
+ out << "OpenAL unknown error.";
+ break;
+ }
+ throw std::runtime_error(out.str());
+ }
+}
+
+unsigned int OpenALPlayer::isPlaying() {
+ ALint val;
+ alGetSourcei(_alId, AL_SOURCE_STATE, &val);
+ if(val != AL_PLAYING)
+ return 0;
+ return _repollSleep;
+}
+
+void OpenALPlayer::updateBuffersWrapper(void *obj) {
+ try {
+ reinterpret_cast<OpenALPlayer *>(obj)->updateBuffers();
+ } catch(std::runtime_error& error) {
+// std::cout << "Terminating Thread: " << error << std::endl;
+ } catch(...) {
+// std::cout << "Terminating Thread! " << std::endl;
+ }
+}
+
+void OpenALPlayer::setNrBuffers(int nrBuffers) {
+ if (_nrBuffers > 0)
+ throw std::runtime_error("cannot modify number of buffers");
+ _nrBuffers = nrBuffers;
+}
+
+int OpenALPlayer::getNrBuffers() {
+ return _nrBuffers;
+}
+
+/**
+* Set position of sound source in coordinate system
+*/
+void OpenALPlayer::setPosition(ALfloat position[]) {
+ memcpy(&_position, position, 3 * sizeof(ALfloat));
+// std::cout << _position[0] << ", " << _position[1] << ", " << _position[2] << std::endl;
+ if (_isInitialized)
+ alSourcefv(_alId, AL_POSITION, _position);
+}
+
+ALfloat* OpenALPlayer::getPosition() {
+ return _position;
+}
+
+/**
+* Set velocity of sound source in coordinate system
+*/
+void OpenALPlayer::setVelocity(ALfloat velocity[]) {
+ memcpy(&_velocity, velocity, 3 * sizeof(ALfloat));
+ if (_isInitialized)
+ alSourcefv(_alId, AL_VELOCITY, _velocity);
+}
+
+ALfloat* OpenALPlayer::getVelocity() {
+ return _velocity;
+}
+
+/**
+* Set direction of sound source in coordinate system
+*/
+void OpenALPlayer::setDirection(ALfloat direction[]) {
+ memcpy(&_direction, direction, 3 * sizeof(ALfloat));
+ if (_isInitialized)
+ alSourcefv(_alId, AL_DIRECTION, _direction);
+}
+
+ALfloat* OpenALPlayer::getDirection() {
+ return _direction;
+}
+
+void OpenALPlayer::setBufferSize(int bufferSize) {
+ if (_bufferSize > 0)
+ throw std::runtime_error("cannot modify buffersize");
+ _bufferSize = bufferSize;
+}
+
+int OpenALPlayer::getBufferSize() {
+ return _bufferSize;
+}
+
+OpenALPlayerCallback* OpenALPlayer::getCallback() {
+ return _audioCallback;
+}
+void OpenALPlayer::setCallback(OpenALPlayerCallback* callback) {
+ _audioCallback = callback;
+}
+
+void* OpenALPlayer::getUserData() {
+ return _userData;
+}
+void OpenALPlayer::setUserData(void* userData) {
+ _userData = userData;
+}
diff --git a/src/uscxml/plugins/invoker/audio/OpenALPlayer.h b/src/uscxml/plugins/invoker/audio/OpenALPlayer.h
new file mode 100644
index 0000000..4d7d189
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/OpenALPlayer.h
@@ -0,0 +1,106 @@
+#ifndef OPENALPLAYER_H_3PORVJDU
+#define OPENALPLAYER_H_3PORVJDU
+
+#include <iostream>
+#include <string.h>
+#include <stdlib.h>
+#include <sstream>
+#include <uscxml/concurrency/tinythread.h>
+
+#include <al.h>
+#include <alc.h>
+
+// sometimes the stream drops if this is less than 5000 bytes
+#define ALPLAY_NR_AUDIO_BUFFERS 3
+#define ALPLAY_AUDIO_BUFFER_SIZE 2048
+//#define ALPLAYER_FORMAT_MONO16 0x1101
+
+class OpenALPlayer;
+
+class OpenALPlayerCallback {
+public:
+ virtual ~OpenALPlayerCallback() {}
+ /*
+ * Returning an OpenALPlayerCallback is a hack to be able to provide a typemap for SWIG.
+ * We cannot use SWIG directors with byte arrays otherwise. Posted to swig-ML already.
+ */
+ virtual OpenALPlayerCallback* getSamples(char* buffer, int size, OpenALPlayer* player) = 0;
+};
+
+class OpenALPlayer {
+private:
+ ALCcontext* _context;
+
+ ALuint _alId;
+ ALfloat _position[3];
+ ALfloat _velocity[3];
+ ALfloat _direction[3];
+ ALfloat _pitch;
+ ALfloat _gain;
+ ALfloat _referenceDistance;
+ ALboolean _isLooping;
+
+ // OpenAL is not really thread safe
+ static tthread::recursive_mutex _alMutex;
+
+ ALenum _format;
+ ALsizei _freq;
+ int _msForBuffer;
+ int _initialSleep;
+ int _bufferSleep;
+ int _repollSleep;
+
+ int _bufferSize;
+ int _nrBuffers;
+ ALuint* _bufferIds;
+ char** _buffers;
+ OpenALPlayerCallback* _audioCallback;
+ void* _userData;
+
+ tthread::thread* _thread;
+ bool _isStarted;
+ bool _isInitialized;
+
+ void updateBuffers();
+ void init();
+ void checkOpenALError(int line);
+
+ // static wrapper as an entry point for pthreads
+ static void updateBuffersWrapper(void *obj);
+
+ // get the index in _buffers for a buffer ID
+ int inline bufferIndex(int bufferId);
+
+public:
+ OpenALPlayer(ALCcontext*, OpenALPlayerCallback*, ALenum, ALsizei);
+ virtual ~OpenALPlayer();
+
+ unsigned int isPlaying();
+
+ ALfloat* getPosition();
+ void setPosition(ALfloat[3]);
+ ALfloat* getVelocity();
+ void setVelocity(ALfloat[3]);
+ ALfloat* getDirection();
+ void setDirection(ALfloat[3]);
+
+ void setBufferSize(int bufferSize);
+ int getBufferSize();
+ void setNrBuffers(int nrBuffers);
+ int getNrBuffers();
+
+ // callback interface for pull
+ OpenALPlayerCallback* getCallback();
+ void setCallback(OpenALPlayerCallback* callback);
+
+ // stream interface for push
+ int write(char* buffer, int size, int* repollAt, bool blocking = false);
+
+ void* getUserData();
+ void setUserData(void* userData);
+
+ void start();
+ void stop();
+};
+
+#endif /* end of include guard: OPENALPLAYER_H_3PORVJDU */
diff --git a/src/uscxml/plugins/invoker/audio/PCMConverter.h b/src/uscxml/plugins/invoker/audio/PCMConverter.h
new file mode 100644
index 0000000..6036af2
--- /dev/null
+++ b/src/uscxml/plugins/invoker/audio/PCMConverter.h
@@ -0,0 +1,30 @@
+#ifndef PCMCONVERTER_H_97Z8U7PA
+#define PCMCONVERTER_H_97Z8U7PA
+
+#include <string>
+#include <al.h>
+#include <alc.h>
+
+namespace uscxml {
+
+struct PCMFormat {
+ ALenum alFormat;
+ unsigned int sampleRate;
+};
+
+class PCMConverter {
+public:
+ PCMConverter(const std::string filename) {}
+ virtual ~PCMConverter() {}
+ virtual void seek(unsigned int pos) = 0;
+ virtual int read(char* buffer, unsigned int size) = 0;
+
+ virtual void setOutFormat(const PCMFormat& format) = 0;
+ virtual PCMFormat getInFormat() = 0;
+protected:
+ PCMConverter() {}
+};
+
+}
+
+#endif /* end of include guard: PCMCONVERTER_H_97Z8U7PA */
diff --git a/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp b/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
index 82e0a48..f53eb67 100644
--- a/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
+++ b/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
@@ -186,7 +186,7 @@ void XHTMLInvoker::cancel(const std::string sendId) {
void XHTMLInvoker::invoke(const InvokeRequest& req) {
_invokeReq = req;
- HTTPServer::registerServlet(_interpreter->getName() + "/xhtml/" + req.invokeid + ".html", this);
+ HTTPServer::registerServlet(_interpreter->getName() + "/" + req.invokeid + ".html", this);
#if __APPLE__
# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
# else
diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp
index 7e1a858..f4b1a57 100644
--- a/src/uscxml/server/HTTPServer.cpp
+++ b/src/uscxml/server/HTTPServer.cpp
@@ -346,7 +346,7 @@ bool HTTPServer::registerServlet(const std::string& path, HTTPServlet* servlet)
INSTANCE->_servlets[suffixedPath] = servlet;
-// LOG(INFO) << "HTTP Servlet listening at: " << servletURL.str() << std::endl;
+ LOG(INFO) << "HTTP Servlet listening at: " << servletURL.str() << std::endl;
// register callback
evhttp_set_cb(INSTANCE->_http, ("/" + suffixedPath).c_str(), HTTPServer::httpRecvReqCallback, servlet);
diff --git a/test/samples/uscxml/test-java-invoker.scxml b/test/samples/uscxml/test-java-invoker.scxml
new file mode 100644
index 0000000..97d66c8
--- /dev/null
+++ b/test/samples/uscxml/test-java-invoker.scxml
@@ -0,0 +1,39 @@
+<scxml datamodel="ecmascript">
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
+ <state id="start">
+ <state id="invokeNull">
+ <invoke type="java">
+ <finalize>
+ <script>dump(_event);</script>
+ </finalize>
+ <content>
+ <foo></foo>
+ </content>
+
+ </invoke>
+ <transition event="*" target="invokeJSON" />
+ </state>
+ <state id="invokeJSON">
+ <invoke type="java">
+ <content>
+ <foo></foo>
+ </content>
+ <finalize>
+ <script>dump(_event);</script>
+ </finalize>
+ </invoke>
+ <transition event="*" target="invokeDOM" />
+ </state>
+ <state id="invokeDOM">
+ <invoke type="java">
+ <content>
+ <foo></foo>
+ </content>
+ <finalize>
+ <script>dump(_event);</script>
+ </finalize>
+ </invoke>
+ <transition event="*" target="invokeNull" />
+ </state>
+ </state>
+</scxml> \ No newline at end of file
diff --git a/test/samples/uscxml/test-openal.scxml b/test/samples/uscxml/test-openal.scxml
new file mode 100644
index 0000000..9a1dec0
--- /dev/null
+++ b/test/samples/uscxml/test-openal.scxml
@@ -0,0 +1,55 @@
+<scxml datamodel="ecmascript" name="spatial.audio.test">
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
+ <script>
+ var bubblesDeg = 0;
+ var electricDeg = 90;
+ </script>
+ <state id="click.continuously">
+ <invoke type="openal" id="audio.id1" />
+ <state id="ready">
+ <onentry>
+ <send event="spatial-audio.play" delay="1ms" />
+ <log expr="'Ready'" />
+ </onentry>
+ <transition target="play" event="spatial-audio.play" />
+ </state>
+ <parallel id="play">
+ <state id="bubbles">
+ <onentry>
+ <send target="#_audio.id1" event="play" id="bubbles">
+ <param name="src" expr="'http://www.talkingwav.com/various/applause.wav'" />
+ </send>
+ </onentry>
+ </state>
+ <state id="electric">
+ <onentry>
+ <send target="#_audio.id1" event="play" id="electric">
+ <param name="src" expr="'http://www.talkingwav.com/various/aaaaagh.wav'" />
+ </send>
+ </onentry>
+ </state>
+ <state id="rotate">
+ <state id="rotateinner">
+ <onentry>
+ <send event="rotate" delay="1000ms" />
+ <send target="#_audio.id1" event="move.source">
+ <param name="source" expr="'bubbles'" />
+ <param name="circle" expr="bubblesDeg + 'deg'" />
+ </send>
+ <send target="#_audio.id1" event="move.source">
+ <param name="source" expr="'electric'" />
+ <param name="circle" expr="electricDeg + 'deg'" />
+ </send>
+ <script>
+ bubblesDeg += 10;
+ electricDeg += 20;
+ </script>
+ </onentry>
+ <transition event="rotate" type="internal" target="rotateinner" />
+ </state>
+ </state>
+ </parallel>
+ </state>
+
+ <final id="final" />
+</scxml> \ No newline at end of file
diff --git a/test/samples/uscxml/test-predicates.scxml b/test/samples/uscxml/test-predicates.scxml
index 98848a2..88e05cc 100644
--- a/test/samples/uscxml/test-predicates.scxml
+++ b/test/samples/uscxml/test-predicates.scxml
@@ -1,8 +1,8 @@
<scxml>
<state id="atomic" />
<state id="compound">
- <state id="compundChild1" />
- <state id="compundChild2" />
+ <state id="compoundChild1" />
+ <state id="compoundChild2" />
</state>
<parallel id="parallel">
</parallel>
diff --git a/test/src/test-predicates.cpp b/test/src/test-predicates.cpp
index ff61f92..336a56b 100644
--- a/test/src/test-predicates.cpp
+++ b/test/src/test-predicates.cpp
@@ -34,14 +34,14 @@ int main(int argc, char** argv) {
assert(initialState[0] == atomicState);
NodeSet<std::string> childs = interpreter.getChildStates(compoundState);
- Node<std::string> compundChild1 = interpreter.getState("compundChild1");
- Node<std::string> compundChild2 = interpreter.getState("compundChild2");
+ Node<std::string> compoundChild1 = interpreter.getState("compoundChild1");
+ Node<std::string> compoundChild2 = interpreter.getState("compoundChild2");
assert(childs.size() > 0);
- assert(Interpreter::isMember(compundChild1, childs));
- assert(Interpreter::isMember(compundChild2, childs));
+ assert(Interpreter::isMember(compoundChild1, childs));
+ assert(Interpreter::isMember(compoundChild2, childs));
assert(!Interpreter::isMember(compoundState, childs));
- assert(Interpreter::isDescendant(compundChild1, compoundState));
+ assert(Interpreter::isDescendant(compoundChild1, compoundState));
std::string transEvents;
transEvents = "error";