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/openal
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/openal')
-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
4 files changed, 1965 insertions, 0 deletions
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