blender/intern/audaspace/OpenAL/AUD_OpenALDevice.cpp
Joerg Mueller 5a8d5f77af Audaspace:
Replacing AUD_Reference with boost::shared_ptr.
2012-11-05 14:24:35 +00:00

1618 lines
33 KiB
C++

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* Copyright 2009-2011 Jörg Hermann Müller
*
* This file is part of AudaSpace.
*
* Audaspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* AudaSpace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Audaspace; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file audaspace/OpenAL/AUD_OpenALDevice.cpp
* \ingroup audopenal
*/
#include "AUD_OpenALDevice.h"
#include "AUD_IFactory.h"
#include "AUD_IReader.h"
#include "AUD_ConverterReader.h"
#include "AUD_MutexLock.h"
#include <cstring>
#include <limits>
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
/*struct AUD_OpenALBufferedFactory
{
/// The factory.
AUD_IFactory* factory;
/// The OpenAL buffer.
ALuint buffer;
};*/
//typedef std::list<AUD_OpenALBufferedFactory*>::iterator AUD_BFIterator;
/******************************************************************************/
/*********************** AUD_OpenALHandle Handle Code *************************/
/******************************************************************************/
static const char* genbuffer_error = "AUD_OpenALDevice: Buffer couldn't be "
"generated.";
static const char* gensource_error = "AUD_OpenALDevice: Source couldn't be "
"generated.";
static const char* queue_error = "AUD_OpenALDevice: Buffer couldn't be "
"queued to the source.";
static const char* bufferdata_error = "AUD_OpenALDevice: Buffer couldn't be "
"filled with data.";
AUD_OpenALDevice::AUD_OpenALHandle::AUD_OpenALHandle(AUD_OpenALDevice* device, ALenum format, boost::shared_ptr<AUD_IReader> reader, bool keep) :
m_isBuffered(false), m_reader(reader), m_keep(keep), m_format(format), m_current(0),
m_eos(false), m_loopcount(0), m_stop(NULL), m_stop_data(NULL), m_status(AUD_STATUS_PLAYING),
m_device(device)
{
AUD_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(AUD_ERROR_OPENAL, genbuffer_error);
try
{
m_device->m_buffer.assureSize(m_device->m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));
int length;
bool eos;
for(int i = 0; i < CYCLE_BUFFERS; i++)
{
length = m_device->m_buffersize;
reader->read(length, eos, m_device->m_buffer.getBuffer());
alBufferData(m_buffers[i], m_format, m_device->m_buffer.getBuffer(),
length * AUD_DEVICE_SAMPLE_SIZE(specs),
specs.rate);
if(alGetError() != AL_NO_ERROR)
AUD_THROW(AUD_ERROR_OPENAL, bufferdata_error);
}
alGenSources(1, &m_source);
if(alGetError() != AL_NO_ERROR)
AUD_THROW(AUD_ERROR_OPENAL, gensource_error);
try
{
alSourceQueueBuffers(m_source, CYCLE_BUFFERS,
m_buffers);
if(alGetError() != AL_NO_ERROR)
AUD_THROW(AUD_ERROR_OPENAL, queue_error);
}
catch(AUD_Exception&)
{
alDeleteSources(1, &m_source);
throw;
}
}
catch(AUD_Exception&)
{
alDeleteBuffers(CYCLE_BUFFERS, m_buffers);
throw;
}
alSourcei(m_source, AL_SOURCE_RELATIVE, 1);
}
bool AUD_OpenALDevice::AUD_OpenALHandle::pause()
{
if(m_status)
{
AUD_MutexLock lock(*m_device);
if(m_status == AUD_STATUS_PLAYING)
{
for(AUD_HandleIterator it = m_device->m_playingSounds.begin(); it != m_device->m_playingSounds.end(); it++)
{
if(it->get() == this)
{
boost::shared_ptr<AUD_OpenALHandle> This = *it;
m_device->m_playingSounds.erase(it);
m_device->m_pausedSounds.push_back(This);
alSourcePause(m_source);
m_status = AUD_STATUS_PAUSED;
return true;
}
}
}
}
return false;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::resume()
{
if(m_status)
{
AUD_MutexLock lock(*m_device);
if(m_status == AUD_STATUS_PAUSED)
{
for(AUD_HandleIterator it = m_device->m_pausedSounds.begin(); it != m_device->m_pausedSounds.end(); it++)
{
if(it->get() == this)
{
boost::shared_ptr<AUD_OpenALHandle> This = *it;
m_device->m_pausedSounds.erase(it);
m_device->m_playingSounds.push_back(This);
m_device->start();
m_status = AUD_STATUS_PLAYING;
return true;
}
}
}
}
return false;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::stop()
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
m_status = AUD_STATUS_INVALID;
alDeleteSources(1, &m_source);
if(!m_isBuffered)
alDeleteBuffers(CYCLE_BUFFERS, m_buffers);
for(AUD_HandleIterator it = m_device->m_playingSounds.begin(); it != m_device->m_playingSounds.end(); it++)
{
if(it->get() == this)
{
boost::shared_ptr<AUD_OpenALHandle> This = *it;
m_device->m_playingSounds.erase(it);
return true;
}
}
for(AUD_HandleIterator it = m_device->m_pausedSounds.begin(); it != m_device->m_pausedSounds.end(); it++)
{
if(it->get() == this)
{
m_device->m_pausedSounds.erase(it);
return true;
}
}
return false;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::getKeep()
{
if(m_status)
return m_keep;
return false;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setKeep(bool keep)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
m_keep = keep;
return true;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::seek(float position)
{
if(!m_status)
return false;
AUD_MutexLock 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);
if(info != AL_PLAYING)
{
if(info == AL_PAUSED)
alSourceStop(m_source);
alSourcei(m_source, AL_BUFFER, 0);
m_current = 0;
ALenum err;
if((err = alGetError()) == AL_NO_ERROR)
{
int length;
AUD_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(int i = 0; i < CYCLE_BUFFERS; i++)
{
length = m_device->m_buffersize;
m_reader->read(length, m_eos, m_device->m_buffer.getBuffer());
alBufferData(m_buffers[i], 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, CYCLE_BUFFERS, m_buffers);
}
alSourceRewind(m_source);
}
}
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getPosition()
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return 0.0f;
float position = 0.0f;
alGetSourcef(m_source, AL_SEC_OFFSET, &position);
if(!m_isBuffered)
{
AUD_Specs specs = m_reader->getSpecs();
position += (m_reader->getPosition() - m_device->m_buffersize *
CYCLE_BUFFERS) / (float)specs.rate;
}
return position;
}
AUD_Status AUD_OpenALDevice::AUD_OpenALHandle::getStatus()
{
return m_status;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getVolume()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_GAIN, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setVolume(float volume)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_GAIN, volume);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getPitch()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_PITCH, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setPitch(float pitch)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_PITCH, pitch);
return true;
}
int AUD_OpenALDevice::AUD_OpenALHandle::getLoopCount()
{
if(!m_status)
return 0;
return m_loopcount;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setLoopCount(int count)
{
if(!m_status)
return false;
m_loopcount = count;
return true;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setStopCallback(stopCallback callback, void* data)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
m_stop = callback;
m_stop_data = data;
return true;
}
/******************************************************************************/
/********************* AUD_OpenALHandle 3DHandle Code *************************/
/******************************************************************************/
AUD_Vector3 AUD_OpenALDevice::AUD_OpenALHandle::getSourceLocation()
{
AUD_Vector3 result = AUD_Vector3(0, 0, 0);
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
ALfloat p[3];
alGetSourcefv(m_source, AL_POSITION, p);
result = AUD_Vector3(p[0], p[1], p[2]);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setSourceLocation(const AUD_Vector3& location)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcefv(m_source, AL_POSITION, (ALfloat*)location.get());
return true;
}
AUD_Vector3 AUD_OpenALDevice::AUD_OpenALHandle::getSourceVelocity()
{
AUD_Vector3 result = AUD_Vector3(0, 0, 0);
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
ALfloat v[3];
alGetSourcefv(m_source, AL_VELOCITY, v);
result = AUD_Vector3(v[0], v[1], v[2]);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setSourceVelocity(const AUD_Vector3& velocity)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcefv(m_source, AL_VELOCITY, (ALfloat*)velocity.get());
return true;
}
AUD_Quaternion AUD_OpenALDevice::AUD_OpenALHandle::getSourceOrientation()
{
return m_orientation;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setSourceOrientation(const AUD_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;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcefv(m_source, AL_DIRECTION, direction);
m_orientation = orientation;
return true;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::isRelative()
{
int result;
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alGetSourcei(m_source, AL_SOURCE_RELATIVE, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setRelative(bool relative)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcei(m_source, AL_SOURCE_RELATIVE, relative);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getVolumeMaximum()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_MAX_GAIN, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setVolumeMaximum(float volume)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_MAX_GAIN, volume);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getVolumeMinimum()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_MIN_GAIN, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setVolumeMinimum(float volume)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_MIN_GAIN, volume);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getDistanceMaximum()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_MAX_DISTANCE, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setDistanceMaximum(float distance)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_MAX_DISTANCE, distance);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getDistanceReference()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_REFERENCE_DISTANCE, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setDistanceReference(float distance)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_REFERENCE_DISTANCE, distance);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getAttenuation()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_ROLLOFF_FACTOR, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setAttenuation(float factor)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_ROLLOFF_FACTOR, factor);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getConeAngleOuter()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_CONE_OUTER_ANGLE, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setConeAngleOuter(float angle)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_CONE_OUTER_ANGLE, angle);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getConeAngleInner()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_CONE_INNER_ANGLE, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setConeAngleInner(float angle)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_CONE_INNER_ANGLE, angle);
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getConeVolumeOuter()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return result;
alGetSourcef(m_source, AL_CONE_OUTER_GAIN, &result);
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setConeVolumeOuter(float volume)
{
if(!m_status)
return false;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcef(m_source, AL_CONE_OUTER_GAIN, volume);
return true;
}
/******************************************************************************/
/**************************** Threading Code **********************************/
/******************************************************************************/
static void *AUD_openalRunThread(void *device)
{
AUD_OpenALDevice* dev = (AUD_OpenALDevice*)device;
dev->updateStreams();
return NULL;
}
void AUD_OpenALDevice::start(bool join)
{
AUD_MutexLock lock(*this);
if(!m_playing)
{
if(join)
pthread_join(m_thread, NULL);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&m_thread, &attr, AUD_openalRunThread, this);
pthread_attr_destroy(&attr);
m_playing = true;
}
}
void AUD_OpenALDevice::updateStreams()
{
boost::shared_ptr<AUD_OpenALHandle> sound;
int length;
ALint info;
AUD_DeviceSpecs specs = m_specs;
ALCenum cerr;
std::list<boost::shared_ptr<AUD_OpenALHandle> > stopSounds;
std::list<boost::shared_ptr<AUD_OpenALHandle> > pauseSounds;
AUD_HandleIterator it;
while(1)
{
lock();
alcSuspendContext(m_context);
cerr = alcGetError(m_device);
if(cerr == ALC_NO_ERROR)
{
// for all sounds
for(it = m_playingSounds.begin(); it != m_playingSounds.end(); it++)
{
sound = *it;
// is it a streamed sound?
if(!sound->m_isBuffered)
{
// check for buffer refilling
alGetSourcei(sound->m_source, AL_BUFFERS_PROCESSED, &info);
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;
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());
}
if(sound->m_loopcount != 0)
sound->m_eos = false;
// read nothing?
if(length == 0)
{
break;
}
// unqueue buffer
alSourceUnqueueBuffers(sound->m_source, 1,
&sound->m_buffers[sound->m_current]);
ALenum err;
if((err = alGetError()) != AL_NO_ERROR)
{
sound->m_eos = true;
break;
}
// fill with new data
alBufferData(sound->m_buffers[sound->m_current],
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,
&sound->m_buffers[sound->m_current]);
if(alGetError() != AL_NO_ERROR)
{
sound->m_eos = true;
break;
}
sound->m_current = (sound->m_current+1) %
AUD_OpenALHandle::CYCLE_BUFFERS;
}
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)
{
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(it = pauseSounds.begin(); it != pauseSounds.end(); it++)
(*it)->pause();
for(it = stopSounds.begin(); it != stopSounds.end(); it++)
(*it)->stop();
pauseSounds.clear();
stopSounds.clear();
alcProcessContext(m_context);
}
// stop thread
if(m_playingSounds.empty() || (cerr != ALC_NO_ERROR))
{
m_playing = false;
unlock();
pthread_exit(NULL);
}
unlock();
#ifdef WIN32
Sleep(20);
#else
usleep(20000);
#endif
}
}
/******************************************************************************/
/**************************** IDevice Code ************************************/
/******************************************************************************/
static const char* open_error = "AUD_OpenALDevice: Device couldn't be opened.";
AUD_OpenALDevice::AUD_OpenALDevice(AUD_DeviceSpecs specs, int 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 = AUD_FORMAT_S16;
#if 0
if(alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_TRUE)
{
ALCchar* devices = const_cast<ALCchar*>(alcGetString(NULL, ALC_DEVICE_SPECIFIER));
printf("OpenAL devices (standard is: %s):\n", alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER));
while(*devices)
{
printf("%s\n", devices);
devices += strlen(devices) + 1;
}
}
#endif
m_device = alcOpenDevice(NULL);
if(!m_device)
AUD_THROW(AUD_ERROR_OPENAL, open_error);
// at least try to set the frequency
ALCint attribs[] = { ALC_FREQUENCY, (ALCint)specs.rate, 0 };
ALCint* attributes = attribs;
if(specs.rate == AUD_RATE_INVALID)
attributes = NULL;
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 = AUD_FORMAT_FLOAT32;
m_useMC = alIsExtensionPresent("AL_EXT_MCFORMATS") == AL_TRUE;
if((!m_useMC && specs.channels > AUD_CHANNELS_STEREO) ||
specs.channels == AUD_CHANNELS_STEREO_LFE ||
specs.channels == AUD_CHANNELS_SURROUND5)
specs.channels = AUD_CHANNELS_STEREO;
alGetError();
alcGetError(m_device);
m_specs = specs;
m_buffersize = buffersize;
m_playing = false;
// m_bufferedFactories = new std::list<AUD_OpenALBufferedFactory*>();
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&m_mutex, &attr);
pthread_mutexattr_destroy(&attr);
start(false);
}
AUD_OpenALDevice::~AUD_OpenALDevice()
{
lock();
alcSuspendContext(m_context);
while(!m_playingSounds.empty())
m_playingSounds.front()->stop();
while(!m_pausedSounds.empty())
m_pausedSounds.front()->stop();
// delete all buffered factories
/*while(!m_bufferedFactories->empty())
{
alDeleteBuffers(1, &(*(m_bufferedFactories->begin()))->buffer);
delete *m_bufferedFactories->begin();
m_bufferedFactories->erase(m_bufferedFactories->begin());
}*/
alcProcessContext(m_context);
// wait for the thread to stop
unlock();
pthread_join(m_thread, NULL);
//delete m_bufferedFactories;
// quit OpenAL
alcMakeContextCurrent(NULL);
alcDestroyContext(m_context);
alcCloseDevice(m_device);
pthread_mutex_destroy(&m_mutex);
}
AUD_DeviceSpecs AUD_OpenALDevice::getSpecs() const
{
return m_specs;
}
bool AUD_OpenALDevice::getFormat(ALenum &format, AUD_Specs specs)
{
bool valid = true;
format = 0;
switch(m_specs.format)
{
case AUD_FORMAT_S16:
switch(specs.channels)
{
case AUD_CHANNELS_MONO:
format = AL_FORMAT_MONO16;
break;
case AUD_CHANNELS_STEREO:
format = AL_FORMAT_STEREO16;
break;
case AUD_CHANNELS_SURROUND4:
if(m_useMC)
{
format = alGetEnumValue("AL_FORMAT_QUAD16");
break;
}
case AUD_CHANNELS_SURROUND51:
if(m_useMC)
{
format = alGetEnumValue("AL_FORMAT_51CHN16");
break;
}
case AUD_CHANNELS_SURROUND61:
if(m_useMC)
{
format = alGetEnumValue("AL_FORMAT_61CHN16");
break;
}
case AUD_CHANNELS_SURROUND71:
if(m_useMC)
{
format = alGetEnumValue("AL_FORMAT_71CHN16");
break;
}
default:
valid = false;
}
break;
case AUD_FORMAT_FLOAT32:
switch(specs.channels)
{
case AUD_CHANNELS_MONO:
format = alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
break;
case AUD_CHANNELS_STEREO:
format = alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
break;
case AUD_CHANNELS_SURROUND4:
if(m_useMC)
{
format = alGetEnumValue("AL_FORMAT_QUAD32");
break;
}
case AUD_CHANNELS_SURROUND51:
if(m_useMC)
{
format = alGetEnumValue("AL_FORMAT_51CHN32");
break;
}
case AUD_CHANNELS_SURROUND61:
if(m_useMC)
{
format = alGetEnumValue("AL_FORMAT_61CHN32");
break;
}
case AUD_CHANNELS_SURROUND71:
if(m_useMC)
{
format = alGetEnumValue("AL_FORMAT_71CHN32");
break;
}
default:
valid = false;
}
break;
default:
valid = false;
}
if(!format)
valid = false;
return valid;
}
boost::shared_ptr<AUD_IHandle> AUD_OpenALDevice::play(boost::shared_ptr<AUD_IReader> reader, bool keep)
{
AUD_Specs specs = reader->getSpecs();
// check format
if(specs.channels == AUD_CHANNELS_INVALID)
return boost::shared_ptr<AUD_IHandle>();
if(m_specs.format != AUD_FORMAT_FLOAT32)
reader = boost::shared_ptr<AUD_IReader>(new AUD_ConverterReader(reader, m_specs));
ALenum format;
if(!getFormat(format, specs))
return boost::shared_ptr<AUD_IHandle>();
AUD_MutexLock lock(*this);
alcSuspendContext(m_context);
boost::shared_ptr<AUD_OpenALDevice::AUD_OpenALHandle> sound;
try
{
// create the handle
sound = boost::shared_ptr<AUD_OpenALDevice::AUD_OpenALHandle>(new AUD_OpenALDevice::AUD_OpenALHandle(this, format, reader, keep));
}
catch(AUD_Exception&)
{
alcProcessContext(m_context);
throw;
}
alcProcessContext(m_context);
// play sound
m_playingSounds.push_back(sound);
start();
return boost::shared_ptr<AUD_IHandle>(sound);
}
boost::shared_ptr<AUD_IHandle> AUD_OpenALDevice::play(boost::shared_ptr<AUD_IFactory> factory, bool keep)
{
/* AUD_XXX disabled
AUD_OpenALHandle* sound = NULL;
lock();
try
{
// check if it is a buffered factory
for(AUD_BFIterator i = m_bufferedFactories->begin();
i != m_bufferedFactories->end(); i++)
{
if((*i)->factory == factory)
{
// create the handle
sound = new AUD_OpenALHandle;
sound->keep = keep;
sound->current = -1;
sound->isBuffered = true;
sound->eos = true;
sound->loopcount = 0;
sound->stop = NULL;
sound->stop_data = NULL;
alcSuspendContext(m_context);
// OpenAL playback code
try
{
alGenSources(1, &sound->source);
if(alGetError() != AL_NO_ERROR)
AUD_THROW(AUD_ERROR_OPENAL, gensource_error);
try
{
alSourcei(sound->source, AL_BUFFER, (*i)->buffer);
if(alGetError() != AL_NO_ERROR)
AUD_THROW(AUD_ERROR_OPENAL, queue_error);
}
catch(AUD_Exception&)
{
alDeleteSources(1, &sound->source);
throw;
}
}
catch(AUD_Exception&)
{
delete sound;
alcProcessContext(m_context);
throw;
}
// play sound
m_playingSounds->push_back(sound);
alSourcei(sound->source, AL_SOURCE_RELATIVE, 1);
start();
alcProcessContext(m_context);
}
}
}
catch(AUD_Exception&)
{
unlock();
throw;
}
unlock();
if(sound)
return sound;*/
return play(factory->createReader(), keep);
}
void AUD_OpenALDevice::stopAll()
{
AUD_MutexLock lock(*this);
alcSuspendContext(m_context);
while(!m_playingSounds.empty())
m_playingSounds.front()->stop();
while(!m_pausedSounds.empty())
m_pausedSounds.front()->stop();
alcProcessContext(m_context);
}
void AUD_OpenALDevice::lock()
{
pthread_mutex_lock(&m_mutex);
}
void AUD_OpenALDevice::unlock()
{
pthread_mutex_unlock(&m_mutex);
}
float AUD_OpenALDevice::getVolume() const
{
float result;
alGetListenerf(AL_GAIN, &result);
return result;
}
void AUD_OpenALDevice::setVolume(float volume)
{
alListenerf(AL_GAIN, volume);
}
/* AUD_XXX Temorary disabled
bool AUD_OpenALDevice::bufferFactory(void *value)
{
bool result = false;
AUD_IFactory* factory = (AUD_IFactory*) value;
// load the factory into an OpenAL buffer
if(factory)
{
// check if the factory is already buffered
lock();
for(AUD_BFIterator i = m_bufferedFactories->begin();
i != m_bufferedFactories->end(); i++)
{
if((*i)->factory == factory)
{
result = true;
break;
}
}
unlock();
if(result)
return result;
AUD_IReader* reader = factory->createReader();
if(reader == NULL)
return false;
AUD_DeviceSpecs specs = m_specs;
specs.specs = reader->getSpecs();
if(m_specs.format != AUD_FORMAT_FLOAT32)
reader = new AUD_ConverterReader(reader, m_specs);
ALenum format;
if(!getFormat(format, specs.specs))
{
return false;
}
// load into a buffer
lock();
alcSuspendContext(m_context);
AUD_OpenALBufferedFactory* bf = new AUD_OpenALBufferedFactory;
bf->factory = factory;
try
{
alGenBuffers(1, &bf->buffer);
if(alGetError() != AL_NO_ERROR)
AUD_THROW(AUD_ERROR_OPENAL);
try
{
sample_t* buf;
int length = reader->getLength();
reader->read(length, buf);
alBufferData(bf->buffer, format, buf,
length * AUD_DEVICE_SAMPLE_SIZE(specs),
specs.rate);
if(alGetError() != AL_NO_ERROR)
AUD_THROW(AUD_ERROR_OPENAL);
}
catch(AUD_Exception&)
{
alDeleteBuffers(1, &bf->buffer);
throw;
}
}
catch(AUD_Exception&)
{
delete bf;
alcProcessContext(m_context);
unlock();
return false;
}
m_bufferedFactories->push_back(bf);
alcProcessContext(m_context);
unlock();
}
else
{
// stop all playing and paused buffered sources
lock();
alcSuspendContext(m_context);
AUD_OpenALHandle* sound;
AUD_HandleIterator it = m_playingSounds->begin();
while(it != m_playingSounds->end())
{
sound = *it;
++it;
if(sound->isBuffered)
stop(sound);
}
alcProcessContext(m_context);
while(!m_bufferedFactories->empty())
{
alDeleteBuffers(1,
&(*(m_bufferedFactories->begin()))->buffer);
delete *m_bufferedFactories->begin();
m_bufferedFactories->erase(m_bufferedFactories->begin());
}
unlock();
}
return true;
}*/
/******************************************************************************/
/**************************** 3D Device Code **********************************/
/******************************************************************************/
AUD_Vector3 AUD_OpenALDevice::getListenerLocation() const
{
ALfloat p[3];
alGetListenerfv(AL_POSITION, p);
return AUD_Vector3(p[0], p[1], p[2]);
}
void AUD_OpenALDevice::setListenerLocation(const AUD_Vector3& location)
{
alListenerfv(AL_POSITION, (ALfloat*)location.get());
}
AUD_Vector3 AUD_OpenALDevice::getListenerVelocity() const
{
ALfloat v[3];
alGetListenerfv(AL_VELOCITY, v);
return AUD_Vector3(v[0], v[1], v[2]);
}
void AUD_OpenALDevice::setListenerVelocity(const AUD_Vector3& velocity)
{
alListenerfv(AL_VELOCITY, (ALfloat*)velocity.get());
}
AUD_Quaternion AUD_OpenALDevice::getListenerOrientation() const
{
return m_orientation;
}
void AUD_OpenALDevice::setListenerOrientation(const AUD_Quaternion& orientation)
{
ALfloat direction[6];
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 AUD_OpenALDevice::getSpeedOfSound() const
{
return alGetFloat(AL_SPEED_OF_SOUND);
}
void AUD_OpenALDevice::setSpeedOfSound(float speed)
{
alSpeedOfSound(speed);
}
float AUD_OpenALDevice::getDopplerFactor() const
{
return alGetFloat(AL_DOPPLER_FACTOR);
}
void AUD_OpenALDevice::setDopplerFactor(float factor)
{
alDopplerFactor(factor);
}
AUD_DistanceModel AUD_OpenALDevice::getDistanceModel() const
{
switch(alGetInteger(AL_DISTANCE_MODEL))
{
case AL_INVERSE_DISTANCE:
return AUD_DISTANCE_MODEL_INVERSE;
case AL_INVERSE_DISTANCE_CLAMPED:
return AUD_DISTANCE_MODEL_INVERSE_CLAMPED;
case AL_LINEAR_DISTANCE:
return AUD_DISTANCE_MODEL_LINEAR;
case AL_LINEAR_DISTANCE_CLAMPED:
return AUD_DISTANCE_MODEL_LINEAR_CLAMPED;
case AL_EXPONENT_DISTANCE:
return AUD_DISTANCE_MODEL_EXPONENT;
case AL_EXPONENT_DISTANCE_CLAMPED:
return AUD_DISTANCE_MODEL_EXPONENT_CLAMPED;
default:
return AUD_DISTANCE_MODEL_INVALID;
}
}
void AUD_OpenALDevice::setDistanceModel(AUD_DistanceModel model)
{
switch(model)
{
case AUD_DISTANCE_MODEL_INVERSE:
alDistanceModel(AL_INVERSE_DISTANCE);
break;
case AUD_DISTANCE_MODEL_INVERSE_CLAMPED:
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
break;
case AUD_DISTANCE_MODEL_LINEAR:
alDistanceModel(AL_LINEAR_DISTANCE);
break;
case AUD_DISTANCE_MODEL_LINEAR_CLAMPED:
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
break;
case AUD_DISTANCE_MODEL_EXPONENT:
alDistanceModel(AL_EXPONENT_DISTANCE);
break;
case AUD_DISTANCE_MODEL_EXPONENT_CLAMPED:
alDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);
break;
default:
alDistanceModel(AL_NONE);
}
}