Removes the playback handle from the synchronizer API and integrates it into the device, removing the ISynchronizer interface completely. This has been discussed in more detail in #126047 and its main purpose is to unify the handling of synchronizer events, especially seek which needed different logic for jack vs other backends. OpenAL now needs a silence playback handle for synchronization but all other backends are pretty straightforward just counting the mixed samples in the SoftwareDevice with some specializations for specific backends that had their own synchronizers before. Note: CoreAudio changes are untested as I don't have a Mac. Pull Request: https://projects.blender.org/blender/blender/pulls/133191
1678 lines
34 KiB
C++
1678 lines
34 KiB
C++
/*******************************************************************************
|
|
* 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 <chrono>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
|
|
#include "Exception.h"
|
|
#include "ISound.h"
|
|
|
|
#include "devices/DeviceManager.h"
|
|
#include "devices/IDeviceFactory.h"
|
|
#include "generator/SilenceReader.h"
|
|
#include "respec/ConverterReader.h"
|
|
|
|
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;
|
|
}
|
|
|
|
bool OpenALDevice::OpenALHandle::reinitialize()
|
|
{
|
|
DeviceSpecs specs = m_device->m_specs;
|
|
specs.specs = m_reader->getSpecs();
|
|
|
|
ALenum format;
|
|
|
|
if(!m_device->getFormat(format, specs.specs))
|
|
return true;
|
|
|
|
m_format = format;
|
|
|
|
// OpenAL playback code
|
|
alGenBuffers(CYCLE_BUFFERS, m_buffers);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
return true;
|
|
|
|
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;
|
|
m_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)
|
|
return true;
|
|
}
|
|
|
|
alGenSources(1, &m_source);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
return true;
|
|
|
|
alSourceQueueBuffers(m_source, m_current, m_buffers);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
return true;
|
|
|
|
alSourcei(m_source, AL_SOURCE_RELATIVE, m_relative);
|
|
|
|
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_relative(1), 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;
|
|
|
|
if(m_stop)
|
|
m_stop(m_stop_data);
|
|
|
|
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(double 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;
|
|
}
|
|
|
|
double 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()
|
|
{
|
|
if(!m_status)
|
|
return false;
|
|
|
|
std::lock_guard<ILockable> lock(*m_device);
|
|
|
|
if(!m_status)
|
|
return false;
|
|
|
|
alGetSourcei(m_source, AL_SOURCE_RELATIVE, &m_relative);
|
|
|
|
return m_relative;
|
|
}
|
|
|
|
bool OpenALDevice::OpenALHandle::setRelative(bool relative)
|
|
{
|
|
if(!m_status)
|
|
return false;
|
|
|
|
std::lock_guard<ILockable> lock(*m_device);
|
|
|
|
if(!m_status)
|
|
return false;
|
|
|
|
m_relative = relative;
|
|
|
|
alSourcei(m_source, AL_SOURCE_RELATIVE, m_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();
|
|
|
|
if(m_checkDisconnect)
|
|
{
|
|
ALCint connected;
|
|
alcGetIntegerv(m_device, alcGetEnumValue(m_device, "ALC_CONNECTED"), 1, &connected);
|
|
|
|
if(!connected)
|
|
{
|
|
// quit OpenAL
|
|
alcMakeContextCurrent(nullptr);
|
|
alcDestroyContext(m_context);
|
|
alcCloseDevice(m_device);
|
|
|
|
// restart
|
|
if(m_name.empty())
|
|
m_device = alcOpenDevice(nullptr);
|
|
else
|
|
m_device = alcOpenDevice(m_name.c_str());
|
|
|
|
// if device opening failed, there's really nothing we can do
|
|
if(m_device)
|
|
{
|
|
// 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);
|
|
|
|
m_checkDisconnect = alcIsExtensionPresent(m_device, "ALC_EXT_disconnect");
|
|
|
|
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;
|
|
else
|
|
specs.format = FORMAT_S16;
|
|
|
|
// if the format of the device changed, all handles are invalidated
|
|
// this is unlikely to happen though
|
|
if(specs.format != m_specs.format)
|
|
stopAll();
|
|
|
|
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;
|
|
|
|
std::list<std::shared_ptr<OpenALHandle> > stopSounds;
|
|
|
|
for(auto& handle : m_playingSounds)
|
|
if(handle->reinitialize())
|
|
stopSounds.push_back(handle);
|
|
|
|
for(auto& handle : m_pausedSounds)
|
|
if(handle->reinitialize())
|
|
stopSounds.push_back(handle);
|
|
|
|
for(auto& sound : stopSounds)
|
|
sound->stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
// pause or
|
|
if(sound->m_keep)
|
|
{
|
|
if(sound->m_stop)
|
|
sound->m_stop(sound->m_stop_data);
|
|
|
|
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, const std::string &name) :
|
|
m_name(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(m_name.empty())
|
|
m_device = alcOpenDevice(nullptr);
|
|
else
|
|
m_device = alcOpenDevice(m_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);
|
|
|
|
m_checkDisconnect = alcIsExtensionPresent(m_device, "ALC_EXT_disconnect");
|
|
|
|
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_SURROUND71)
|
|
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);
|
|
}
|
|
|
|
void OpenALDevice::seekSynchronizer(double time)
|
|
{
|
|
std::lock_guard<ILockable> lock(*this);
|
|
|
|
m_synchronizerPosition = uint64_t(time * m_specs.rate);
|
|
if(m_silenceHandle)
|
|
m_silenceHandle->seek(time);
|
|
}
|
|
|
|
double OpenALDevice::getSynchronizerPosition()
|
|
{
|
|
std::lock_guard<ILockable> lock(*this);
|
|
|
|
if(m_silenceHandle)
|
|
return m_silenceHandle->getPosition();
|
|
|
|
return m_synchronizerPosition;
|
|
}
|
|
|
|
void OpenALDevice::playSynchronizer()
|
|
{
|
|
std::lock_guard<ILockable> lock(*this);
|
|
|
|
if(m_silenceHandle)
|
|
m_silenceHandle->resume();
|
|
else
|
|
{
|
|
auto reader = std::make_shared<SilenceReader>(m_specs.rate);
|
|
reader->seek(m_synchronizerPosition);
|
|
m_silenceHandle = play(reader);
|
|
}
|
|
}
|
|
|
|
void OpenALDevice::stopSynchronizer()
|
|
{
|
|
std::lock_guard<ILockable> lock(*this);
|
|
|
|
if(m_silenceHandle)
|
|
{
|
|
m_synchronizerPosition = m_silenceHandle->getPosition();
|
|
m_silenceHandle->stop();
|
|
m_silenceHandle.reset();
|
|
}
|
|
}
|
|
|
|
void OpenALDevice::setSyncCallback(syncFunction function, void* data)
|
|
{
|
|
}
|
|
|
|
int OpenALDevice::isSynchronizerPlaying()
|
|
{
|
|
std::lock_guard<ILockable> lock(*this);
|
|
|
|
if(m_silenceHandle)
|
|
return m_silenceHandle->getStatus() == STATUS_PLAYING;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**************************** 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(const 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(const std::string &name)
|
|
{
|
|
}
|
|
};
|
|
|
|
void OpenALDevice::registerPlugin()
|
|
{
|
|
auto names = OpenALDevice::getDeviceNames();
|
|
DeviceManager::registerDevice("OpenAL", std::shared_ptr<IDeviceFactory>(new OpenALDeviceFactory));
|
|
for(const 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
|