summaryrefslogtreecommitdiffstats
path: root/src/uscxml/plugins/invoker/audio
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 /src/uscxml/plugins/invoker/audio
parent01f8198f8b548e3f28cad1a441ceb8af6ea850a4 (diff)
downloaduscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.zip
uscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.tar.gz
uscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.tar.bz2
Extended Java bindings and OpenAL invoker
Diffstat (limited to 'src/uscxml/plugins/invoker/audio')
-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
9 files changed, 1370 insertions, 0 deletions
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 */