diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-08-09 15:05:52 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-08-09 15:05:52 (GMT) |
commit | 6dce9df7f483f3229bb2f34f0386ce37a1551e07 (patch) | |
tree | 1d3acaec4612d74ee3234c808df7ae5fa3b4ef9f /src/uscxml/plugins | |
parent | 01f8198f8b548e3f28cad1a441ceb8af6ea850a4 (diff) | |
download | uscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.zip uscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.tar.gz uscxml-6dce9df7f483f3229bb2f34f0386ce37a1551e07.tar.bz2 |
Extended Java bindings and OpenAL invoker
Diffstat (limited to 'src/uscxml/plugins')
-rw-r--r-- | src/uscxml/plugins/datamodel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/CMakeLists.txt | 35 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/AudioToolbox.h | 32 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/AudioToolbox.mm | 152 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/LibSoundFile.cpp | 50 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/LibSoundFile.h | 27 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp | 362 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/OpenALInvoker.h | 88 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/OpenALPlayer.cpp | 523 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/OpenALPlayer.h | 106 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/audio/PCMConverter.h | 30 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp | 2 |
12 files changed, 1406 insertions, 3 deletions
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 |