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:
Diffstat (limited to 'extern/audaspace/plugins/openal/OpenALDevice.cpp')
-rw-r--r--extern/audaspace/plugins/openal/OpenALDevice.cpp1490
1 files changed, 1490 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