Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJörg Müller <nexyon@gmail.com>2017-08-18 09:24:12 +0300
committerJörg Müller <nexyon@gmail.com>2017-08-18 09:24:12 +0300
commit986267300ba42a5c99d2802cd701803dd558e389 (patch)
tree9bf6a84f8e3ebb8d01e5617b1cccfd2693cc1345 /extern/audaspace/plugins
parentd0dad0260434c4420fa9756264c1cc5e745e5ec9 (diff)
Audaspace: Moving audaspace 1.3 into extern.
Deleting the old internal audaspace. Major changes from there are: - The whole library was refactored to use C++11. - Many stability and performance improvements. - Major Python API refactor: - Most requested: Play self generated sounds using numpy arrays. - For games: Sound list, random sounds and dynamic music. - Writing sounds to files. - Sequencing API. - Opening sound devices, eg. Jack. - Ability to choose different OpenAL devices in the user settings.
Diffstat (limited to 'extern/audaspace/plugins')
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEG.cpp63
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEG.h60
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp397
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEGReader.h184
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp427
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEGWriter.h145
-rw-r--r--extern/audaspace/plugins/jack/JackDevice.cpp385
-rw-r--r--extern/audaspace/plugins/jack/JackDevice.h204
-rw-r--r--extern/audaspace/plugins/jack/JackLibrary.cpp59
-rw-r--r--extern/audaspace/plugins/jack/JackLibrary.h48
-rw-r--r--extern/audaspace/plugins/jack/JackSymbols.h45
-rw-r--r--extern/audaspace/plugins/jack/JackSynchronizer.cpp58
-rw-r--r--extern/audaspace/plugins/jack/JackSynchronizer.h59
-rw-r--r--extern/audaspace/plugins/libsndfile/SndFile.cpp62
-rw-r--r--extern/audaspace/plugins/libsndfile/SndFile.h60
-rw-r--r--extern/audaspace/plugins/libsndfile/SndFileReader.cpp161
-rw-r--r--extern/audaspace/plugins/libsndfile/SndFileReader.h125
-rw-r--r--extern/audaspace/plugins/libsndfile/SndFileWriter.cpp128
-rw-r--r--extern/audaspace/plugins/libsndfile/SndFileWriter.h84
-rw-r--r--extern/audaspace/plugins/openal/OpenALDevice.cpp1490
-rw-r--r--extern/audaspace/plugins/openal/OpenALDevice.h296
-rw-r--r--extern/audaspace/plugins/openal/OpenALReader.cpp96
-rw-r--r--extern/audaspace/plugins/openal/OpenALReader.h83
-rw-r--r--extern/audaspace/plugins/sdl/SDLDevice.cpp156
-rw-r--r--extern/audaspace/plugins/sdl/SDLDevice.h82
25 files changed, 4957 insertions, 0 deletions
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp
new file mode 100644
index 00000000000..7f9b762f816
--- /dev/null
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "FFMPEG.h"
+#include "FFMPEGReader.h"
+#include "FFMPEGWriter.h"
+#include "file/FileManager.h"
+
+AUD_NAMESPACE_BEGIN
+
+FFMPEG::FFMPEG()
+{
+ av_register_all();
+}
+
+void FFMPEG::registerPlugin()
+{
+ std::shared_ptr<FFMPEG> plugin = std::shared_ptr<FFMPEG>(new FFMPEG);
+ FileManager::registerInput(plugin);
+ FileManager::registerOutput(plugin);
+}
+
+std::shared_ptr<IReader> FFMPEG::createReader(std::string filename)
+{
+ return std::shared_ptr<IReader>(new FFMPEGReader(filename));
+}
+
+std::shared_ptr<IReader> FFMPEG::createReader(std::shared_ptr<Buffer> buffer)
+{
+ return std::shared_ptr<IReader>(new FFMPEGReader(buffer));
+}
+
+std::shared_ptr<IWriter> FFMPEG::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
+{
+ return std::shared_ptr<IWriter>(new FFMPEGWriter(filename, specs, format, codec, bitrate));
+}
+
+#ifdef FFMPEG_PLUGIN
+extern "C" AUD_PLUGIN_API void registerPlugin()
+{
+ FFMPEG::registerPlugin();
+}
+
+extern "C" AUD_PLUGIN_API const char* getName()
+{
+ return "FFMPEG";
+}
+#endif
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEG.h b/extern/audaspace/plugins/ffmpeg/FFMPEG.h
new file mode 100644
index 00000000000..108ba547e0f
--- /dev/null
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEG.h
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef FFMPEG_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file FFMPEG.h
+ * @ingroup plugin
+ * The FFMPEG class.
+ */
+
+#include "file/IFileInput.h"
+#include "file/IFileOutput.h"
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This plugin class reads and writes sounds via ffmpeg.
+ */
+class AUD_PLUGIN_API FFMPEG : public IFileInput, public IFileOutput
+{
+private:
+ // delete copy constructor and operator=
+ FFMPEG(const FFMPEG&) = delete;
+ FFMPEG& operator=(const FFMPEG&) = delete;
+
+public:
+ /**
+ * Creates a new ffmpeg plugin.
+ */
+ FFMPEG();
+
+ /**
+ * Registers this plugin.
+ */
+ static void registerPlugin();
+
+ virtual std::shared_ptr<IReader> createReader(std::string filename);
+ virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer);
+ virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp
new file mode 100644
index 00000000000..6b79cc5abfd
--- /dev/null
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "FFMPEGReader.h"
+#include "Exception.h"
+
+#include <algorithm>
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avio.h>
+}
+
+AUD_NAMESPACE_BEGIN
+
+int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
+{
+ AVFrame* frame = nullptr;
+ int got_frame;
+ int read_length;
+ uint8_t* orig_data = packet.data;
+ int orig_size = packet.size;
+
+ int buf_size = buffer.getSize();
+ int buf_pos = 0;
+
+ while(packet.size > 0)
+ {
+ got_frame = 0;
+
+ if(!frame)
+ frame = av_frame_alloc();
+ else
+ av_frame_unref(frame);
+
+ read_length = avcodec_decode_audio4(m_codecCtx, frame, &got_frame, &packet);
+ if(read_length < 0)
+ break;
+
+ if(got_frame)
+ {
+ int data_size = av_samples_get_buffer_size(nullptr, m_codecCtx->channels, frame->nb_samples, m_codecCtx->sample_fmt, 1);
+
+ if(buf_size - buf_pos < data_size)
+ {
+ buffer.resize(buf_size + data_size, true);
+ buf_size += data_size;
+ }
+
+ if(m_tointerleave)
+ {
+ int single_size = data_size / m_codecCtx->channels / frame->nb_samples;
+ for(int channel = 0; channel < m_codecCtx->channels; channel++)
+ {
+ for(int i = 0; i < frame->nb_samples; i++)
+ {
+ std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size,
+ frame->data[channel] + i * single_size, single_size);
+ }
+ }
+ }
+ else
+ std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos, frame->data[0], data_size);
+
+ buf_pos += data_size;
+ }
+ packet.size -= read_length;
+ packet.data += read_length;
+ }
+
+ packet.data = orig_data;
+ packet.size = orig_size;
+ av_free(frame);
+
+ return buf_pos;
+}
+
+void FFMPEGReader::init()
+{
+ m_position = 0;
+ m_pkgbuf_left = 0;
+
+ if(avformat_find_stream_info(m_formatCtx, nullptr) < 0)
+ AUD_THROW(FileException, "File couldn't be read, ffmpeg couldn't find the stream info.");
+
+ // find audio stream and codec
+ m_stream = -1;
+
+ for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++)
+ {
+ if((m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ && (m_stream < 0))
+ {
+ m_stream=i;
+ break;
+ }
+ }
+
+ if(m_stream == -1)
+ AUD_THROW(FileException, "File couldn't be read, no audio stream found by ffmpeg.");
+
+ m_codecCtx = m_formatCtx->streams[m_stream]->codec;
+
+ // get a decoder and open it
+ AVCodec* aCodec = avcodec_find_decoder(m_codecCtx->codec_id);
+ if(!aCodec)
+ AUD_THROW(FileException, "File couldn't be read, no decoder found with ffmpeg.");
+
+ if(avcodec_open2(m_codecCtx, aCodec, nullptr) < 0)
+ AUD_THROW(FileException, "File couldn't be read, ffmpeg codec couldn't be opened.");
+
+ m_specs.channels = (Channels) m_codecCtx->channels;
+ m_tointerleave = av_sample_fmt_is_planar(m_codecCtx->sample_fmt);
+
+ switch(av_get_packed_sample_fmt(m_codecCtx->sample_fmt))
+ {
+ case AV_SAMPLE_FMT_U8:
+ m_convert = convert_u8_float;
+ m_specs.format = FORMAT_U8;
+ break;
+ case AV_SAMPLE_FMT_S16:
+ m_convert = convert_s16_float;
+ m_specs.format = FORMAT_S16;
+ break;
+ case AV_SAMPLE_FMT_S32:
+ m_convert = convert_s32_float;
+ m_specs.format = FORMAT_S32;
+ break;
+ case AV_SAMPLE_FMT_FLT:
+ m_convert = convert_copy<float>;
+ m_specs.format = FORMAT_FLOAT32;
+ break;
+ case AV_SAMPLE_FMT_DBL:
+ m_convert = convert_double_float;
+ m_specs.format = FORMAT_FLOAT64;
+ break;
+ default:
+ AUD_THROW(FileException, "File couldn't be read, ffmpeg sample format unknown.");
+ }
+
+ m_specs.rate = (SampleRate) m_codecCtx->sample_rate;
+}
+
+FFMPEGReader::FFMPEGReader(std::string filename) :
+ m_pkgbuf(),
+ m_formatCtx(nullptr),
+ m_aviocontext(nullptr),
+ m_membuf(nullptr)
+{
+ // open file
+ if(avformat_open_input(&m_formatCtx, filename.c_str(), nullptr, nullptr)!=0)
+ AUD_THROW(FileException, "File couldn't be opened with ffmpeg.");
+
+ try
+ {
+ init();
+ }
+ catch(Exception&)
+ {
+ avformat_close_input(&m_formatCtx);
+ throw;
+ }
+}
+
+FFMPEGReader::FFMPEGReader(std::shared_ptr<Buffer> buffer) :
+ m_pkgbuf(),
+ m_membuffer(buffer),
+ m_membufferpos(0)
+{
+ m_membuf = reinterpret_cast<data_t*>(av_malloc(FF_MIN_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE));
+
+ m_aviocontext = avio_alloc_context(m_membuf, FF_MIN_BUFFER_SIZE, 0, this, read_packet, nullptr, seek_packet);
+
+ if(!m_aviocontext)
+ {
+ av_free(m_aviocontext);
+ AUD_THROW(FileException, "Buffer reading context couldn't be created with ffmpeg.");
+ }
+
+ m_formatCtx = avformat_alloc_context();
+ m_formatCtx->pb = m_aviocontext;
+ if(avformat_open_input(&m_formatCtx, "", nullptr, nullptr)!=0)
+ {
+ av_free(m_aviocontext);
+ AUD_THROW(FileException, "Buffer couldn't be read with ffmpeg.");
+ }
+
+ try
+ {
+ init();
+ }
+ catch(Exception&)
+ {
+ avformat_close_input(&m_formatCtx);
+ av_free(m_aviocontext);
+ throw;
+ }
+}
+
+FFMPEGReader::~FFMPEGReader()
+{
+ avcodec_close(m_codecCtx);
+ avformat_close_input(&m_formatCtx);
+}
+
+int FFMPEGReader::read_packet(void* opaque, uint8_t* buf, int buf_size)
+{
+ FFMPEGReader* reader = reinterpret_cast<FFMPEGReader*>(opaque);
+
+ int size = std::min(buf_size, int(reader->m_membuffer->getSize() - reader->m_membufferpos));
+
+ if(size < 0)
+ return -1;
+
+ std::memcpy(buf, ((data_t*)reader->m_membuffer->getBuffer()) + reader->m_membufferpos, size);
+ reader->m_membufferpos += size;
+
+ return size;
+}
+
+int64_t FFMPEGReader::seek_packet(void* opaque, int64_t offset, int whence)
+{
+ FFMPEGReader* reader = reinterpret_cast<FFMPEGReader*>(opaque);
+
+ switch(whence)
+ {
+ case SEEK_SET:
+ reader->m_membufferpos = 0;
+ break;
+ case SEEK_END:
+ reader->m_membufferpos = reader->m_membuffer->getSize();
+ break;
+ case AVSEEK_SIZE:
+ return reader->m_membuffer->getSize();
+ }
+
+ return (reader->m_membufferpos += offset);
+}
+
+bool FFMPEGReader::isSeekable() const
+{
+ return true;
+}
+
+void FFMPEGReader::seek(int position)
+{
+ if(position >= 0)
+ {
+ uint64_t st_time = m_formatCtx->start_time;
+ uint64_t seek_pos = ((uint64_t)position) * ((uint64_t)AV_TIME_BASE) / ((uint64_t)m_specs.rate);
+
+ if(st_time != AV_NOPTS_VALUE) {
+ seek_pos += st_time;
+ }
+
+ double pts_time_base =
+ av_q2d(m_formatCtx->streams[m_stream]->time_base);
+ uint64_t pts_st_time =
+ ((st_time != AV_NOPTS_VALUE) ? st_time : 0)
+ / pts_time_base / (uint64_t) AV_TIME_BASE;
+
+ // a value < 0 tells us that seeking failed
+ if(av_seek_frame(m_formatCtx, -1, seek_pos,
+ AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0)
+ {
+ avcodec_flush_buffers(m_codecCtx);
+ m_position = position;
+
+ AVPacket packet;
+ bool search = true;
+
+ while(search && av_read_frame(m_formatCtx, &packet) >= 0)
+ {
+ // is it a frame from the audio stream?
+ if(packet.stream_index == m_stream)
+ {
+ // decode the package
+ m_pkgbuf_left = decode(packet, m_pkgbuf);
+ search = false;
+
+ // check position
+ if(packet.pts != AV_NOPTS_VALUE)
+ {
+ // calculate real position, and read to frame!
+ m_position = (packet.pts - pts_st_time) * pts_time_base * m_specs.rate;
+
+ if(m_position < position)
+ {
+ // read until we're at the right position
+ int length = AUD_DEFAULT_BUFFER_SIZE;
+ Buffer buffer(length * AUD_SAMPLE_SIZE(m_specs));
+ bool eos;
+ for(int len = position - m_position; len > 0; len -= AUD_DEFAULT_BUFFER_SIZE)
+ {
+ if(len < AUD_DEFAULT_BUFFER_SIZE)
+ length = len;
+ read(length, eos, buffer.getBuffer());
+ }
+ }
+ }
+ }
+ av_free_packet(&packet);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "seeking failed!\n");
+ // Seeking failed, do nothing.
+ }
+ }
+}
+
+int FFMPEGReader::getLength() const
+{
+ // return approximated remaning size
+ return (int)((m_formatCtx->duration * m_codecCtx->sample_rate)
+ / AV_TIME_BASE)-m_position;
+}
+
+int FFMPEGReader::getPosition() const
+{
+ return m_position;
+}
+
+Specs FFMPEGReader::getSpecs() const
+{
+ return m_specs.specs;
+}
+
+void FFMPEGReader::read(int& length, bool& eos, sample_t* buffer)
+{
+ // read packages and decode them
+ AVPacket packet;
+ int data_size = 0;
+ int pkgbuf_pos;
+ int left = length;
+ int sample_size = AUD_DEVICE_SAMPLE_SIZE(m_specs);
+
+ sample_t* buf = buffer;
+ pkgbuf_pos = m_pkgbuf_left;
+ m_pkgbuf_left = 0;
+
+ // there may still be data in the buffer from the last call
+ if(pkgbuf_pos > 0)
+ {
+ data_size = std::min(pkgbuf_pos, left * sample_size);
+ m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format));
+ buf += data_size / AUD_FORMAT_SIZE(m_specs.format);
+ left -= data_size/sample_size;
+ }
+
+ // for each frame read as long as there isn't enough data already
+ while((left > 0) && (av_read_frame(m_formatCtx, &packet) >= 0))
+ {
+ // is it a frame from the audio stream?
+ if(packet.stream_index == m_stream)
+ {
+ // decode the package
+ pkgbuf_pos = decode(packet, m_pkgbuf);
+
+ // copy to output buffer
+ data_size = std::min(pkgbuf_pos, left * sample_size);
+ m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format));
+ buf += data_size / AUD_FORMAT_SIZE(m_specs.format);
+ left -= data_size/sample_size;
+ }
+ av_free_packet(&packet);
+ }
+ // read more data than necessary?
+ if(pkgbuf_pos > data_size)
+ {
+ m_pkgbuf_left = pkgbuf_pos-data_size;
+ memmove(m_pkgbuf.getBuffer(),
+ ((data_t*)m_pkgbuf.getBuffer())+data_size,
+ pkgbuf_pos-data_size);
+ }
+
+ if((eos = (left > 0)))
+ length -= left;
+
+ m_position += length;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h
new file mode 100644
index 00000000000..e2ae959912d
--- /dev/null
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef FFMPEG_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file FFMPEGReader.h
+ * @ingroup plugin
+ * The FFMPEGReader class.
+ */
+
+#include "respec/ConverterFunctions.h"
+#include "IReader.h"
+#include "util/Buffer.h"
+
+#include <string>
+#include <memory>
+
+struct AVCodecContext;
+extern "C" {
+#include <libavformat/avformat.h>
+}
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This class reads a sound file via ffmpeg.
+ * \warning Seeking may not be accurate! Moreover the position is updated after
+ * a buffer reading call. So calling getPosition right after seek
+ * normally results in a wrong value.
+ */
+class AUD_PLUGIN_API FFMPEGReader : public IReader
+{
+private:
+ /**
+ * The current position in samples.
+ */
+ int m_position;
+
+ /**
+ * The specification of the audio data.
+ */
+ DeviceSpecs m_specs;
+
+ /**
+ * The buffer for package reading.
+ */
+ Buffer m_pkgbuf;
+
+ /**
+ * The count of samples still available from the last read package.
+ */
+ int m_pkgbuf_left;
+
+ /**
+ * The AVFormatContext structure for using ffmpeg.
+ */
+ AVFormatContext* m_formatCtx;
+
+ /**
+ * The AVCodecContext structure for using ffmpeg.
+ */
+ AVCodecContext* m_codecCtx;
+
+ /**
+ * The AVIOContext to read the data from.
+ */
+ AVIOContext* m_aviocontext;
+
+ /**
+ * The stream ID in the file.
+ */
+ int m_stream;
+
+ /**
+ * Converter function.
+ */
+ convert_f m_convert;
+
+ /**
+ * The memory file to read from.
+ */
+ std::shared_ptr<Buffer> m_membuffer;
+
+ /**
+ * The buffer to read with.
+ */
+ data_t* m_membuf;
+
+ /**
+ * Reading position of the buffer.
+ */
+ int64_t m_membufferpos;
+
+ /**
+ * Whether the audio data has to be interleaved after reading.
+ */
+ bool m_tointerleave;
+
+ /**
+ * Decodes a packet into the given buffer.
+ * \param packet The AVPacket to decode.
+ * \param buffer The target buffer.
+ * \return The count of read bytes.
+ */
+ AUD_LOCAL int decode(AVPacket& packet, Buffer& buffer);
+
+ /**
+ * Initializes the object.
+ */
+ AUD_LOCAL void init();
+
+ // delete copy constructor and operator=
+ FFMPEGReader(const FFMPEGReader&) = delete;
+ FFMPEGReader& operator=(const FFMPEGReader&) = delete;
+
+public:
+ /**
+ * Creates a new reader.
+ * \param filename The path to the file to be read.
+ * \exception Exception Thrown if the file specified does not exist or
+ * cannot be read with ffmpeg.
+ */
+ FFMPEGReader(std::string filename);
+
+ /**
+ * Creates a new reader.
+ * \param buffer The buffer to read from.
+ * \exception Exception Thrown if the buffer specified cannot be read
+ * with ffmpeg.
+ */
+ FFMPEGReader(std::shared_ptr<Buffer> buffer);
+
+ /**
+ * Destroys the reader and closes the file.
+ */
+ virtual ~FFMPEGReader();
+
+ /**
+ * Reads data to a memory buffer.
+ * This function is used for avio only.
+ * @param opaque The FFMPEGReader.
+ * @param buf The buffer to read to.
+ * @param buf_size The size of the buffer.
+ * @return How many bytes have been read.
+ */
+ static int read_packet(void* opaque, uint8_t* buf, int buf_size);
+
+ /**
+ * Seeks within data.
+ * This function is used for avio only.
+ * @param opaque The FFMPEGReader.
+ * @param offset The byte offset to seek to.
+ * @param whence The seeking action.
+ * @return The current position or the size of the data if requested.
+ */
+ static int64_t seek_packet(void* opaque, int64_t offset, int whence);
+
+ virtual bool isSeekable() const;
+ virtual void seek(int position);
+ virtual int getLength() const;
+ virtual int getPosition() const;
+ virtual Specs getSpecs() const;
+ virtual void read(int& length, bool& eos, sample_t* buffer);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp
new file mode 100644
index 00000000000..f79f0f7fc6b
--- /dev/null
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp
@@ -0,0 +1,427 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "FFMPEGWriter.h"
+#include "Exception.h"
+
+#include <algorithm>
+#include <cstring>
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avio.h>
+}
+
+AUD_NAMESPACE_BEGIN
+
+void FFMPEGWriter::encode()
+{
+ sample_t* data = m_input_buffer.getBuffer();
+
+ if(m_deinterleave)
+ {
+ m_deinterleave_buffer.assureSize(m_input_buffer.getSize());
+
+ sample_t* dbuf = m_deinterleave_buffer.getBuffer();
+ // deinterleave
+ int single_size = sizeof(sample_t);
+ for(int channel = 0; channel < m_specs.channels; channel++)
+ {
+ for(int i = 0; i < m_input_buffer.getSize() / AUD_SAMPLE_SIZE(m_specs); i++)
+ {
+ std::memcpy(((data_t*)dbuf) + (m_input_samples * channel + i) * single_size,
+ ((data_t*)data) + ((m_specs.channels * i) + channel) * single_size, single_size);
+ }
+ }
+
+ // convert first
+ if(m_input_size)
+ m_convert(reinterpret_cast<data_t*>(data), reinterpret_cast<data_t*>(dbuf), m_input_samples * m_specs.channels);
+ else
+ std::memcpy(data, dbuf, m_input_buffer.getSize());
+ }
+ else
+ // convert first
+ if(m_input_size)
+ m_convert(reinterpret_cast<data_t*>(data), reinterpret_cast<data_t*>(data), m_input_samples * m_specs.channels);
+
+ AVPacket packet;
+
+ packet.data = nullptr;
+ packet.size = 0;
+
+ av_init_packet(&packet);
+
+ AVFrame* frame = av_frame_alloc();
+ av_frame_unref(frame);
+ int got_packet;
+
+ frame->nb_samples = m_input_samples;
+ frame->format = m_codecCtx->sample_fmt;
+ frame->channel_layout = m_codecCtx->channel_layout;
+
+ if(avcodec_fill_audio_frame(frame, m_specs.channels, m_codecCtx->sample_fmt, reinterpret_cast<data_t*>(data), m_input_buffer.getSize(), 0) < 0)
+ AUD_THROW(FileException, "File couldn't be written, filling the audio frame failed with ffmpeg.");
+
+ AVRational sample_time = { 1, static_cast<int>(m_specs.rate) };
+ frame->pts = av_rescale_q(m_position - m_input_samples, m_codecCtx->time_base, sample_time);
+
+ if(avcodec_encode_audio2(m_codecCtx, &packet, frame, &got_packet))
+ {
+ av_frame_free(&frame);
+ AUD_THROW(FileException, "File couldn't be written, audio encoding failed with ffmpeg.");
+ }
+
+ if(got_packet)
+ {
+ packet.flags |= AV_PKT_FLAG_KEY;
+ packet.stream_index = m_stream->index;
+ if(av_write_frame(m_formatCtx, &packet) < 0)
+ {
+ av_free_packet(&packet);
+ av_frame_free(&frame);
+ AUD_THROW(FileException, "Frame couldn't be writen to the file with ffmpeg.");
+ }
+ av_free_packet(&packet);
+ }
+
+ av_frame_free(&frame);
+}
+
+void FFMPEGWriter::close()
+{
+ int got_packet = true;
+
+ while(got_packet)
+ {
+ AVPacket packet;
+
+ packet.data = nullptr;
+ packet.size = 0;
+
+ av_init_packet(&packet);
+
+ if(avcodec_encode_audio2(m_codecCtx, &packet, nullptr, &got_packet))
+ AUD_THROW(FileException, "File end couldn't be written, audio encoding failed with ffmpeg.");
+
+ if(got_packet)
+ {
+ packet.flags |= AV_PKT_FLAG_KEY;
+ packet.stream_index = m_stream->index;
+ if(av_write_frame(m_formatCtx, &packet))
+ {
+ av_free_packet(&packet);
+ AUD_THROW(FileException, "Final frames couldn't be writen to the file with ffmpeg.");
+ }
+ av_free_packet(&packet);
+ }
+ }
+}
+
+FFMPEGWriter::FFMPEGWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) :
+ m_position(0),
+ m_specs(specs),
+ m_input_samples(0),
+ m_deinterleave(false)
+{
+ static const char* formats[] = { nullptr, "ac3", "flac", "matroska", "mp2", "mp3", "ogg", "wav" };
+
+ if(avformat_alloc_output_context2(&m_formatCtx, nullptr, formats[format], filename.c_str()) < 0)
+ AUD_THROW(FileException, "File couldn't be written, format couldn't be found with ffmpeg.");
+
+ m_outputFmt = m_formatCtx->oformat;
+
+ if(!m_outputFmt) {
+ avformat_free_context(m_formatCtx);
+ AUD_THROW(FileException, "File couldn't be written, output format couldn't be found with ffmpeg.");
+ }
+
+ m_outputFmt->audio_codec = AV_CODEC_ID_NONE;
+
+ switch(codec)
+ {
+ case CODEC_AAC:
+ m_outputFmt->audio_codec = AV_CODEC_ID_AAC;
+ break;
+ case CODEC_AC3:
+ m_outputFmt->audio_codec = AV_CODEC_ID_AC3;
+ break;
+ case CODEC_FLAC:
+ m_outputFmt->audio_codec = AV_CODEC_ID_FLAC;
+ break;
+ case CODEC_MP2:
+ m_outputFmt->audio_codec = AV_CODEC_ID_MP2;
+ break;
+ case CODEC_MP3:
+ m_outputFmt->audio_codec = AV_CODEC_ID_MP3;
+ break;
+ case CODEC_OPUS:
+ m_outputFmt->audio_codec = AV_CODEC_ID_OPUS;
+ break;
+ case CODEC_PCM:
+ switch(specs.format)
+ {
+ case FORMAT_U8:
+ m_outputFmt->audio_codec = AV_CODEC_ID_PCM_U8;
+ break;
+ case FORMAT_S16:
+ m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S16LE;
+ break;
+ case FORMAT_S24:
+ m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S24LE;
+ break;
+ case FORMAT_S32:
+ m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S32LE;
+ break;
+ case FORMAT_FLOAT32:
+ m_outputFmt->audio_codec = AV_CODEC_ID_PCM_F32LE;
+ break;
+ case FORMAT_FLOAT64:
+ m_outputFmt->audio_codec = AV_CODEC_ID_PCM_F64LE;
+ break;
+ default:
+ m_outputFmt->audio_codec = AV_CODEC_ID_NONE;
+ break;
+ }
+ break;
+ case CODEC_VORBIS:
+ m_outputFmt->audio_codec = AV_CODEC_ID_VORBIS;
+ break;
+ default:
+ m_outputFmt->audio_codec = AV_CODEC_ID_NONE;
+ break;
+ }
+
+ try
+ {
+ if(m_outputFmt->audio_codec == AV_CODEC_ID_NONE)
+ AUD_THROW(FileException, "File couldn't be written, audio codec not found with ffmpeg.");
+
+ AVCodec* codec = avcodec_find_encoder(m_outputFmt->audio_codec);
+ if(!codec)
+ AUD_THROW(FileException, "File couldn't be written, audio encoder couldn't be found with ffmpeg.");
+
+ m_stream = avformat_new_stream(m_formatCtx, codec);
+ if(!m_stream)
+ AUD_THROW(FileException, "File couldn't be written, stream creation failed with ffmpeg.");
+
+ m_stream->id = m_formatCtx->nb_streams - 1;
+
+ m_codecCtx = m_stream->codec;
+
+ switch(m_specs.format)
+ {
+ case FORMAT_U8:
+ m_convert = convert_float_u8;
+ m_codecCtx->sample_fmt = AV_SAMPLE_FMT_U8;
+ break;
+ case FORMAT_S16:
+ m_convert = convert_float_s16;
+ m_codecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
+ break;
+ case FORMAT_S32:
+ m_convert = convert_float_s32;
+ m_codecCtx->sample_fmt = AV_SAMPLE_FMT_S32;
+ break;
+ case FORMAT_FLOAT64:
+ m_convert = convert_float_double;
+ m_codecCtx->sample_fmt = AV_SAMPLE_FMT_DBL;
+ break;
+ default:
+ m_convert = convert_copy<sample_t>;
+ m_codecCtx->sample_fmt = AV_SAMPLE_FMT_FLT;
+ break;
+ }
+
+ if(m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
+ m_codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+ bool format_supported = false;
+
+ for(int i = 0; codec->sample_fmts[i] != -1; i++)
+ {
+ if(av_get_alt_sample_fmt(codec->sample_fmts[i], false) == m_codecCtx->sample_fmt)
+ {
+ m_deinterleave = av_sample_fmt_is_planar(codec->sample_fmts[i]);
+ m_codecCtx->sample_fmt = codec->sample_fmts[i];
+ format_supported = true;
+ }
+ }
+
+ if(!format_supported)
+ {
+ int chosen_index = 0;
+ auto chosen = av_get_alt_sample_fmt(codec->sample_fmts[chosen_index], false);
+ for(int i = 1; codec->sample_fmts[i] != -1; i++)
+ {
+ auto fmt = av_get_alt_sample_fmt(codec->sample_fmts[i], false);
+ if((fmt > chosen && chosen < m_codecCtx->sample_fmt) || (fmt > m_codecCtx->sample_fmt && fmt < chosen))
+ {
+ chosen = fmt;
+ chosen_index = i;
+ }
+ }
+
+ m_codecCtx->sample_fmt = codec->sample_fmts[chosen_index];
+ m_deinterleave = av_sample_fmt_is_planar(m_codecCtx->sample_fmt);
+ switch(av_get_alt_sample_fmt(m_codecCtx->sample_fmt, false))
+ {
+ case AV_SAMPLE_FMT_U8:
+ specs.format = FORMAT_U8;
+ m_convert = convert_float_u8;
+ break;
+ case AV_SAMPLE_FMT_S16:
+ specs.format = FORMAT_S16;
+ m_convert = convert_float_s16;
+ break;
+ case AV_SAMPLE_FMT_S32:
+ specs.format = FORMAT_S32;
+ m_convert = convert_float_s32;
+ break;
+ case AV_SAMPLE_FMT_FLT:
+ specs.format = FORMAT_FLOAT32;
+ m_convert = convert_copy<sample_t>;
+ break;
+ case AV_SAMPLE_FMT_DBL:
+ specs.format = FORMAT_FLOAT64;
+ m_convert = convert_float_double;
+ break;
+ default:
+ AUD_THROW(FileException, "File couldn't be written, sample format not supported with ffmpeg.");
+ }
+ }
+
+ m_codecCtx->sample_rate = 0;
+
+ if(codec->supported_samplerates)
+ {
+ for(int i = 0; codec->supported_samplerates[i]; i++)
+ {
+ if(codec->supported_samplerates[i] == m_specs.rate)
+ {
+ m_codecCtx->sample_rate = codec->supported_samplerates[i];
+ break;
+ }
+ else if((codec->supported_samplerates[i] > m_codecCtx->sample_rate && m_specs.rate > m_codecCtx->sample_rate) ||
+ (codec->supported_samplerates[i] < m_codecCtx->sample_rate && m_specs.rate < codec->supported_samplerates[i]))
+ {
+ m_codecCtx->sample_rate = codec->supported_samplerates[i];
+ }
+ }
+ }
+
+ if(m_codecCtx->sample_rate == 0)
+ m_codecCtx->sample_rate = m_specs.rate;
+
+ m_specs.rate = m_codecCtx->sample_rate;
+
+ m_codecCtx->codec_id = m_outputFmt->audio_codec;
+ m_codecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
+ m_codecCtx->bit_rate = bitrate;
+ m_codecCtx->channels = m_specs.channels;
+ m_stream->time_base.num = m_codecCtx->time_base.num = 1;
+ m_stream->time_base.den = m_codecCtx->time_base.den = m_codecCtx->sample_rate;
+
+ if(avcodec_open2(m_codecCtx, codec, nullptr) < 0)
+ AUD_THROW(FileException, "File couldn't be written, encoder couldn't be opened with ffmpeg.");
+
+ int samplesize = std::max(int(AUD_SAMPLE_SIZE(m_specs)), AUD_DEVICE_SAMPLE_SIZE(m_specs));
+
+ if((m_input_size = m_codecCtx->frame_size))
+ m_input_buffer.resize(m_input_size * samplesize);
+
+ if(avio_open(&m_formatCtx->pb, filename.c_str(), AVIO_FLAG_WRITE))
+ AUD_THROW(FileException, "File couldn't be written, file opening failed with ffmpeg.");
+
+ avformat_write_header(m_formatCtx, nullptr);
+ }
+ catch(Exception&)
+ {
+ avformat_free_context(m_formatCtx);
+ throw;
+ }
+}
+
+FFMPEGWriter::~FFMPEGWriter()
+{
+ // writte missing data
+ if(m_input_samples)
+ encode();
+
+ close();
+
+ av_write_trailer(m_formatCtx);
+
+ avcodec_close(m_codecCtx);
+
+ avio_close(m_formatCtx->pb);
+ avformat_free_context(m_formatCtx);
+}
+
+int FFMPEGWriter::getPosition() const
+{
+ return m_position;
+}
+
+DeviceSpecs FFMPEGWriter::getSpecs() const
+{
+ return m_specs;
+}
+
+void FFMPEGWriter::write(unsigned int length, sample_t* buffer)
+{
+ unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs);
+
+ if(m_input_size)
+ {
+ sample_t* inbuf = m_input_buffer.getBuffer();
+
+ while(length)
+ {
+ unsigned int len = std::min(m_input_size - m_input_samples, length);
+
+ std::memcpy(inbuf + m_input_samples * m_specs.channels, buffer, len * samplesize);
+
+ buffer += len * m_specs.channels;
+ m_input_samples += len;
+ m_position += len;
+ length -= len;
+
+ if(m_input_samples == m_input_size)
+ {
+ encode();
+
+ m_input_samples = 0;
+ }
+ }
+ }
+ else // PCM data, can write directly!
+ {
+ int samplesize = AUD_SAMPLE_SIZE(m_specs);
+ m_input_buffer.assureSize(length * std::max(AUD_DEVICE_SAMPLE_SIZE(m_specs), samplesize));
+
+ sample_t* buf = m_input_buffer.getBuffer();
+ m_convert(reinterpret_cast<data_t*>(buf), reinterpret_cast<data_t*>(buffer), length * m_specs.channels);
+
+ m_input_samples = length;
+
+ m_position += length;
+
+ encode();
+ }
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.h b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.h
new file mode 100644
index 00000000000..690185deb64
--- /dev/null
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.h
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef FFMPEG_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file FFMPEGWriter.h
+ * @ingroup plugin
+ * The FFMPEGWriter class.
+ */
+
+#include "respec/ConverterFunctions.h"
+#include "util/Buffer.h"
+#include "file/IWriter.h"
+
+#include <string>
+
+struct AVCodecContext;
+extern "C" {
+#include <libavformat/avformat.h>
+}
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This class writes a sound file via ffmpeg.
+ */
+class AUD_PLUGIN_API FFMPEGWriter : public IWriter
+{
+private:
+ /**
+ * The current position in samples.
+ */
+ int m_position;
+
+ /**
+ * The specification of the audio data.
+ */
+ DeviceSpecs m_specs;
+
+ /**
+ * The AVFormatContext structure for using ffmpeg.
+ */
+ AVFormatContext* m_formatCtx;
+
+ /**
+ * The AVCodecContext structure for using ffmpeg.
+ */
+ AVCodecContext* m_codecCtx;
+
+ /**
+ * The AVOutputFormat structure for using ffmpeg.
+ */
+ AVOutputFormat* m_outputFmt;
+
+ /**
+ * The AVStream structure for using ffmpeg.
+ */
+ AVStream* m_stream;
+
+ /**
+ * The input buffer for the format converted data before encoding.
+ */
+ Buffer m_input_buffer;
+
+ /**
+ * The buffer used for deinterleaving.
+ */
+ Buffer m_deinterleave_buffer;
+
+ /**
+ * The count of input samples we have so far.
+ */
+ unsigned int m_input_samples;
+
+ /**
+ * The count of input samples necessary to encode a packet.
+ */
+ unsigned int m_input_size;
+
+ /**
+ * Whether the ouput has to be deinterleaved before writing.
+ */
+ bool m_deinterleave;
+
+ /**
+ * Converter function.
+ */
+ convert_f m_convert;
+
+ // delete copy constructor and operator=
+ FFMPEGWriter(const FFMPEGWriter&) = delete;
+ FFMPEGWriter& operator=(const FFMPEGWriter&) = delete;
+
+ /**
+ * Encodes to the output buffer.
+ */
+ AUD_LOCAL void encode();
+
+ /**
+ * Finishes writing to the file.
+ */
+ AUD_LOCAL void close();
+
+public:
+ /**
+ * Creates a new writer.
+ * \param filename The path to the file to be read.
+ * \param specs The file's audio specification.
+ * \param format The file's container format.
+ * \param codec The codec used for encoding the audio data.
+ * \param bitrate The bitrate for encoding.
+ * \exception Exception Thrown if the file specified does not exist or
+ * cannot be read with ffmpeg.
+ */
+ FFMPEGWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
+
+ /**
+ * Destroys the writer and closes the file.
+ */
+ virtual ~FFMPEGWriter();
+
+ virtual int getPosition() const;
+ virtual DeviceSpecs getSpecs() const;
+ virtual void write(unsigned int length, sample_t* buffer);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/jack/JackDevice.cpp b/extern/audaspace/plugins/jack/JackDevice.cpp
new file mode 100644
index 00000000000..1d238f74c3a
--- /dev/null
+++ b/extern/audaspace/plugins/jack/JackDevice.cpp
@@ -0,0 +1,385 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "JackDevice.h"
+#include "JackLibrary.h"
+#include "devices/DeviceManager.h"
+#include "devices/IDeviceFactory.h"
+#include "Exception.h"
+#include "IReader.h"
+
+#include <cstring>
+#include <algorithm>
+
+AUD_NAMESPACE_BEGIN
+
+void JackDevice::updateRingBuffers()
+{
+ size_t size, temp;
+ unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs);
+ unsigned int i, j;
+ unsigned int channels = m_specs.channels;
+ sample_t* buffer = m_buffer.getBuffer();
+ float* deinterleave = m_deinterleavebuf.getBuffer();
+ jack_transport_state_t state;
+ jack_position_t position;
+
+ std::unique_lock<std::mutex> lock(m_mixingLock);
+
+ while(m_valid)
+ {
+ if(m_sync > 1)
+ {
+ if(m_syncFunc)
+ {
+ state = AUD_jack_transport_query(m_client, &position);
+ m_syncFunc(m_syncFuncData, state != JackTransportStopped, position.frame / (float) m_specs.rate);
+ }
+
+ for(i = 0; i < channels; i++)
+ AUD_jack_ringbuffer_reset(m_ringbuffers[i]);
+ }
+
+ size = AUD_jack_ringbuffer_write_space(m_ringbuffers[0]);
+ for(i = 1; i < channels; i++)
+ if((temp = AUD_jack_ringbuffer_write_space(m_ringbuffers[i])) < size)
+ size = temp;
+
+ while(size > samplesize)
+ {
+ size /= samplesize;
+ mix((data_t*)buffer, size);
+ for(i = 0; i < channels; i++)
+ {
+ for(j = 0; j < size; j++)
+ deinterleave[i * size + j] = buffer[i + j * channels];
+ AUD_jack_ringbuffer_write(m_ringbuffers[i], (char*)(deinterleave + i * size), size * sizeof(float));
+ }
+
+ size = AUD_jack_ringbuffer_write_space(m_ringbuffers[0]);
+ for(i = 1; i < channels; i++)
+ if((temp = AUD_jack_ringbuffer_write_space(m_ringbuffers[i])) < size)
+ size = temp;
+ }
+
+ if(m_sync > 1)
+ {
+ m_sync = 3;
+ }
+
+ m_mixingCondition.wait(lock);
+ }
+}
+
+int JackDevice::jack_mix(jack_nframes_t length, void* data)
+{
+ JackDevice* device = (JackDevice*)data;
+ unsigned int i;
+ int count = device->m_specs.channels;
+ char* buffer;
+
+ if(device->m_sync)
+ {
+ // play silence while syncing
+ for(unsigned int i = 0; i < count; i++)
+ std::memset(AUD_jack_port_get_buffer(device->m_ports[i], length), 0, length * sizeof(float));
+ }
+ else
+ {
+ size_t temp;
+ size_t readsamples = AUD_jack_ringbuffer_read_space(device->m_ringbuffers[0]);
+ for(i = 1; i < count; i++)
+ if((temp = AUD_jack_ringbuffer_read_space(device->m_ringbuffers[i])) < readsamples)
+ readsamples = temp;
+
+ readsamples = std::min(readsamples / sizeof(float), size_t(length));
+
+ for(unsigned int i = 0; i < count; i++)
+ {
+ buffer = (char*)AUD_jack_port_get_buffer(device->m_ports[i], length);
+ AUD_jack_ringbuffer_read(device->m_ringbuffers[i], buffer, readsamples * sizeof(float));
+ if(readsamples < length)
+ std::memset(buffer + readsamples * sizeof(float), 0, (length - readsamples) * sizeof(float));
+ }
+
+ if(device->m_mixingLock.try_lock())
+ {
+ device->m_mixingCondition.notify_all();
+ device->m_mixingLock.unlock();
+ }
+ }
+
+ return 0;
+}
+
+int JackDevice::jack_sync(jack_transport_state_t state, jack_position_t* pos, void* data)
+{
+ JackDevice* device = (JackDevice*)data;
+
+ if(state == JackTransportStopped)
+ return 1;
+
+ if(device->m_mixingLock.try_lock())
+ {
+ if(device->m_sync > 2)
+ {
+ if(device->m_sync == 3)
+ {
+ device->m_sync = 0;
+ device->m_mixingLock.unlock();
+ return 1;
+ }
+ }
+ else
+ {
+ device->m_sync = 2;
+ device->m_mixingCondition.notify_all();
+ }
+ device->m_mixingLock.unlock();
+ }
+ else if(!device->m_sync)
+ device->m_sync = 1;
+
+ return 0;
+}
+
+void JackDevice::jack_shutdown(void* data)
+{
+ JackDevice* device = (JackDevice*)data;
+ device->m_valid = false;
+}
+
+JackDevice::JackDevice(std::string name, DeviceSpecs specs, int buffersize) :
+ m_synchronizer(this)
+{
+ if(specs.channels == CHANNELS_INVALID)
+ specs.channels = CHANNELS_STEREO;
+
+ // jack uses floats
+ m_specs = specs;
+ m_specs.format = FORMAT_FLOAT32;
+
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+
+ // open client
+ m_client = AUD_jack_client_open(name.c_str(), options, &status);
+ if(m_client == nullptr)
+ AUD_THROW(DeviceException, "Connecting to the JACK server failed.");
+
+ // set callbacks
+ AUD_jack_set_process_callback(m_client, JackDevice::jack_mix, this);
+ AUD_jack_on_shutdown(m_client, JackDevice::jack_shutdown, this);
+ AUD_jack_set_sync_callback(m_client, JackDevice::jack_sync, this);
+
+ // register our output channels which are called ports in jack
+ m_ports = new jack_port_t*[m_specs.channels];
+
+ try
+ {
+ char portname[64];
+ for(int i = 0; i < m_specs.channels; i++)
+ {
+ sprintf(portname, "out %d", i+1);
+ m_ports[i] = AUD_jack_port_register(m_client, portname, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ if(m_ports[i] == nullptr)
+ AUD_THROW(DeviceException, "Registering output port with JACK failed.");
+ }
+ }
+ catch(Exception&)
+ {
+ AUD_jack_client_close(m_client);
+ delete[] m_ports;
+ throw;
+ }
+
+ m_specs.rate = (SampleRate)AUD_jack_get_sample_rate(m_client);
+
+ buffersize *= sizeof(sample_t);
+ m_ringbuffers = new jack_ringbuffer_t*[specs.channels];
+ for(unsigned int i = 0; i < specs.channels; i++)
+ m_ringbuffers[i] = AUD_jack_ringbuffer_create(buffersize);
+ buffersize *= specs.channels;
+ m_deinterleavebuf.resize(buffersize);
+ m_buffer.resize(buffersize);
+
+ create();
+
+ m_valid = true;
+ m_sync = 0;
+ m_syncFunc = nullptr;
+ m_nextState = m_state = AUD_jack_transport_query(m_client, nullptr);
+
+ // activate the client
+ if(AUD_jack_activate(m_client))
+ {
+ AUD_jack_client_close(m_client);
+ delete[] m_ports;
+ for(unsigned int i = 0; i < specs.channels; i++)
+ AUD_jack_ringbuffer_free(m_ringbuffers[i]);
+ delete[] m_ringbuffers;
+ destroy();
+
+ AUD_THROW(DeviceException, "Client activation with JACK failed.");
+ }
+
+ const char** ports = AUD_jack_get_ports(m_client, nullptr, nullptr,
+ JackPortIsPhysical | JackPortIsInput);
+ if(ports != nullptr)
+ {
+ for(int i = 0; i < m_specs.channels && ports[i]; i++)
+ AUD_jack_connect(m_client, AUD_jack_port_name(m_ports[i]), ports[i]);
+
+ AUD_jack_free(ports);
+ }
+
+ m_mixingThread = std::thread(&JackDevice::updateRingBuffers, this);
+}
+
+JackDevice::~JackDevice()
+{
+ if(m_valid)
+ AUD_jack_client_close(m_client);
+ m_valid = false;
+
+ delete[] m_ports;
+
+ m_mixingLock.lock();
+ m_mixingCondition.notify_all();
+ m_mixingLock.unlock();
+
+ m_mixingThread.join();
+
+ for(unsigned int i = 0; i < m_specs.channels; i++)
+ AUD_jack_ringbuffer_free(m_ringbuffers[i]);
+ delete[] m_ringbuffers;
+
+ destroy();
+}
+
+ISynchronizer* JackDevice::getSynchronizer()
+{
+ return &m_synchronizer;
+}
+
+void JackDevice::playing(bool playing)
+{
+ // Do nothing.
+}
+
+void JackDevice::startPlayback()
+{
+ AUD_jack_transport_start(m_client);
+ m_nextState = JackTransportRolling;
+}
+
+void JackDevice::stopPlayback()
+{
+ AUD_jack_transport_stop(m_client);
+ m_nextState = JackTransportStopped;
+}
+
+void JackDevice::seekPlayback(float time)
+{
+ if(time >= 0.0f)
+ AUD_jack_transport_locate(m_client, time * m_specs.rate);
+}
+
+void JackDevice::setSyncCallback(ISynchronizer::syncFunction sync, void* data)
+{
+ m_syncFunc = sync;
+ m_syncFuncData = data;
+}
+
+float JackDevice::getPlaybackPosition()
+{
+ jack_position_t position;
+ AUD_jack_transport_query(m_client, &position);
+ return position.frame / (float) m_specs.rate;
+}
+
+bool JackDevice::doesPlayback()
+{
+ jack_transport_state_t state = AUD_jack_transport_query(m_client, nullptr);
+
+ if(state != m_state)
+ m_nextState = m_state = state;
+
+ return m_nextState != JackTransportStopped;
+}
+
+class JackDeviceFactory : public IDeviceFactory
+{
+private:
+ DeviceSpecs m_specs;
+ int m_buffersize;
+ std::string m_name;
+
+public:
+ JackDeviceFactory() :
+ m_buffersize(AUD_DEFAULT_BUFFER_SIZE),
+ m_name("Audaspace")
+ {
+ m_specs.format = FORMAT_FLOAT32;
+ m_specs.channels = CHANNELS_STEREO;
+ m_specs.rate = RATE_48000;
+ }
+
+ virtual std::shared_ptr<IDevice> openDevice()
+ {
+ return std::shared_ptr<IDevice>(new JackDevice(m_name, m_specs, m_buffersize));
+ }
+
+ virtual int getPriority()
+ {
+ return 0;
+ }
+
+ virtual void setSpecs(DeviceSpecs specs)
+ {
+ m_specs = specs;
+ }
+
+ virtual void setBufferSize(int buffersize)
+ {
+ m_buffersize = buffersize;
+ }
+
+ virtual void setName(std::string name)
+ {
+ m_name = name;
+ }
+};
+
+void JackDevice::registerPlugin()
+{
+ if(loadJACK())
+ DeviceManager::registerDevice("JACK", std::shared_ptr<IDeviceFactory>(new JackDeviceFactory));
+}
+
+#ifdef JACK_PLUGIN
+extern "C" AUD_PLUGIN_API void registerPlugin()
+{
+ JackDevice::registerPlugin();
+}
+
+extern "C" AUD_PLUGIN_API const char* getName()
+{
+ return "JACK";
+}
+#endif
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/jack/JackDevice.h b/extern/audaspace/plugins/jack/JackDevice.h
new file mode 100644
index 00000000000..72143eda149
--- /dev/null
+++ b/extern/audaspace/plugins/jack/JackDevice.h
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef JACK_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file JackDevice.h
+ * @ingroup plugin
+ * The JackDevice class.
+ */
+
+#include "JackSynchronizer.h"
+#include "devices/SoftwareDevice.h"
+#include "util/Buffer.h"
+
+#include <string>
+#include <condition_variable>
+#include <thread>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This device plays back through JACK.
+ */
+class AUD_PLUGIN_API JackDevice : public SoftwareDevice
+{
+private:
+ /**
+ * The output ports of jack.
+ */
+ jack_port_t** m_ports;
+
+ /**
+ * The jack client.
+ */
+ jack_client_t* m_client;
+
+ /**
+ * The output buffer.
+ */
+ Buffer m_buffer;
+
+ /**
+ * The deinterleaving buffer.
+ */
+ Buffer m_deinterleavebuf;
+
+ jack_ringbuffer_t** m_ringbuffers;
+
+ /**
+ * Whether the device is valid.
+ */
+ bool m_valid;
+
+ /// Synchronizer.
+ JackSynchronizer m_synchronizer;
+
+ /**
+ * Invalidates the jack device.
+ * \param data The jack device that gets invalidet by jack.
+ */
+ AUD_LOCAL static void jack_shutdown(void* data);
+
+ /**
+ * Mixes the next bytes into the buffer.
+ * \param length The length in samples to be filled.
+ * \param data A pointer to the jack device.
+ * \return 0 what shows success.
+ */
+ AUD_LOCAL static int jack_mix(jack_nframes_t length, void* data);
+
+ AUD_LOCAL static int jack_sync(jack_transport_state_t state, jack_position_t* pos, void* data);
+
+ /**
+ * Next JACK Transport state (-1 if not expected to change).
+ */
+ jack_transport_state_t m_nextState;
+
+ /**
+ * Current jack transport status.
+ */
+ jack_transport_state_t m_state;
+
+ /**
+ * Syncronisation state.
+ */
+ int m_sync;
+
+ /**
+ * External syncronisation callback function.
+ */
+ ISynchronizer::syncFunction m_syncFunc;
+
+ /**
+ * Data for the sync function.
+ */
+ void* m_syncFuncData;
+
+ /**
+ * The mixing thread.
+ */
+ std::thread m_mixingThread;
+
+ /**
+ * Mutex for mixing.
+ */
+ std::mutex m_mixingLock;
+
+ /**
+ * Condition for mixing.
+ */
+ std::condition_variable m_mixingCondition;
+
+ /**
+ * Updates the ring buffers.
+ */
+ AUD_LOCAL void updateRingBuffers();
+
+ // delete copy constructor and operator=
+ JackDevice(const JackDevice&) = delete;
+ JackDevice& operator=(const JackDevice&) = delete;
+
+protected:
+ virtual void playing(bool playing);
+
+public:
+ /**
+ * Creates a JACK client for audio output.
+ * \param name The client name.
+ * \param specs The wanted audio specification, where only the channel count
+ * is important.
+ * \param buffersize The size of the internal buffer.
+ * \exception Exception Thrown if the audio device cannot be opened.
+ */
+ JackDevice(std::string name, DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
+
+ /**
+ * Closes the JACK client.
+ */
+ virtual ~JackDevice();
+
+ virtual ISynchronizer* getSynchronizer();
+
+ /**
+ * Starts jack transport playback.
+ */
+ void startPlayback();
+
+ /**
+ * Stops jack transport playback.
+ */
+ void stopPlayback();
+
+ /**
+ * Seeks jack transport playback.
+ * \param time The time to seek to.
+ */
+ void seekPlayback(float time);
+
+ /**
+ * Sets the sync callback for jack transport playback.
+ * \param sync The callback function.
+ * \param data The data for the function.
+ */
+ void setSyncCallback(ISynchronizer::syncFunction sync, void* data);
+
+ /**
+ * Retrieves the jack transport playback time.
+ * \return The current time position.
+ */
+ float getPlaybackPosition();
+
+ /**
+ * Returns whether jack transport plays back.
+ * \return Whether jack transport plays back.
+ */
+ bool doesPlayback();
+
+ /**
+ * Registers this plugin.
+ */
+ static void registerPlugin();
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/jack/JackLibrary.cpp b/extern/audaspace/plugins/jack/JackLibrary.cpp
new file mode 100644
index 00000000000..92462a34cca
--- /dev/null
+++ b/extern/audaspace/plugins/jack/JackLibrary.cpp
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#define JACK_LIBRARY_IMPLEMENTATION
+
+#include <string>
+#include <array>
+
+#include "JackLibrary.h"
+
+#ifdef DYNLOAD_JACK
+#include "plugin/PluginManager.h"
+#endif
+
+AUD_NAMESPACE_BEGIN
+
+bool loadJACK()
+{
+#ifdef DYNLOAD_JACK
+ std::array<const std::string, 5> names = {"libjack.so", "libjack.so.0", "libjack.so.1", "libjack.so.2", "libjack.dll"};
+
+ void* handle = nullptr;
+
+ for(auto& name : names)
+ {
+ handle = PluginManager::openLibrary(name);
+ if(handle)
+ break;
+ }
+
+ if (!handle)
+ return false;
+
+#define JACK_SYMBOL(sym) AUD_##sym = reinterpret_cast<decltype(&sym)>(PluginManager::lookupLibrary(handle, #sym))
+#else
+#define JACK_SYMBOL(sym) AUD_##sym = &sym
+#endif
+
+#include "JackSymbols.h"
+
+#undef JACK_SYMBOL
+
+ return AUD_jack_client_open != nullptr;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/jack/JackLibrary.h b/extern/audaspace/plugins/jack/JackLibrary.h
new file mode 100644
index 00000000000..4e210852702
--- /dev/null
+++ b/extern/audaspace/plugins/jack/JackLibrary.h
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef JACK_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file JackDevice.h
+ * @ingroup plugin
+ * The JackDevice class.
+ */
+
+#include "Audaspace.h"
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+AUD_NAMESPACE_BEGIN
+
+#ifdef JACK_LIBRARY_IMPLEMENTATION
+#define JACK_SYMBOL(sym) decltype(&sym) AUD_##sym
+#else
+#define JACK_SYMBOL(sym) extern decltype(&sym) AUD_##sym
+#endif
+
+#include "JackSymbols.h"
+
+#undef JACK_SYMBOL
+
+bool loadJACK();
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/jack/JackSymbols.h b/extern/audaspace/plugins/jack/JackSymbols.h
new file mode 100644
index 00000000000..f8e22a7da34
--- /dev/null
+++ b/extern/audaspace/plugins/jack/JackSymbols.h
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+JACK_SYMBOL(jack_transport_query);
+JACK_SYMBOL(jack_transport_locate);
+
+JACK_SYMBOL(jack_transport_start);
+JACK_SYMBOL(jack_transport_stop);
+
+JACK_SYMBOL(jack_ringbuffer_reset);
+JACK_SYMBOL(jack_ringbuffer_write);
+JACK_SYMBOL(jack_ringbuffer_write_space);
+JACK_SYMBOL(jack_ringbuffer_write_advance);
+JACK_SYMBOL(jack_ringbuffer_read);
+JACK_SYMBOL(jack_ringbuffer_create);
+JACK_SYMBOL(jack_ringbuffer_free);
+JACK_SYMBOL(jack_ringbuffer_read_space);
+JACK_SYMBOL(jack_set_sync_callback);
+
+JACK_SYMBOL(jack_port_get_buffer);
+
+JACK_SYMBOL(jack_client_open);
+JACK_SYMBOL(jack_set_process_callback);
+JACK_SYMBOL(jack_on_shutdown);
+JACK_SYMBOL(jack_port_register);
+JACK_SYMBOL(jack_client_close);
+JACK_SYMBOL(jack_get_sample_rate);
+JACK_SYMBOL(jack_activate);
+JACK_SYMBOL(jack_get_ports);
+JACK_SYMBOL(jack_port_name);
+JACK_SYMBOL(jack_connect);
+JACK_SYMBOL(jack_free);
diff --git a/extern/audaspace/plugins/jack/JackSynchronizer.cpp b/extern/audaspace/plugins/jack/JackSynchronizer.cpp
new file mode 100644
index 00000000000..cd4c448786d
--- /dev/null
+++ b/extern/audaspace/plugins/jack/JackSynchronizer.cpp
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "JackSynchronizer.h"
+
+#include "JackDevice.h"
+
+AUD_NAMESPACE_BEGIN
+
+JackSynchronizer::JackSynchronizer(JackDevice* device) :
+ m_device(device)
+{
+}
+
+void JackSynchronizer::seek(std::shared_ptr<IHandle> handle, float time)
+{
+ m_device->seekPlayback(time);
+}
+
+float JackSynchronizer::getPosition(std::shared_ptr<IHandle> handle)
+{
+ return m_device->getPlaybackPosition();
+}
+
+void JackSynchronizer::play()
+{
+ m_device->startPlayback();
+}
+
+void JackSynchronizer::stop()
+{
+ m_device->stopPlayback();
+}
+
+void JackSynchronizer::setSyncCallback(ISynchronizer::syncFunction function, void* data)
+{
+ m_device->setSyncCallback(function, data);
+}
+
+int JackSynchronizer::isPlaying()
+{
+ return m_device->doesPlayback();
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/jack/JackSynchronizer.h b/extern/audaspace/plugins/jack/JackSynchronizer.h
new file mode 100644
index 00000000000..5c7341a7872
--- /dev/null
+++ b/extern/audaspace/plugins/jack/JackSynchronizer.h
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef JACK_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file JackSynchronizer.h
+ * @ingroup plugin
+ * The JackSynchronizer class.
+ */
+
+#include "devices/ISynchronizer.h"
+
+AUD_NAMESPACE_BEGIN
+
+class JackDevice;
+
+/**
+ * This class is a Synchronizer implementation using JACK Transport.
+ */
+class AUD_PLUGIN_API JackSynchronizer : public ISynchronizer
+{
+private:
+ /// The device that is being synchronized.
+ JackDevice* m_device;
+
+public:
+ /**
+ * Creates a new JackSynchronizer.
+ * @param device The device that should be synchronized.
+ */
+ JackSynchronizer(JackDevice* device);
+
+ virtual void seek(std::shared_ptr<IHandle> handle, float time);
+ virtual float getPosition(std::shared_ptr<IHandle> handle);
+ virtual void play();
+ virtual void stop();
+ virtual void setSyncCallback(syncFunction function, void* data);
+ virtual int isPlaying();
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/libsndfile/SndFile.cpp b/extern/audaspace/plugins/libsndfile/SndFile.cpp
new file mode 100644
index 00000000000..ba4ff24ad68
--- /dev/null
+++ b/extern/audaspace/plugins/libsndfile/SndFile.cpp
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "SndFile.h"
+#include "SndFileReader.h"
+#include "SndFileWriter.h"
+#include "file/FileManager.h"
+
+AUD_NAMESPACE_BEGIN
+
+SndFile::SndFile()
+{
+}
+
+void SndFile::registerPlugin()
+{
+ std::shared_ptr<SndFile> plugin = std::shared_ptr<SndFile>(new SndFile);
+ FileManager::registerInput(plugin);
+ FileManager::registerOutput(plugin);
+}
+
+std::shared_ptr<IReader> SndFile::createReader(std::string filename)
+{
+ return std::shared_ptr<IReader>(new SndFileReader(filename));
+}
+
+std::shared_ptr<IReader> SndFile::createReader(std::shared_ptr<Buffer> buffer)
+{
+ return std::shared_ptr<IReader>(new SndFileReader(buffer));
+}
+
+std::shared_ptr<IWriter> SndFile::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
+{
+ return std::shared_ptr<IWriter>(new SndFileWriter(filename, specs, format, codec, bitrate));
+}
+
+#ifdef LIBSNDFILE_PLUGIN
+extern "C" AUD_PLUGIN_API void registerPlugin()
+{
+ SndFile::registerPlugin();
+}
+
+extern "C" AUD_PLUGIN_API const char* getName()
+{
+ return "LibSndFile";
+}
+#endif
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/libsndfile/SndFile.h b/extern/audaspace/plugins/libsndfile/SndFile.h
new file mode 100644
index 00000000000..61afed1d564
--- /dev/null
+++ b/extern/audaspace/plugins/libsndfile/SndFile.h
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef LIBSNDFILE_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file SndFile.h
+ * @ingroup plugin
+ * The SndFile class.
+ */
+
+#include "file/IFileInput.h"
+#include "file/IFileOutput.h"
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This plugin class reads and writes sounds via libsndfile.
+ */
+class AUD_PLUGIN_API SndFile : public IFileInput, public IFileOutput
+{
+private:
+ // delete copy constructor and operator=
+ SndFile(const SndFile&) = delete;
+ SndFile& operator=(const SndFile&) = delete;
+
+public:
+ /**
+ * Creates a new libsndfile plugin.
+ */
+ SndFile();
+
+ /**
+ * Registers this plugin.
+ */
+ static void registerPlugin();
+
+ virtual std::shared_ptr<IReader> createReader(std::string filename);
+ virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer);
+ virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/libsndfile/SndFileReader.cpp b/extern/audaspace/plugins/libsndfile/SndFileReader.cpp
new file mode 100644
index 00000000000..d2d89814c07
--- /dev/null
+++ b/extern/audaspace/plugins/libsndfile/SndFileReader.cpp
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "SndFileReader.h"
+#include "util/Buffer.h"
+#include "Exception.h"
+
+#include <cstring>
+
+AUD_NAMESPACE_BEGIN
+
+sf_count_t SndFileReader::vio_get_filelen(void* user_data)
+{
+ SndFileReader* reader = (SndFileReader*)user_data;
+ return reader->m_membuffer->getSize();
+}
+
+sf_count_t SndFileReader::vio_seek(sf_count_t offset, int whence,
+ void* user_data)
+{
+ SndFileReader* reader = (SndFileReader*)user_data;
+
+ switch(whence)
+ {
+ case SEEK_SET:
+ reader->m_memoffset = offset;
+ break;
+ case SEEK_CUR:
+ reader->m_memoffset = reader->m_memoffset + offset;
+ break;
+ case SEEK_END:
+ reader->m_memoffset = reader->m_membuffer->getSize() + offset;
+ break;
+ }
+
+ return reader->m_memoffset;
+}
+
+sf_count_t SndFileReader::vio_read(void* ptr, sf_count_t count,
+ void* user_data)
+{
+ SndFileReader* reader = (SndFileReader*)user_data;
+
+ if(reader->m_memoffset + count > reader->m_membuffer->getSize())
+ count = reader->m_membuffer->getSize() - reader->m_memoffset;
+
+ std::memcpy(ptr, ((data_t*)reader->m_membuffer->getBuffer()) +
+ reader->m_memoffset, count);
+ reader->m_memoffset += count;
+
+ return count;
+}
+
+sf_count_t SndFileReader::vio_tell(void* user_data)
+{
+ SndFileReader* reader = (SndFileReader*)user_data;
+
+ return reader->m_memoffset;
+}
+
+SndFileReader::SndFileReader(std::string filename) :
+ m_position(0)
+{
+ SF_INFO sfinfo;
+
+ sfinfo.format = 0;
+ m_sndfile = sf_open(filename.c_str(), SFM_READ, &sfinfo);
+
+ if(!m_sndfile)
+ AUD_THROW(FileException, "The file couldn't be opened with libsndfile.");
+
+ m_specs.channels = (Channels) sfinfo.channels;
+ m_specs.rate = (SampleRate) sfinfo.samplerate;
+ m_length = sfinfo.frames;
+ m_seekable = sfinfo.seekable;
+}
+
+SndFileReader::SndFileReader(std::shared_ptr<Buffer> buffer) :
+ m_position(0),
+ m_membuffer(buffer),
+ m_memoffset(0)
+{
+ m_vio.get_filelen = vio_get_filelen;
+ m_vio.read = vio_read;
+ m_vio.seek = vio_seek;
+ m_vio.tell = vio_tell;
+ m_vio.write = nullptr;
+
+ SF_INFO sfinfo;
+
+ sfinfo.format = 0;
+ m_sndfile = sf_open_virtual(&m_vio, SFM_READ, &sfinfo, this);
+
+ if(!m_sndfile)
+ AUD_THROW(FileException, "The buffer couldn't be read with libsndfile.");
+
+ m_specs.channels = (Channels) sfinfo.channels;
+ m_specs.rate = (SampleRate) sfinfo.samplerate;
+ m_length = sfinfo.frames;
+ m_seekable = sfinfo.seekable;
+}
+
+SndFileReader::~SndFileReader()
+{
+ sf_close(m_sndfile);
+}
+
+bool SndFileReader::isSeekable() const
+{
+ return m_seekable;
+}
+
+void SndFileReader::seek(int position)
+{
+ if(m_seekable)
+ {
+ position = sf_seek(m_sndfile, position, SEEK_SET);
+ m_position = position;
+ }
+}
+
+int SndFileReader::getLength() const
+{
+ return m_length;
+}
+
+int SndFileReader::getPosition() const
+{
+ return m_position;
+}
+
+Specs SndFileReader::getSpecs() const
+{
+ return m_specs;
+}
+
+void SndFileReader::read(int& length, bool& eos, sample_t* buffer)
+{
+ int olen = length;
+
+ length = sf_readf_float(m_sndfile, buffer, length);
+
+ m_position += length;
+
+ eos = length < olen;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/libsndfile/SndFileReader.h b/extern/audaspace/plugins/libsndfile/SndFileReader.h
new file mode 100644
index 00000000000..081c29c686c
--- /dev/null
+++ b/extern/audaspace/plugins/libsndfile/SndFileReader.h
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#include "IReader.h"
+
+#ifdef LIBSNDFILE_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file SndFileReader.h
+ * @ingroup plugin
+ * The SndFileReader class.
+ */
+
+#include <string>
+#include <sndfile.h>
+#include <memory>
+
+AUD_NAMESPACE_BEGIN
+
+class Buffer;
+
+/**
+ * This class reads a sound file via libsndfile.
+ */
+class AUD_PLUGIN_API SndFileReader : public IReader
+{
+private:
+ /**
+ * The current position in samples.
+ */
+ int m_position;
+
+ /**
+ * The sample count in the file.
+ */
+ int m_length;
+
+ /**
+ * Whether the file is seekable.
+ */
+ bool m_seekable;
+
+ /**
+ * The specification of the audio data.
+ */
+ Specs m_specs;
+
+ /**
+ * The sndfile.
+ */
+ SNDFILE* m_sndfile;
+
+ /**
+ * The virtual IO structure for memory file reading.
+ */
+ SF_VIRTUAL_IO m_vio;
+
+ /**
+ * The pointer to the memory file.
+ */
+ std::shared_ptr<Buffer> m_membuffer;
+
+ /**
+ * The current reading pointer of the memory file.
+ */
+ int m_memoffset;
+
+ // Functions for libsndfile virtual IO functionality
+ AUD_LOCAL static sf_count_t vio_get_filelen(void* user_data);
+ AUD_LOCAL static sf_count_t vio_seek(sf_count_t offset, int whence, void* user_data);
+ AUD_LOCAL static sf_count_t vio_read(void* ptr, sf_count_t count, void* user_data);
+ AUD_LOCAL static sf_count_t vio_tell(void* user_data);
+
+ // delete copy constructor and operator=
+ SndFileReader(const SndFileReader&) = delete;
+ SndFileReader& operator=(const SndFileReader&) = delete;
+
+public:
+ /**
+ * Creates a new reader.
+ * \param filename The path to the file to be read.
+ * \exception Exception Thrown if the file specified does not exist or
+ * cannot be read with libsndfile.
+ */
+ SndFileReader(std::string filename);
+
+ /**
+ * Creates a new reader.
+ * \param buffer The buffer to read from.
+ * \exception Exception Thrown if the buffer specified cannot be read
+ * with libsndfile.
+ */
+ SndFileReader(std::shared_ptr<Buffer> buffer);
+
+ /**
+ * Destroys the reader and closes the file.
+ */
+ virtual ~SndFileReader();
+
+ virtual bool isSeekable() const;
+ virtual void seek(int position);
+ virtual int getLength() const;
+ virtual int getPosition() const;
+ virtual Specs getSpecs() const;
+ virtual void read(int& length, bool& eos, sample_t* buffer);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/libsndfile/SndFileWriter.cpp b/extern/audaspace/plugins/libsndfile/SndFileWriter.cpp
new file mode 100644
index 00000000000..d2ab117132d
--- /dev/null
+++ b/extern/audaspace/plugins/libsndfile/SndFileWriter.cpp
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "SndFileWriter.h"
+#include "Exception.h"
+
+#include <cstring>
+
+AUD_NAMESPACE_BEGIN
+
+SndFileWriter::SndFileWriter(std::string filename, DeviceSpecs specs,
+ Container format, Codec codec, unsigned int bitrate) :
+ m_position(0), m_specs(specs)
+{
+ SF_INFO sfinfo;
+
+ sfinfo.channels = specs.channels;
+ sfinfo.samplerate = int(specs.rate);
+
+ switch(format)
+ {
+ case CONTAINER_FLAC:
+ sfinfo.format = SF_FORMAT_FLAC;
+ switch(specs.format)
+ {
+ case FORMAT_S16:
+ sfinfo.format |= SF_FORMAT_PCM_16;
+ break;
+ case FORMAT_S24:
+ sfinfo.format |= SF_FORMAT_PCM_24;
+ break;
+ case FORMAT_S32:
+ sfinfo.format |= SF_FORMAT_PCM_32;
+ break;
+ case FORMAT_FLOAT32:
+ sfinfo.format |= SF_FORMAT_FLOAT;
+ break;
+ case FORMAT_FLOAT64:
+ sfinfo.format |= SF_FORMAT_DOUBLE;
+ break;
+ default:
+ sfinfo.format = 0;
+ break;
+ }
+ break;
+ case CONTAINER_OGG:
+ if(codec == CODEC_VORBIS)
+ sfinfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
+ else
+ sfinfo.format = 0;
+ break;
+ case CONTAINER_WAV:
+ sfinfo.format = SF_FORMAT_WAV;
+ switch(specs.format)
+ {
+ case FORMAT_U8:
+ sfinfo.format |= SF_FORMAT_PCM_U8;
+ break;
+ case FORMAT_S16:
+ sfinfo.format |= SF_FORMAT_PCM_16;
+ break;
+ case FORMAT_S24:
+ sfinfo.format |= SF_FORMAT_PCM_24;
+ break;
+ case FORMAT_S32:
+ sfinfo.format |= SF_FORMAT_PCM_32;
+ break;
+ case FORMAT_FLOAT32:
+ sfinfo.format |= SF_FORMAT_FLOAT;
+ break;
+ case FORMAT_FLOAT64:
+ sfinfo.format |= SF_FORMAT_DOUBLE;
+ break;
+ default:
+ sfinfo.format = 0;
+ break;
+ }
+ break;
+ default:
+ sfinfo.format = 0;
+ break;
+ }
+
+ if(sfinfo.format == 0)
+ AUD_THROW(FileException, "This format couldn't be written with libsndfile.");
+
+ m_sndfile = sf_open(filename.c_str(), SFM_WRITE, &sfinfo);
+
+ if(!m_sndfile)
+ AUD_THROW(FileException, "The file couldn't be written with libsndfile.");
+}
+
+SndFileWriter::~SndFileWriter()
+{
+ sf_close(m_sndfile);
+}
+
+int SndFileWriter::getPosition() const
+{
+ return m_position;
+}
+
+DeviceSpecs SndFileWriter::getSpecs() const
+{
+ return m_specs;
+}
+
+void SndFileWriter::write(unsigned int length, sample_t* buffer)
+{
+ length = sf_writef_float(m_sndfile, buffer, length);
+
+ m_position += length;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/libsndfile/SndFileWriter.h b/extern/audaspace/plugins/libsndfile/SndFileWriter.h
new file mode 100644
index 00000000000..75d761f5163
--- /dev/null
+++ b/extern/audaspace/plugins/libsndfile/SndFileWriter.h
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef LIBSNDFILE_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file SndFileWriter.h
+ * @ingroup plugin
+ * The SndFileWriter class.
+ */
+
+#include "file/IWriter.h"
+
+#include <string>
+#include <sndfile.h>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This class writes a sound file via libsndfile.
+ */
+class AUD_PLUGIN_API SndFileWriter : public IWriter
+{
+private:
+ /**
+ * The current position in samples.
+ */
+ int m_position;
+
+ /**
+ * The specification of the audio data.
+ */
+ DeviceSpecs m_specs;
+
+ /**
+ * The sndfile.
+ */
+ SNDFILE* m_sndfile;
+
+ // delete copy constructor and operator=
+ SndFileWriter(const SndFileWriter&) = delete;
+ SndFileWriter& operator=(const SndFileWriter&) = delete;
+
+public:
+ /**
+ * Creates a new writer.
+ * \param filename The path to the file to be read.
+ * \param specs The file's audio specification.
+ * \param format The file's container format.
+ * \param codec The codec used for encoding the audio data.
+ * \param bitrate The bitrate for encoding.
+ * \exception Exception Thrown if the file specified cannot be written
+ * with libsndfile.
+ */
+ SndFileWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
+
+ /**
+ * Destroys the writer and closes the file.
+ */
+ virtual ~SndFileWriter();
+
+ virtual int getPosition() const;
+ virtual DeviceSpecs getSpecs() const;
+ virtual void write(unsigned int length, sample_t* buffer);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/openal/OpenALDevice.cpp b/extern/audaspace/plugins/openal/OpenALDevice.cpp
new file mode 100644
index 00000000000..2a609789a6d
--- /dev/null
+++ b/extern/audaspace/plugins/openal/OpenALDevice.cpp
@@ -0,0 +1,1490 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "OpenALDevice.h"
+#include "devices/DeviceManager.h"
+#include "devices/IDeviceFactory.h"
+#include "respec/ConverterReader.h"
+#include "Exception.h"
+#include "ISound.h"
+
+#include <chrono>
+#include <cstring>
+#include <iostream>
+
+AUD_NAMESPACE_BEGIN
+
+/******************************************************************************/
+/*********************** OpenALHandle Handle Code *************************/
+/******************************************************************************/
+
+bool OpenALDevice::OpenALHandle::pause(bool keep)
+{
+ if(m_status)
+ {
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(m_status == STATUS_PLAYING)
+ {
+ for(auto it = m_device->m_playingSounds.begin(); it != m_device->m_playingSounds.end(); it++)
+ {
+ if(it->get() == this)
+ {
+ std::shared_ptr<OpenALHandle> This = *it;
+
+ m_device->m_playingSounds.erase(it);
+ m_device->m_pausedSounds.push_back(This);
+
+ alSourcePause(m_source);
+
+ m_status = keep ? STATUS_STOPPED : STATUS_PAUSED;
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+OpenALDevice::OpenALHandle::OpenALHandle(OpenALDevice* device, ALenum format, std::shared_ptr<IReader> reader, bool keep) :
+ m_isBuffered(false), m_reader(reader), m_keep(keep), m_format(format),
+ m_eos(false), m_loopcount(0), m_stop(nullptr), m_stop_data(nullptr), m_status(STATUS_PLAYING),
+ m_device(device)
+{
+ DeviceSpecs specs = m_device->m_specs;
+ specs.specs = m_reader->getSpecs();
+
+ // OpenAL playback code
+ alGenBuffers(CYCLE_BUFFERS, m_buffers);
+ if(alGetError() != AL_NO_ERROR)
+ AUD_THROW(DeviceException, "Buffer generation failed while staring playback with OpenAL.");
+
+ try
+ {
+ m_device->m_buffer.assureSize(m_device->m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));
+ int length;
+ bool eos;
+
+ for(m_current = 0; m_current < CYCLE_BUFFERS; m_current++)
+ {
+ length = m_device->m_buffersize;
+ reader->read(length, eos, m_device->m_buffer.getBuffer());
+
+ if(length == 0)
+ break;
+
+ alBufferData(m_buffers[m_current], m_format, m_device->m_buffer.getBuffer(), length * AUD_DEVICE_SAMPLE_SIZE(specs), specs.rate);
+
+ if(alGetError() != AL_NO_ERROR)
+ AUD_THROW(DeviceException, "Filling the buffer with data failed while starting playback with OpenAL.");
+ }
+
+ alGenSources(1, &m_source);
+ if(alGetError() != AL_NO_ERROR)
+ AUD_THROW(DeviceException, "Source generation failed while starting playback with OpenAL.");
+
+ try
+ {
+ alSourceQueueBuffers(m_source, m_current, m_buffers);
+ if(alGetError() != AL_NO_ERROR)
+ AUD_THROW(DeviceException, "Buffer queuing failed while starting playback with OpenAL.");
+ }
+ catch(Exception&)
+ {
+ alDeleteSources(1, &m_source);
+ throw;
+ }
+ }
+ catch(Exception&)
+ {
+ alDeleteBuffers(CYCLE_BUFFERS, m_buffers);
+ throw;
+ }
+ alSourcei(m_source, AL_SOURCE_RELATIVE, 1);
+}
+
+bool OpenALDevice::OpenALHandle::pause()
+{
+ return pause(false);
+}
+
+bool OpenALDevice::OpenALHandle::resume()
+{
+ if(m_status)
+ {
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(m_status == STATUS_PAUSED)
+ {
+ for(auto it = m_device->m_pausedSounds.begin(); it != m_device->m_pausedSounds.end(); it++)
+ {
+ if(it->get() == this)
+ {
+ std::shared_ptr<OpenALHandle> This = *it;
+
+ m_device->m_pausedSounds.erase(it);
+ m_device->m_playingSounds.push_back(This);
+
+ m_device->start();
+ m_status = STATUS_PLAYING;
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool OpenALDevice::OpenALHandle::stop()
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ m_status = STATUS_INVALID;
+
+ alDeleteSources(1, &m_source);
+ if(!m_isBuffered)
+ alDeleteBuffers(CYCLE_BUFFERS, m_buffers);
+
+ for(auto it = m_device->m_playingSounds.begin(); it != m_device->m_playingSounds.end(); it++)
+ {
+ if(it->get() == this)
+ {
+ std::shared_ptr<OpenALHandle> This = *it;
+
+ m_device->m_playingSounds.erase(it);
+
+ return true;
+ }
+ }
+
+ for(auto it = m_device->m_pausedSounds.begin(); it != m_device->m_pausedSounds.end(); it++)
+ {
+ if(it->get() == this)
+ {
+ std::shared_ptr<OpenALHandle> This = *it;
+
+ m_device->m_pausedSounds.erase(it);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool OpenALDevice::OpenALHandle::getKeep()
+{
+ if(m_status)
+ return m_keep;
+
+ return false;
+}
+
+bool OpenALDevice::OpenALHandle::setKeep(bool keep)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ m_keep = keep;
+
+ return true;
+}
+
+bool OpenALDevice::OpenALHandle::seek(float position)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(m_isBuffered)
+ alSourcef(m_source, AL_SEC_OFFSET, position);
+ else
+ {
+ m_reader->seek((int)(position * m_reader->getSpecs().rate));
+ m_eos = false;
+
+ ALint info;
+
+ alGetSourcei(m_source, AL_SOURCE_STATE, &info);
+
+ // we need to stop playing sounds as well to clear the buffers
+ // this might cause clicks, but fixes a bug regarding position determination
+ if(info == AL_PAUSED || info == AL_PLAYING)
+ alSourceStop(m_source);
+
+ alSourcei(m_source, AL_BUFFER, 0);
+
+ ALenum err;
+ if((err = alGetError()) == AL_NO_ERROR)
+ {
+ int length;
+ DeviceSpecs specs = m_device->m_specs;
+ specs.specs = m_reader->getSpecs();
+ m_device->m_buffer.assureSize(m_device->m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));
+
+ for(m_current = 0; m_current < CYCLE_BUFFERS; m_current++)
+ {
+ length = m_device->m_buffersize;
+
+ m_reader->read(length, m_eos, m_device->m_buffer.getBuffer());
+
+ if(length == 0)
+ break;
+
+ alBufferData(m_buffers[m_current], m_format, m_device->m_buffer.getBuffer(), length * AUD_DEVICE_SAMPLE_SIZE(specs), specs.rate);
+
+ if(alGetError() != AL_NO_ERROR)
+ break;
+ }
+
+ if(m_loopcount != 0)
+ m_eos = false;
+
+ alSourceQueueBuffers(m_source, m_current, m_buffers);
+ }
+
+ alSourceRewind(m_source);
+ }
+
+ if(m_status == STATUS_STOPPED)
+ m_status = STATUS_PAUSED;
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getPosition()
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return 0.0f;
+
+ float position = 0.0f;
+
+ alGetSourcef(m_source, AL_SEC_OFFSET, &position);
+
+ if(!m_isBuffered)
+ {
+ int queued;
+
+ // this usually always returns CYCLE_BUFFERS
+ alGetSourcei(m_source, AL_BUFFERS_QUEUED, &queued);
+
+ Specs specs = m_reader->getSpecs();
+ position += (m_reader->getPosition() - m_device->m_buffersize * queued) / (float)specs.rate;
+ }
+
+ return position;
+}
+
+Status OpenALDevice::OpenALHandle::getStatus()
+{
+ return m_status;
+}
+
+float OpenALDevice::OpenALHandle::getVolume()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_GAIN, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setVolume(float volume)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(volume >= 0.0f)
+ alSourcef(m_source, AL_GAIN, volume);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getPitch()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_PITCH, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setPitch(float pitch)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(pitch > 0.0f)
+ alSourcef(m_source, AL_PITCH, pitch);
+
+ return true;
+}
+
+int OpenALDevice::OpenALHandle::getLoopCount()
+{
+ if(!m_status)
+ return 0;
+ return m_loopcount;
+}
+
+bool OpenALDevice::OpenALHandle::setLoopCount(int count)
+{
+ if(!m_status)
+ return false;
+
+ if(m_status == STATUS_STOPPED && (count > m_loopcount || count < 0))
+ m_status = STATUS_PAUSED;
+
+ m_loopcount = count;
+
+ return true;
+}
+
+bool OpenALDevice::OpenALHandle::setStopCallback(stopCallback callback, void* data)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ m_stop = callback;
+ m_stop_data = data;
+
+ return true;
+}
+
+/******************************************************************************/
+/********************* OpenALHandle 3DHandle Code *************************/
+/******************************************************************************/
+
+Vector3 OpenALDevice::OpenALHandle::getLocation()
+{
+ Vector3 result = Vector3(0, 0, 0);
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ ALfloat p[3];
+ alGetSourcefv(m_source, AL_POSITION, p);
+
+ result = Vector3(p[0], p[1], p[2]);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setLocation(const Vector3& location)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ alSourcefv(m_source, AL_POSITION, (ALfloat*)location.get());
+
+ return true;
+}
+
+Vector3 OpenALDevice::OpenALHandle::getVelocity()
+{
+ Vector3 result = Vector3(0, 0, 0);
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ ALfloat v[3];
+ alGetSourcefv(m_source, AL_VELOCITY, v);
+
+ result = Vector3(v[0], v[1], v[2]);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setVelocity(const Vector3& velocity)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ alSourcefv(m_source, AL_VELOCITY, (ALfloat*)velocity.get());
+
+ return true;
+}
+
+Quaternion OpenALDevice::OpenALHandle::getOrientation()
+{
+ return m_orientation;
+}
+
+bool OpenALDevice::OpenALHandle::setOrientation(const Quaternion& orientation)
+{
+ ALfloat direction[3];
+ direction[0] = -2 * (orientation.w() * orientation.y() +
+ orientation.x() * orientation.z());
+ direction[1] = 2 * (orientation.x() * orientation.w() -
+ orientation.z() * orientation.y());
+ direction[2] = 2 * (orientation.x() * orientation.x() +
+ orientation.y() * orientation.y()) - 1;
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ alSourcefv(m_source, AL_DIRECTION, direction);
+
+ m_orientation = orientation;
+
+ return true;
+}
+
+bool OpenALDevice::OpenALHandle::isRelative()
+{
+ int result;
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ alGetSourcei(m_source, AL_SOURCE_RELATIVE, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setRelative(bool relative)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ alSourcei(m_source, AL_SOURCE_RELATIVE, relative);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getVolumeMaximum()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_MAX_GAIN, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setVolumeMaximum(float volume)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(volume >= 0.0f && volume <= 1.0f)
+ alSourcef(m_source, AL_MAX_GAIN, volume);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getVolumeMinimum()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_MIN_GAIN, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setVolumeMinimum(float volume)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(volume >= 0.0f && volume <= 1.0f)
+ alSourcef(m_source, AL_MIN_GAIN, volume);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getDistanceMaximum()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_MAX_DISTANCE, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setDistanceMaximum(float distance)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(distance >= 0.0f)
+ alSourcef(m_source, AL_MAX_DISTANCE, distance);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getDistanceReference()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_REFERENCE_DISTANCE, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setDistanceReference(float distance)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(distance >= 0.0f)
+ alSourcef(m_source, AL_REFERENCE_DISTANCE, distance);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getAttenuation()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_ROLLOFF_FACTOR, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setAttenuation(float factor)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(factor >= 0.0f)
+ alSourcef(m_source, AL_ROLLOFF_FACTOR, factor);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getConeAngleOuter()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_CONE_OUTER_ANGLE, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setConeAngleOuter(float angle)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ alSourcef(m_source, AL_CONE_OUTER_ANGLE, angle);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getConeAngleInner()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_CONE_INNER_ANGLE, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setConeAngleInner(float angle)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ alSourcef(m_source, AL_CONE_INNER_ANGLE, angle);
+
+ return true;
+}
+
+float OpenALDevice::OpenALHandle::getConeVolumeOuter()
+{
+ float result = std::numeric_limits<float>::quiet_NaN();
+
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return result;
+
+ alGetSourcef(m_source, AL_CONE_OUTER_GAIN, &result);
+
+ return result;
+}
+
+bool OpenALDevice::OpenALHandle::setConeVolumeOuter(float volume)
+{
+ if(!m_status)
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_device);
+
+ if(!m_status)
+ return false;
+
+ if(volume >= 0.0f && volume <= 1.0f)
+ alSourcef(m_source, AL_CONE_OUTER_GAIN, volume);
+
+ return true;
+}
+
+/******************************************************************************/
+/**************************** Threading Code **********************************/
+/******************************************************************************/
+
+void OpenALDevice::start()
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ if(!m_playing)
+ {
+ if(m_thread.joinable())
+ m_thread.join();
+
+ m_thread = std::thread(&OpenALDevice::updateStreams, this);
+
+ m_playing = true;
+ }
+}
+
+void OpenALDevice::updateStreams()
+{
+ int length;
+
+ ALint info;
+ DeviceSpecs specs = m_specs;
+ ALCenum cerr;
+ std::list<std::shared_ptr<OpenALHandle> > stopSounds;
+ std::list<std::shared_ptr<OpenALHandle> > pauseSounds;
+
+ auto sleepDuration = std::chrono::milliseconds(20);
+
+ for(;;)
+ {
+ lock();
+
+ alcSuspendContext(m_context);
+ cerr = alcGetError(m_device);
+ if(cerr == ALC_NO_ERROR)
+ {
+ // for all sounds
+ for(auto& sound : m_playingSounds)
+ {
+ // is it a streamed sound?
+ if(!sound->m_isBuffered)
+ {
+ // check for buffer refilling
+ alGetSourcei(sound->m_source, AL_BUFFERS_PROCESSED, &info);
+
+ info += (OpenALHandle::CYCLE_BUFFERS - sound->m_current);
+
+ if(info)
+ {
+ specs.specs = sound->m_reader->getSpecs();
+ m_buffer.assureSize(m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));
+
+ // for all empty buffers
+ while(info--)
+ {
+ // if there's still data to play back
+ if(!sound->m_eos)
+ {
+ // read data
+ length = m_buffersize;
+
+ try
+ {
+ sound->m_reader->read(length, sound->m_eos, m_buffer.getBuffer());
+
+ // looping necessary?
+ if(length == 0 && sound->m_loopcount)
+ {
+ if(sound->m_loopcount > 0)
+ sound->m_loopcount--;
+
+ sound->m_reader->seek(0);
+
+ length = m_buffersize;
+ sound->m_reader->read(length, sound->m_eos, m_buffer.getBuffer());
+ }
+ }
+ catch(Exception& e)
+ {
+ length = 0;
+ std::cerr << "Caught exception while reading sound data during playback with OpenAL: " << e.getMessage() << std::endl;
+ }
+
+ if(sound->m_loopcount != 0)
+ sound->m_eos = false;
+
+ // read nothing?
+ if(length == 0)
+ {
+ break;
+ }
+
+ ALuint buffer;
+
+ if(sound->m_current < OpenALHandle::CYCLE_BUFFERS)
+ buffer = sound->m_buffers[sound->m_current++];
+ else
+ alSourceUnqueueBuffers(sound->m_source, 1, &buffer);
+
+ ALenum err;
+ if((err = alGetError()) != AL_NO_ERROR)
+ {
+ sound->m_eos = true;
+ break;
+ }
+
+ // fill with new data
+ alBufferData(buffer, sound->m_format, m_buffer.getBuffer(), length * AUD_DEVICE_SAMPLE_SIZE(specs), specs.rate);
+
+ if((err = alGetError()) != AL_NO_ERROR)
+ {
+ sound->m_eos = true;
+ break;
+ }
+
+ // and queue again
+ alSourceQueueBuffers(sound->m_source, 1,&buffer);
+ if(alGetError() != AL_NO_ERROR)
+ {
+ sound->m_eos = true;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ // check if the sound has been stopped
+ alGetSourcei(sound->m_source, AL_SOURCE_STATE, &info);
+
+ if(info != AL_PLAYING)
+ {
+ // if it really stopped
+ if(sound->m_eos && info != AL_INITIAL)
+ {
+ if(sound->m_stop)
+ sound->m_stop(sound->m_stop_data);
+
+ // pause or
+ if(sound->m_keep)
+ pauseSounds.push_back(sound);
+ // stop
+ else
+ stopSounds.push_back(sound);
+ }
+ // continue playing
+ else
+ alSourcePlay(sound->m_source);
+ }
+ }
+
+ for(auto& sound : pauseSounds)
+ sound->pause(true);
+
+ for(auto& sound : stopSounds)
+ sound->stop();
+
+ pauseSounds.clear();
+ stopSounds.clear();
+
+ alcProcessContext(m_context);
+ }
+
+ // stop thread
+ if(m_playingSounds.empty() || (cerr != ALC_NO_ERROR))
+ {
+ m_playing = false;
+ unlock();
+
+ return;
+ }
+
+ unlock();
+
+ std::this_thread::sleep_for(sleepDuration);
+ }
+}
+
+/******************************************************************************/
+/**************************** IDevice Code ************************************/
+/******************************************************************************/
+
+OpenALDevice::OpenALDevice(DeviceSpecs specs, int buffersize, std::string name) :
+ m_playing(false), m_buffersize(buffersize)
+{
+ // cannot determine how many channels or which format OpenAL uses, but
+ // it at least is able to play 16 bit stereo audio
+ specs.format = FORMAT_S16;
+
+ if(name.empty())
+ m_device = alcOpenDevice(nullptr);
+ else
+ m_device = alcOpenDevice(name.c_str());
+
+ if(!m_device)
+ AUD_THROW(DeviceException, "The audio device couldn't be opened with OpenAL.");
+
+ // at least try to set the frequency
+ ALCint attribs[] = { ALC_FREQUENCY, (ALCint)specs.rate, 0 };
+ ALCint* attributes = attribs;
+ if(specs.rate == RATE_INVALID)
+ attributes = nullptr;
+
+ m_context = alcCreateContext(m_device, attributes);
+ alcMakeContextCurrent(m_context);
+
+ alcGetIntegerv(m_device, ALC_FREQUENCY, 1, (ALCint*)&specs.rate);
+
+ // check for specific formats and channel counts to be played back
+ if(alIsExtensionPresent("AL_EXT_FLOAT32") == AL_TRUE)
+ specs.format = FORMAT_FLOAT32;
+
+ m_useMC = alIsExtensionPresent("AL_EXT_MCFORMATS") == AL_TRUE;
+
+ if((!m_useMC && specs.channels > CHANNELS_STEREO) ||
+ specs.channels == CHANNELS_STEREO_LFE ||
+ specs.channels == CHANNELS_SURROUND5)
+ specs.channels = CHANNELS_STEREO;
+
+ alGetError();
+ alcGetError(m_device);
+
+ m_specs = specs;
+}
+
+OpenALDevice::~OpenALDevice()
+{
+ lock();
+ alcSuspendContext(m_context);
+
+ while(!m_playingSounds.empty())
+ m_playingSounds.front()->stop();
+
+ while(!m_pausedSounds.empty())
+ m_pausedSounds.front()->stop();
+
+ alcProcessContext(m_context);
+
+ // wait for the thread to stop
+ unlock();
+ if(m_thread.joinable())
+ m_thread.join();
+
+ // quit OpenAL
+ alcMakeContextCurrent(nullptr);
+ alcDestroyContext(m_context);
+ alcCloseDevice(m_device);
+}
+
+DeviceSpecs OpenALDevice::getSpecs() const
+{
+ return m_specs;
+}
+
+bool OpenALDevice::getFormat(ALenum &format, Specs specs)
+{
+ bool valid = true;
+ format = 0;
+
+ switch(m_specs.format)
+ {
+ case FORMAT_S16:
+ switch(specs.channels)
+ {
+ case CHANNELS_MONO:
+ format = AL_FORMAT_MONO16;
+ break;
+ case CHANNELS_STEREO:
+ format = AL_FORMAT_STEREO16;
+ break;
+ case CHANNELS_SURROUND4:
+ if(m_useMC)
+ {
+ format = alGetEnumValue("AL_FORMAT_QUAD16");
+ break;
+ }
+ case CHANNELS_SURROUND51:
+ if(m_useMC)
+ {
+ format = alGetEnumValue("AL_FORMAT_51CHN16");
+ break;
+ }
+ case CHANNELS_SURROUND61:
+ if(m_useMC)
+ {
+ format = alGetEnumValue("AL_FORMAT_61CHN16");
+ break;
+ }
+ case CHANNELS_SURROUND71:
+ if(m_useMC)
+ {
+ format = alGetEnumValue("AL_FORMAT_71CHN16");
+ break;
+ }
+ default:
+ valid = false;
+ }
+ break;
+ case FORMAT_FLOAT32:
+ switch(specs.channels)
+ {
+ case CHANNELS_MONO:
+ format = alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
+ break;
+ case CHANNELS_STEREO:
+ format = alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
+ break;
+ case CHANNELS_SURROUND4:
+ if(m_useMC)
+ {
+ format = alGetEnumValue("AL_FORMAT_QUAD32");
+ break;
+ }
+ case CHANNELS_SURROUND51:
+ if(m_useMC)
+ {
+ format = alGetEnumValue("AL_FORMAT_51CHN32");
+ break;
+ }
+ case CHANNELS_SURROUND61:
+ if(m_useMC)
+ {
+ format = alGetEnumValue("AL_FORMAT_61CHN32");
+ break;
+ }
+ case CHANNELS_SURROUND71:
+ if(m_useMC)
+ {
+ format = alGetEnumValue("AL_FORMAT_71CHN32");
+ break;
+ }
+ default:
+ valid = false;
+ }
+ break;
+ default:
+ valid = false;
+ }
+
+ if(!format)
+ valid = false;
+
+ return valid;
+}
+
+std::shared_ptr<IHandle> OpenALDevice::play(std::shared_ptr<IReader> reader, bool keep)
+{
+ Specs specs = reader->getSpecs();
+
+ // check format
+ if(specs.channels == CHANNELS_INVALID)
+ return std::shared_ptr<IHandle>();
+
+ if(m_specs.format != FORMAT_FLOAT32)
+ reader = std::shared_ptr<IReader>(new ConverterReader(reader, m_specs));
+
+ ALenum format;
+
+ if(!getFormat(format, specs))
+ return std::shared_ptr<IHandle>();
+
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ alcSuspendContext(m_context);
+
+ std::shared_ptr<OpenALDevice::OpenALHandle> sound;
+
+ try
+ {
+ // create the handle
+ sound = std::shared_ptr<OpenALDevice::OpenALHandle>(new OpenALDevice::OpenALHandle(this, format, reader, keep));
+ }
+ catch(Exception&)
+ {
+ alcProcessContext(m_context);
+ throw;
+ }
+
+ alcProcessContext(m_context);
+
+ // play sound
+ m_playingSounds.push_back(sound);
+
+ start();
+
+ return std::shared_ptr<IHandle>(sound);
+}
+
+std::shared_ptr<IHandle> OpenALDevice::play(std::shared_ptr<ISound> sound, bool keep)
+{
+ return play(sound->createReader(), keep);
+}
+
+void OpenALDevice::stopAll()
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ alcSuspendContext(m_context);
+
+ while(!m_playingSounds.empty())
+ m_playingSounds.front()->stop();
+
+ while(!m_pausedSounds.empty())
+ m_pausedSounds.front()->stop();
+
+ alcProcessContext(m_context);
+}
+
+void OpenALDevice::lock()
+{
+ m_mutex.lock();
+}
+
+void OpenALDevice::unlock()
+{
+ m_mutex.unlock();
+}
+
+float OpenALDevice::getVolume() const
+{
+ float result;
+
+ alGetListenerf(AL_GAIN, &result);
+ return result;
+}
+
+void OpenALDevice::setVolume(float volume)
+{
+ if(volume < 0.0f)
+ return;
+
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ alListenerf(AL_GAIN, volume);
+}
+
+ISynchronizer* OpenALDevice::getSynchronizer()
+{
+ return &m_synchronizer;
+}
+
+/******************************************************************************/
+/**************************** 3D Device Code **********************************/
+/******************************************************************************/
+
+Vector3 OpenALDevice::getListenerLocation() const
+{
+ ALfloat p[3];
+
+ alGetListenerfv(AL_POSITION, p);
+ return Vector3(p[0], p[1], p[2]);
+}
+
+void OpenALDevice::setListenerLocation(const Vector3& location)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ alListenerfv(AL_POSITION, (ALfloat*)location.get());
+}
+
+Vector3 OpenALDevice::getListenerVelocity() const
+{
+ ALfloat v[3];
+
+ alGetListenerfv(AL_VELOCITY, v);
+ return Vector3(v[0], v[1], v[2]);
+}
+
+void OpenALDevice::setListenerVelocity(const Vector3& velocity)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ alListenerfv(AL_VELOCITY, (ALfloat*)velocity.get());
+}
+
+Quaternion OpenALDevice::getListenerOrientation() const
+{
+ return m_orientation;
+}
+
+void OpenALDevice::setListenerOrientation(const Quaternion& orientation)
+{
+ ALfloat direction[6];
+
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ direction[0] = -2 * (orientation.w() * orientation.y() +
+ orientation.x() * orientation.z());
+ direction[1] = 2 * (orientation.x() * orientation.w() -
+ orientation.z() * orientation.y());
+ direction[2] = 2 * (orientation.x() * orientation.x() +
+ orientation.y() * orientation.y()) - 1;
+ direction[3] = 2 * (orientation.x() * orientation.y() -
+ orientation.w() * orientation.z());
+ direction[4] = 1 - 2 * (orientation.x() * orientation.x() +
+ orientation.z() * orientation.z());
+ direction[5] = 2 * (orientation.w() * orientation.x() +
+ orientation.y() * orientation.z());
+ alListenerfv(AL_ORIENTATION, direction);
+ m_orientation = orientation;
+}
+
+float OpenALDevice::getSpeedOfSound() const
+{
+ return alGetFloat(AL_SPEED_OF_SOUND);
+}
+
+void OpenALDevice::setSpeedOfSound(float speed)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ alSpeedOfSound(speed);
+}
+
+float OpenALDevice::getDopplerFactor() const
+{
+ return alGetFloat(AL_DOPPLER_FACTOR);
+}
+
+void OpenALDevice::setDopplerFactor(float factor)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ alDopplerFactor(factor);
+}
+
+DistanceModel OpenALDevice::getDistanceModel() const
+{
+ switch(alGetInteger(AL_DISTANCE_MODEL))
+ {
+ case AL_INVERSE_DISTANCE:
+ return DISTANCE_MODEL_INVERSE;
+ case AL_INVERSE_DISTANCE_CLAMPED:
+ return DISTANCE_MODEL_INVERSE_CLAMPED;
+ case AL_LINEAR_DISTANCE:
+ return DISTANCE_MODEL_LINEAR;
+ case AL_LINEAR_DISTANCE_CLAMPED:
+ return DISTANCE_MODEL_LINEAR_CLAMPED;
+ case AL_EXPONENT_DISTANCE:
+ return DISTANCE_MODEL_EXPONENT;
+ case AL_EXPONENT_DISTANCE_CLAMPED:
+ return DISTANCE_MODEL_EXPONENT_CLAMPED;
+ default:
+ return DISTANCE_MODEL_INVALID;
+ }
+}
+
+void OpenALDevice::setDistanceModel(DistanceModel model)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ switch(model)
+ {
+ case DISTANCE_MODEL_INVERSE:
+ alDistanceModel(AL_INVERSE_DISTANCE);
+ break;
+ case DISTANCE_MODEL_INVERSE_CLAMPED:
+ alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
+ break;
+ case DISTANCE_MODEL_LINEAR:
+ alDistanceModel(AL_LINEAR_DISTANCE);
+ break;
+ case DISTANCE_MODEL_LINEAR_CLAMPED:
+ alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
+ break;
+ case DISTANCE_MODEL_EXPONENT:
+ alDistanceModel(AL_EXPONENT_DISTANCE);
+ break;
+ case DISTANCE_MODEL_EXPONENT_CLAMPED:
+ alDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);
+ break;
+ default:
+ alDistanceModel(AL_NONE);
+ }
+}
+
+std::list<std::string> OpenALDevice::getDeviceNames()
+{
+ std::list<std::string> names;
+
+ if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") == AL_TRUE)
+ {
+ ALCchar* devices = const_cast<ALCchar*>(alcGetString(nullptr, ALC_DEVICE_SPECIFIER));
+ std::string default_device = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
+
+ while(*devices)
+ {
+ std::string device = devices;
+
+ if(device == default_device)
+ names.push_front(device);
+ else
+ names.push_back(device);
+
+ devices += strlen(devices) + 1;
+ }
+ }
+
+ return names;
+}
+
+class OpenALDeviceFactory : public IDeviceFactory
+{
+private:
+ DeviceSpecs m_specs;
+ int m_buffersize;
+ std::string m_name;
+
+public:
+ OpenALDeviceFactory(std::string name = "") :
+ m_buffersize(AUD_DEFAULT_BUFFER_SIZE),
+ m_name(name)
+ {
+ m_specs.format = FORMAT_FLOAT32;
+ m_specs.channels = CHANNELS_SURROUND51;
+ m_specs.rate = RATE_48000;
+ }
+
+ virtual std::shared_ptr<IDevice> openDevice()
+ {
+ return std::shared_ptr<IDevice>(new OpenALDevice(m_specs, m_buffersize, m_name));
+ }
+
+ virtual int getPriority()
+ {
+ return 1 << 10;
+ }
+
+ virtual void setSpecs(DeviceSpecs specs)
+ {
+ m_specs = specs;
+ }
+
+ virtual void setBufferSize(int buffersize)
+ {
+ m_buffersize = buffersize;
+ }
+
+ virtual void setName(std::string name)
+ {
+ }
+};
+
+void OpenALDevice::registerPlugin()
+{
+ auto names = OpenALDevice::getDeviceNames();
+ DeviceManager::registerDevice("OpenAL", std::shared_ptr<IDeviceFactory>(new OpenALDeviceFactory));
+ for(std::string &name : names)
+ {
+ DeviceManager::registerDevice("OpenAL - " + name, std::shared_ptr<IDeviceFactory>(new OpenALDeviceFactory(name)));
+ }
+}
+
+#ifdef OPENAL_PLUGIN
+extern "C" AUD_PLUGIN_API void registerPlugin()
+{
+ OpenALDevice::registerPlugin();
+}
+
+extern "C" AUD_PLUGIN_API const char* getName()
+{
+ return "OpenAL";
+}
+#endif
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/openal/OpenALDevice.h b/extern/audaspace/plugins/openal/OpenALDevice.h
new file mode 100644
index 00000000000..b9b461a327c
--- /dev/null
+++ b/extern/audaspace/plugins/openal/OpenALDevice.h
@@ -0,0 +1,296 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef OPENAL_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file OpenALDevice.h
+ * @ingroup plugin
+ * The OpenALDevice class.
+ */
+
+#include "devices/IDevice.h"
+#include "devices/IHandle.h"
+#include "devices/I3DDevice.h"
+#include "devices/I3DHandle.h"
+#include "devices/DefaultSynchronizer.h"
+#include "util/Buffer.h"
+
+#include <al.h>
+#include <alc.h>
+#include <list>
+#include <mutex>
+#include <thread>
+#include <string>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This device plays through OpenAL.
+ */
+class AUD_PLUGIN_API OpenALDevice : public IDevice, public I3DDevice
+{
+private:
+ /// Saves the data for playback.
+ class OpenALHandle : public IHandle, public I3DHandle
+ {
+ private:
+ friend class OpenALDevice;
+
+ static const int CYCLE_BUFFERS = 3;
+
+ /// Whether it's a buffered or a streamed source.
+ bool m_isBuffered;
+
+ /// The reader source.
+ std::shared_ptr<IReader> m_reader;
+
+ /// Whether to keep the source if end of it is reached.
+ bool m_keep;
+
+ /// OpenAL sample format.
+ ALenum m_format;
+
+ /// OpenAL source.
+ ALuint m_source;
+
+ /// OpenAL buffers.
+ ALuint m_buffers[CYCLE_BUFFERS];
+
+ /// The first buffer to be read next.
+ int m_current;
+
+ /// Whether the stream doesn't return any more data.
+ bool m_eos;
+
+ /// The loop count of the source.
+ int m_loopcount;
+
+ /// The stop callback.
+ stopCallback m_stop;
+
+ /// Stop callback data.
+ void* m_stop_data;
+
+ /// Orientation.
+ Quaternion m_orientation;
+
+ /// Current status of the handle
+ Status m_status;
+
+ /// Own device.
+ OpenALDevice* m_device;
+
+ AUD_LOCAL bool pause(bool keep);
+
+ // delete copy constructor and operator=
+ OpenALHandle(const OpenALHandle&) = delete;
+ OpenALHandle& operator=(const OpenALHandle&) = delete;
+
+ public:
+
+ /**
+ * Creates a new OpenAL handle.
+ * \param device The OpenAL device the handle belongs to.
+ * \param format The AL format.
+ * \param reader The reader this handle plays.
+ * \param keep Whether to keep the handle alive when the reader ends.
+ */
+ OpenALHandle(OpenALDevice* device, ALenum format, std::shared_ptr<IReader> reader, bool keep);
+
+ virtual ~OpenALHandle() {}
+ virtual bool pause();
+ virtual bool resume();
+ virtual bool stop();
+ virtual bool getKeep();
+ virtual bool setKeep(bool keep);
+ virtual bool seek(float position);
+ virtual float getPosition();
+ virtual Status getStatus();
+ virtual float getVolume();
+ virtual bool setVolume(float volume);
+ virtual float getPitch();
+ virtual bool setPitch(float pitch);
+ virtual int getLoopCount();
+ virtual bool setLoopCount(int count);
+ virtual bool setStopCallback(stopCallback callback = 0, void* data = 0);
+
+ virtual Vector3 getLocation();
+ virtual bool setLocation(const Vector3& location);
+ virtual Vector3 getVelocity();
+ virtual bool setVelocity(const Vector3& velocity);
+ virtual Quaternion getOrientation();
+ virtual bool setOrientation(const Quaternion& orientation);
+ virtual bool isRelative();
+ virtual bool setRelative(bool relative);
+ virtual float getVolumeMaximum();
+ virtual bool setVolumeMaximum(float volume);
+ virtual float getVolumeMinimum();
+ virtual bool setVolumeMinimum(float volume);
+ virtual float getDistanceMaximum();
+ virtual bool setDistanceMaximum(float distance);
+ virtual float getDistanceReference();
+ virtual bool setDistanceReference(float distance);
+ virtual float getAttenuation();
+ virtual bool setAttenuation(float factor);
+ virtual float getConeAngleOuter();
+ virtual bool setConeAngleOuter(float angle);
+ virtual float getConeAngleInner();
+ virtual bool setConeAngleInner(float angle);
+ virtual float getConeVolumeOuter();
+ virtual bool setConeVolumeOuter(float volume);
+ };
+
+ /**
+ * The OpenAL device handle.
+ */
+ ALCdevice* m_device;
+
+ /**
+ * The OpenAL context.
+ */
+ ALCcontext* m_context;
+
+ /**
+ * The specification of the device.
+ */
+ DeviceSpecs m_specs;
+
+ /**
+ * Whether the device has the AL_EXT_MCFORMATS extension.
+ */
+ bool m_useMC;
+
+ /**
+ * The list of sounds that are currently playing.
+ */
+ std::list<std::shared_ptr<OpenALHandle> > m_playingSounds;
+
+ /**
+ * The list of sounds that are currently paused.
+ */
+ std::list<std::shared_ptr<OpenALHandle> > m_pausedSounds;
+
+ /**
+ * The mutex for locking.
+ */
+ std::recursive_mutex m_mutex;
+
+ /**
+ * The streaming thread.
+ */
+ std::thread m_thread;
+
+ /**
+ * The condition for streaming thread wakeup.
+ */
+ bool m_playing;
+
+ /**
+ * Buffer size.
+ */
+ int m_buffersize;
+
+ /**
+ * Device buffer.
+ */
+ Buffer m_buffer;
+
+ /**
+ * Orientation.
+ */
+ Quaternion m_orientation;
+
+ /// Synchronizer.
+ DefaultSynchronizer m_synchronizer;
+
+ /**
+ * Starts the streaming thread.
+ * \param Whether the previous thread should be joined.
+ */
+ AUD_LOCAL void start();
+
+ /**
+ * Streaming thread main function.
+ */
+ AUD_LOCAL void updateStreams();
+
+ /**
+ * Gets the format according to the specs.
+ * \param format The variable to put the format into.
+ * \param specs The specs to read the channel count from.
+ * \return Whether the format is valid or not.
+ */
+ AUD_LOCAL bool getFormat(ALenum &format, Specs specs);
+
+ // delete copy constructor and operator=
+ OpenALDevice(const OpenALDevice&) = delete;
+ OpenALDevice& operator=(const OpenALDevice&) = delete;
+
+public:
+ /**
+ * Opens the OpenAL audio device for playback.
+ * \param specs The wanted audio specification.
+ * \param buffersize The size of the internal buffer.
+ * \param name The name of the device to be opened.
+ * \note The specification really used for opening the device may differ.
+ * \note The buffersize will be multiplicated by three for this device.
+ * \exception DeviceException Thrown if the audio device cannot be opened.
+ */
+ OpenALDevice(DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE, std::string name = "");
+
+ virtual ~OpenALDevice();
+
+ virtual DeviceSpecs getSpecs() const;
+ virtual std::shared_ptr<IHandle> play(std::shared_ptr<IReader> reader, bool keep = false);
+ virtual std::shared_ptr<IHandle> play(std::shared_ptr<ISound> sound, bool keep = false);
+ virtual void stopAll();
+ virtual void lock();
+ virtual void unlock();
+ virtual float getVolume() const;
+ virtual void setVolume(float volume);
+ virtual ISynchronizer* getSynchronizer();
+
+ virtual Vector3 getListenerLocation() const;
+ virtual void setListenerLocation(const Vector3& location);
+ virtual Vector3 getListenerVelocity() const;
+ virtual void setListenerVelocity(const Vector3& velocity);
+ virtual Quaternion getListenerOrientation() const;
+ virtual void setListenerOrientation(const Quaternion& orientation);
+ virtual float getSpeedOfSound() const;
+ virtual void setSpeedOfSound(float speed);
+ virtual float getDopplerFactor() const;
+ virtual void setDopplerFactor(float factor);
+ virtual DistanceModel getDistanceModel() const;
+ virtual void setDistanceModel(DistanceModel model);
+
+ /**
+ * Retrieves a list of available hardware devices to open with OpenAL.
+ * @return The list of devices to open.
+ */
+ static std::list<std::string> getDeviceNames();
+
+ /**
+ * Registers this plugin.
+ */
+ static void registerPlugin();
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/openal/OpenALReader.cpp b/extern/audaspace/plugins/openal/OpenALReader.cpp
new file mode 100644
index 00000000000..52356d4f7ec
--- /dev/null
+++ b/extern/audaspace/plugins/openal/OpenALReader.cpp
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "OpenALReader.h"
+#include "respec/ConverterFunctions.h"
+#include "Exception.h"
+
+#include <algorithm>
+#include <al.h>
+
+AUD_NAMESPACE_BEGIN
+
+OpenALReader::OpenALReader(Specs specs, int buffersize) :
+ m_specs(specs),
+ m_position(0),
+ m_device(nullptr)
+{
+ if((specs.channels != CHANNELS_MONO) && (specs.channels != CHANNELS_STEREO))
+ specs.channels = CHANNELS_MONO;
+
+ m_device = alcCaptureOpenDevice(nullptr, specs.rate,
+ specs.channels == CHANNELS_MONO ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
+ buffersize * specs.channels * 2);
+
+ if(!m_device)
+ AUD_THROW(DeviceException, "The capture device couldn't be opened with OpenAL.");
+
+ alcCaptureStart(m_device);
+}
+
+OpenALReader::~OpenALReader()
+{
+ if(m_device)
+ {
+ //alcCaptureStop(m_device);
+ alcCaptureCloseDevice(m_device);
+ }
+}
+
+bool OpenALReader::isSeekable() const
+{
+ return false;
+}
+
+void OpenALReader::seek(int position)
+{
+ m_position = position;
+}
+
+int OpenALReader::getLength() const
+{
+ int length;
+ alcGetIntegerv(m_device, ALC_CAPTURE_SAMPLES, 1, &length);
+ return length;
+}
+
+int OpenALReader::getPosition() const
+{
+ return m_position;
+}
+
+Specs OpenALReader::getSpecs() const
+{
+ return m_specs;
+}
+
+void OpenALReader::read(int & length, bool& eos, sample_t* buffer)
+{
+ int len = getLength();
+ length = std::min(length, len);
+
+ if(length > 0)
+ {
+ alcCaptureSamples(m_device, buffer, length);
+ convert_s16_float((data_t*)buffer, (data_t*)buffer, length * m_specs.channels);
+ }
+
+ eos = false;
+
+ m_position += length;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/openal/OpenALReader.h b/extern/audaspace/plugins/openal/OpenALReader.h
new file mode 100644
index 00000000000..5d96ea9b027
--- /dev/null
+++ b/extern/audaspace/plugins/openal/OpenALReader.h
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef OPENAL_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file OpenALReader.h
+ * @ingroup plugin
+ * The OpenALReader class.
+ */
+
+#include "IReader.h"
+
+#include <alc.h>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This class is used for sine tone playback.
+ * The output format is in the 16 bit format and stereo, the sample rate can be
+ * specified.
+ * As the two channels both play the same the output could also be mono, but
+ * in most cases this will result in having to resample for output, so stereo
+ * sound is created directly.
+ */
+class AUD_PLUGIN_API OpenALReader : public IReader
+{
+private:
+ /**
+ * The specs of the reader.
+ */
+ Specs m_specs;
+
+ /**
+ * The current position in samples.
+ */
+ int m_position;
+
+ /**
+ * The capture device.
+ */
+ ALCdevice* m_device;
+
+ // delete copy constructor and operator=
+ OpenALReader(const OpenALReader&) = delete;
+ OpenALReader& operator=(const OpenALReader&) = delete;
+
+public:
+ /**
+ * Creates a new reader.
+ * \param specs The desired specification of the output samples.
+ * \param buffersize The buffer size used to read from the device.
+ */
+ OpenALReader(Specs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
+
+ virtual ~OpenALReader();
+
+ virtual bool isSeekable() const;
+ virtual void seek(int position);
+ virtual int getLength() const;
+ virtual int getPosition() const;
+ virtual Specs getSpecs() const;
+ virtual void read(int & length, bool& eos, sample_t* buffer);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/sdl/SDLDevice.cpp b/extern/audaspace/plugins/sdl/SDLDevice.cpp
new file mode 100644
index 00000000000..603e16001b8
--- /dev/null
+++ b/extern/audaspace/plugins/sdl/SDLDevice.cpp
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include "SDLDevice.h"
+#include "devices/DeviceManager.h"
+#include "devices/IDeviceFactory.h"
+#include "Exception.h"
+#include "IReader.h"
+
+AUD_NAMESPACE_BEGIN
+
+void SDLDevice::SDL_mix(void* data, Uint8* buffer, int length)
+{
+ SDLDevice* device = (SDLDevice*)data;
+
+ if(!device->m_playback)
+ {
+ SDL_PauseAudio(1);
+
+ std::memset(buffer, 0, length);
+
+ return;
+ }
+
+ device->mix((data_t*)buffer, length / AUD_DEVICE_SAMPLE_SIZE(device->m_specs));
+}
+
+void SDLDevice::playing(bool playing)
+{
+ if(!m_playback)
+ SDL_PauseAudio(playing ? 0 : 1);
+
+ m_playback = playing;
+}
+
+SDLDevice::SDLDevice(DeviceSpecs specs, int buffersize) :
+ m_playback(false)
+{
+ if(specs.channels == CHANNELS_INVALID)
+ specs.channels = CHANNELS_STEREO;
+ if(specs.format == FORMAT_INVALID)
+ specs.format = FORMAT_S16;
+ if(specs.rate == RATE_INVALID)
+ specs.rate = RATE_48000;
+
+ m_specs = specs;
+
+ SDL_AudioSpec format, obtained;
+
+ format.freq = m_specs.rate;
+ if(m_specs.format == FORMAT_U8)
+ format.format = AUDIO_U8;
+ else
+ format.format = AUDIO_S16SYS;
+ format.channels = m_specs.channels;
+ format.samples = buffersize;
+ format.callback = SDLDevice::SDL_mix;
+ format.userdata = this;
+
+ if(SDL_OpenAudio(&format, &obtained) != 0)
+ AUD_THROW(DeviceException, "The audio device couldn't be opened with SDL.");
+
+ m_specs.rate = (SampleRate)obtained.freq;
+ m_specs.channels = (Channels)obtained.channels;
+ if(obtained.format == AUDIO_U8)
+ m_specs.format = FORMAT_U8;
+ else if(obtained.format == AUDIO_S16LSB || obtained.format == AUDIO_S16MSB)
+ m_specs.format = FORMAT_S16;
+ else
+ {
+ SDL_CloseAudio();
+ AUD_THROW(DeviceException, "The sample format obtained from SDL is not supported.");
+ }
+
+ create();
+}
+
+SDLDevice::~SDLDevice()
+{
+ SDL_PauseAudio(1);
+ SDL_CloseAudio();
+
+ destroy();
+}
+
+class SDLDeviceFactory : public IDeviceFactory
+{
+private:
+ DeviceSpecs m_specs;
+ int m_buffersize;
+
+public:
+ SDLDeviceFactory() :
+ m_buffersize(AUD_DEFAULT_BUFFER_SIZE)
+ {
+ m_specs.format = FORMAT_S16;
+ m_specs.channels = CHANNELS_STEREO;
+ m_specs.rate = RATE_48000;
+ }
+
+ virtual std::shared_ptr<IDevice> openDevice()
+ {
+ return std::shared_ptr<IDevice>(new SDLDevice(m_specs, m_buffersize));
+ }
+
+ virtual int getPriority()
+ {
+ return 1 << 5;
+ }
+
+ virtual void setSpecs(DeviceSpecs specs)
+ {
+ m_specs = specs;
+ }
+
+ virtual void setBufferSize(int buffersize)
+ {
+ m_buffersize = buffersize;
+ }
+
+ virtual void setName(std::string name)
+ {
+ }
+};
+
+void SDLDevice::registerPlugin()
+{
+ DeviceManager::registerDevice("SDL", std::shared_ptr<IDeviceFactory>(new SDLDeviceFactory));
+}
+
+#ifdef SDL_PLUGIN
+extern "C" AUD_PLUGIN_API void registerPlugin()
+{
+ SDLDevice::registerPlugin();
+}
+
+extern "C" AUD_PLUGIN_API const char* getName()
+{
+ return "SDL";
+}
+#endif
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/sdl/SDLDevice.h b/extern/audaspace/plugins/sdl/SDLDevice.h
new file mode 100644
index 00000000000..935732bb281
--- /dev/null
+++ b/extern/audaspace/plugins/sdl/SDLDevice.h
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+#ifdef SDL_PLUGIN
+#define AUD_BUILD_PLUGIN
+#endif
+
+/**
+ * @file SDLDevice.h
+ * @ingroup plugin
+ * The SDLDevice class.
+ */
+
+#include "devices/SoftwareDevice.h"
+
+#include <SDL.h>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This device plays back through SDL, the simple direct media layer.
+ */
+class AUD_PLUGIN_API SDLDevice : public SoftwareDevice
+{
+private:
+ /**
+ * Whether there is currently playback.
+ */
+ bool m_playback;
+
+ /**
+ * Mixes the next bytes into the buffer.
+ * \param data The SDL device.
+ * \param buffer The target buffer.
+ * \param length The length in bytes to be filled.
+ */
+ AUD_LOCAL static void SDL_mix(void* data, Uint8* buffer, int length);
+
+ // delete copy constructor and operator=
+ SDLDevice(const SDLDevice&) = delete;
+ SDLDevice& operator=(const SDLDevice&) = delete;
+
+protected:
+ virtual void playing(bool playing);
+
+public:
+ /**
+ * Opens the SDL audio device for playback.
+ * \param specs The wanted audio specification.
+ * \param buffersize The size of the internal buffer.
+ * \note The specification really used for opening the device may differ.
+ * \exception Exception Thrown if the audio device cannot be opened.
+ */
+ SDLDevice(DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
+
+ /**
+ * Closes the SDL audio device.
+ */
+ virtual ~SDLDevice();
+
+ /**
+ * Registers this plugin.
+ */
+ static void registerPlugin();
+};
+
+AUD_NAMESPACE_END