forked from bartvdbraak/blender
6c5c58e057
See mailing list for additional information.
1363 lines
29 KiB
C++
1363 lines
29 KiB
C++
/*
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN LGPL LICENSE BLOCK *****
|
|
*
|
|
* Copyright 2009 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 Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with AudaSpace. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* ***** END LGPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include "AUD_OpenALDevice.h"
|
|
#include "AUD_IReader.h"
|
|
#include "AUD_IMixer.h"
|
|
#include "AUD_ConverterFactory.h"
|
|
#include "AUD_SourceCaps.h"
|
|
|
|
#include <cstring>
|
|
#include <limits>
|
|
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#define AUD_OPENAL_CYCLE_BUFFERS 3
|
|
|
|
/// Saves the data for playback.
|
|
struct AUD_OpenALHandle : AUD_Handle
|
|
{
|
|
/// Whether it's a buffered or a streamed source.
|
|
bool isBuffered;
|
|
|
|
/// The reader source.
|
|
AUD_IReader* reader;
|
|
|
|
/// Whether to keep the source if end of it is reached.
|
|
bool keep;
|
|
|
|
/// OpenAL sample format.
|
|
ALenum format;
|
|
|
|
/// OpenAL source.
|
|
ALuint source;
|
|
|
|
/// OpenAL buffers.
|
|
ALuint buffers[AUD_OPENAL_CYCLE_BUFFERS];
|
|
|
|
/// The first buffer to be read next.
|
|
int current;
|
|
|
|
/// Whether the stream doesn't return any more data.
|
|
bool data_end;
|
|
};
|
|
|
|
struct AUD_OpenALBufferedFactory
|
|
{
|
|
/// The factory.
|
|
AUD_IFactory* factory;
|
|
|
|
/// The OpenAL buffer.
|
|
ALuint buffer;
|
|
};
|
|
|
|
typedef std::list<AUD_OpenALHandle*>::iterator AUD_HandleIterator;
|
|
typedef std::list<AUD_OpenALBufferedFactory*>::iterator AUD_BFIterator;
|
|
|
|
/******************************************************************************/
|
|
/**************************** Threading Code **********************************/
|
|
/******************************************************************************/
|
|
|
|
void* AUD_openalRunThread(void* device)
|
|
{
|
|
AUD_OpenALDevice* dev = (AUD_OpenALDevice*)device;
|
|
dev->updateStreams();
|
|
return NULL;
|
|
}
|
|
|
|
void AUD_OpenALDevice::start()
|
|
{
|
|
lock();
|
|
|
|
if(!m_playing)
|
|
{
|
|
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;
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
void AUD_OpenALDevice::updateStreams()
|
|
{
|
|
AUD_OpenALHandle* sound;
|
|
|
|
int length;
|
|
sample_t* buffer;
|
|
|
|
ALint info;
|
|
AUD_Specs specs;
|
|
|
|
while(1)
|
|
{
|
|
lock();
|
|
|
|
alcSuspendContext(m_context);
|
|
|
|
// for all sounds
|
|
AUD_HandleIterator it = m_playingSounds->begin();
|
|
while(it != m_playingSounds->end())
|
|
{
|
|
sound = *it;
|
|
// increment the iterator to make sure it's valid,
|
|
// in case the sound gets deleted after stopping
|
|
++it;
|
|
|
|
// is it a streamed sound?
|
|
if(!sound->isBuffered)
|
|
{
|
|
// check for buffer refilling
|
|
alGetSourcei(sound->source, AL_BUFFERS_PROCESSED, &info);
|
|
|
|
if(info)
|
|
{
|
|
specs = sound->reader->getSpecs();
|
|
|
|
// for all empty buffers
|
|
while(info--)
|
|
{
|
|
// if there's still data to play back
|
|
if(!sound->data_end)
|
|
{
|
|
// read data
|
|
length = m_buffersize;
|
|
sound->reader->read(length, buffer);
|
|
|
|
// read nothing?
|
|
if(length == 0)
|
|
{
|
|
sound->data_end = true;
|
|
break;
|
|
}
|
|
|
|
// unqueue buffer
|
|
alSourceUnqueueBuffers(sound->source, 1,
|
|
&sound->buffers[sound->current]);
|
|
ALenum err;
|
|
if((err = alGetError()) != AL_NO_ERROR)
|
|
{
|
|
sound->data_end = true;
|
|
break;
|
|
}
|
|
|
|
// fill with new data
|
|
alBufferData(sound->buffers[sound->current],
|
|
sound->format,
|
|
buffer,
|
|
length * AUD_SAMPLE_SIZE(specs),
|
|
specs.rate);
|
|
|
|
if(alGetError() != AL_NO_ERROR)
|
|
{
|
|
sound->data_end = true;
|
|
break;
|
|
}
|
|
|
|
// and queue again
|
|
alSourceQueueBuffers(sound->source, 1,
|
|
&sound->buffers[sound->current]);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
{
|
|
sound->data_end = true;
|
|
break;
|
|
}
|
|
|
|
sound->current = (sound->current+1) %
|
|
AUD_OPENAL_CYCLE_BUFFERS;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if the sound has been stopped
|
|
alGetSourcei(sound->source, AL_SOURCE_STATE, &info);
|
|
|
|
if(info != AL_PLAYING)
|
|
{
|
|
// if it really stopped
|
|
if(sound->data_end)
|
|
{
|
|
// pause or
|
|
if(sound->keep)
|
|
pause(sound);
|
|
// stop
|
|
else
|
|
stop(sound);
|
|
}
|
|
// continue playing
|
|
else
|
|
alSourcePlay(sound->source);
|
|
}
|
|
}
|
|
|
|
alcProcessContext(m_context);
|
|
|
|
// stop thread
|
|
if(m_playingSounds->empty())
|
|
{
|
|
unlock();
|
|
m_playing = false;
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
unlock();
|
|
|
|
#ifdef WIN32
|
|
Sleep(20);
|
|
#else
|
|
usleep(20000);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**************************** IDevice Code ************************************/
|
|
/******************************************************************************/
|
|
|
|
bool AUD_OpenALDevice::isValid(AUD_Handle* handle)
|
|
{
|
|
for(AUD_HandleIterator i = m_playingSounds->begin();
|
|
i != m_playingSounds->end(); i++)
|
|
if(*i == handle)
|
|
return true;
|
|
for(AUD_HandleIterator i = m_pausedSounds->begin();
|
|
i != m_pausedSounds->end(); i++)
|
|
if(*i == handle)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
AUD_OpenALDevice::AUD_OpenALDevice(AUD_Specs 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.channels = AUD_CHANNELS_STEREO;
|
|
specs.format = AUD_FORMAT_S16;
|
|
|
|
m_device = alcOpenDevice(NULL);
|
|
|
|
if(!m_device)
|
|
AUD_THROW(AUD_ERROR_OPENAL);
|
|
|
|
// at least try to set the frequency
|
|
ALCint attribs[] = { ALC_FREQUENCY, 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;
|
|
|
|
alGetError();
|
|
|
|
m_converter = new AUD_ConverterFactory(specs); AUD_NEW("factory")
|
|
|
|
m_specs = specs;
|
|
m_buffersize = buffersize;
|
|
m_playing = false;
|
|
|
|
m_playingSounds = new std::list<AUD_OpenALHandle*>(); AUD_NEW("list")
|
|
m_pausedSounds = new std::list<AUD_OpenALHandle*>(); AUD_NEW("list")
|
|
m_bufferedFactories = new std::list<AUD_OpenALBufferedFactory*>();
|
|
AUD_NEW("list")
|
|
|
|
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);
|
|
}
|
|
|
|
AUD_OpenALDevice::~AUD_OpenALDevice()
|
|
{
|
|
AUD_OpenALHandle* sound;
|
|
|
|
lock();
|
|
alcSuspendContext(m_context);
|
|
|
|
// delete all playing sounds
|
|
while(!m_playingSounds->empty())
|
|
{
|
|
sound = *(m_playingSounds->begin());
|
|
alDeleteSources(1, &sound->source);
|
|
if(!sound->isBuffered)
|
|
{
|
|
delete sound->reader; AUD_DELETE("reader")
|
|
alDeleteBuffers(AUD_OPENAL_CYCLE_BUFFERS, sound->buffers);
|
|
}
|
|
delete sound; AUD_DELETE("handle")
|
|
m_playingSounds->erase(m_playingSounds->begin());
|
|
}
|
|
|
|
// delete all paused sounds
|
|
while(!m_pausedSounds->empty())
|
|
{
|
|
sound = *(m_pausedSounds->begin());
|
|
alDeleteSources(1, &sound->source);
|
|
if(!sound->isBuffered)
|
|
{
|
|
delete sound->reader; AUD_DELETE("reader")
|
|
alDeleteBuffers(AUD_OPENAL_CYCLE_BUFFERS, sound->buffers);
|
|
}
|
|
delete sound; AUD_DELETE("handle")
|
|
m_pausedSounds->erase(m_pausedSounds->begin());
|
|
}
|
|
|
|
// delete all buffered factories
|
|
while(!m_bufferedFactories->empty())
|
|
{
|
|
alDeleteBuffers(1, &(*(m_bufferedFactories->begin()))->buffer);
|
|
delete *m_bufferedFactories->begin(); AUD_DELETE("bufferedfactory");
|
|
m_bufferedFactories->erase(m_bufferedFactories->begin());
|
|
}
|
|
|
|
alcProcessContext(m_context);
|
|
|
|
// wait for the thread to stop
|
|
if(m_playing)
|
|
{
|
|
unlock();
|
|
pthread_join(m_thread, NULL);
|
|
}
|
|
else
|
|
unlock();
|
|
|
|
delete m_playingSounds; AUD_DELETE("list")
|
|
delete m_pausedSounds; AUD_DELETE("list")
|
|
delete m_bufferedFactories; AUD_DELETE("list")
|
|
|
|
// quit OpenAL
|
|
alcMakeContextCurrent(NULL);
|
|
alcDestroyContext(m_context);
|
|
alcCloseDevice(m_device);
|
|
|
|
delete m_converter; AUD_DELETE("factory")
|
|
|
|
pthread_mutex_destroy(&m_mutex);
|
|
}
|
|
|
|
AUD_Specs AUD_OpenALDevice::getSpecs()
|
|
{
|
|
return m_specs;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::getFormat(ALenum &format, AUD_Specs specs)
|
|
{
|
|
bool valid = true;
|
|
format = 0;
|
|
|
|
switch(specs.format)
|
|
{
|
|
case AUD_FORMAT_U8:
|
|
switch(specs.channels)
|
|
{
|
|
case AUD_CHANNELS_MONO:
|
|
format = AL_FORMAT_MONO8;
|
|
break;
|
|
case AUD_CHANNELS_STEREO:
|
|
format = AL_FORMAT_STEREO8;
|
|
break;
|
|
case AUD_CHANNELS_SURROUND4:
|
|
if(m_useMC)
|
|
{
|
|
format = alGetEnumValue("AL_FORMAT_QUAD8");
|
|
break;
|
|
}
|
|
case AUD_CHANNELS_SURROUND51:
|
|
if(m_useMC)
|
|
{
|
|
format = alGetEnumValue("AL_FORMAT_51CHN8");
|
|
break;
|
|
}
|
|
case AUD_CHANNELS_SURROUND61:
|
|
if(m_useMC)
|
|
{
|
|
format = alGetEnumValue("AL_FORMAT_61CHN8");
|
|
break;
|
|
}
|
|
case AUD_CHANNELS_SURROUND71:
|
|
if(m_useMC)
|
|
{
|
|
format = alGetEnumValue("AL_FORMAT_71CHN8");
|
|
break;
|
|
}
|
|
default:
|
|
valid = false;
|
|
}
|
|
break;
|
|
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;
|
|
}
|
|
|
|
AUD_Handle* AUD_OpenALDevice::play(AUD_IFactory* factory, bool keep)
|
|
{
|
|
// 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
|
|
AUD_OpenALHandle* sound = new AUD_OpenALHandle; AUD_NEW("handle")
|
|
sound->keep = keep;
|
|
sound->current = -1;
|
|
sound->isBuffered = true;
|
|
sound->data_end = true;
|
|
|
|
alcSuspendContext(m_context);
|
|
|
|
// OpenAL playback code
|
|
try
|
|
{
|
|
alGenSources(1, &sound->source);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
AUD_THROW(AUD_ERROR_OPENAL);
|
|
|
|
try
|
|
{
|
|
alSourcei(sound->source, AL_BUFFER, (*i)->buffer);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
AUD_THROW(AUD_ERROR_OPENAL);
|
|
}
|
|
catch(AUD_Exception e)
|
|
{
|
|
alDeleteSources(1, &sound->source);
|
|
throw;
|
|
}
|
|
}
|
|
catch(AUD_Exception e)
|
|
{
|
|
delete sound; AUD_DELETE("handle")
|
|
alcProcessContext(m_context);
|
|
unlock();
|
|
throw;
|
|
}
|
|
|
|
// play sound
|
|
m_playingSounds->push_back(sound);
|
|
|
|
alSourcei(sound->source, AL_SOURCE_RELATIVE, 1);
|
|
start();
|
|
|
|
alcProcessContext(m_context);
|
|
unlock();
|
|
|
|
return sound;
|
|
}
|
|
}
|
|
|
|
AUD_IReader* reader = factory->createReader();
|
|
|
|
if(reader == NULL)
|
|
AUD_THROW(AUD_ERROR_READER);
|
|
|
|
AUD_Specs specs;
|
|
|
|
specs = reader->getSpecs();
|
|
|
|
// check format
|
|
bool valid = true;
|
|
|
|
if(specs.format == AUD_FORMAT_INVALID)
|
|
valid = false;
|
|
else if(specs.format == AUD_FORMAT_S24 ||
|
|
specs.format == AUD_FORMAT_S32 ||
|
|
specs.format == AUD_FORMAT_FLOAT32 ||
|
|
specs.format == AUD_FORMAT_FLOAT64)
|
|
{
|
|
m_converter->setReader(reader);
|
|
reader = m_converter->createReader();
|
|
specs = reader->getSpecs();
|
|
}
|
|
|
|
// create the handle
|
|
AUD_OpenALHandle* sound = new AUD_OpenALHandle; AUD_NEW("handle")
|
|
sound->keep = keep;
|
|
sound->reader = reader;
|
|
sound->current = 0;
|
|
sound->isBuffered = false;
|
|
sound->data_end = false;
|
|
|
|
valid &= getFormat(sound->format, specs);
|
|
|
|
if(!valid)
|
|
{
|
|
delete sound; AUD_DELETE("handle")
|
|
delete reader; AUD_DELETE("reader")
|
|
return NULL;
|
|
}
|
|
|
|
lock();
|
|
alcSuspendContext(m_context);
|
|
|
|
// OpenAL playback code
|
|
try
|
|
{
|
|
alGenBuffers(AUD_OPENAL_CYCLE_BUFFERS, sound->buffers);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
AUD_THROW(AUD_ERROR_OPENAL);
|
|
|
|
try
|
|
{
|
|
sample_t* buf;
|
|
int length;
|
|
|
|
for(int i = 0; i < AUD_OPENAL_CYCLE_BUFFERS; i++)
|
|
{
|
|
length = m_buffersize;
|
|
reader->read(length, buf);
|
|
alBufferData(sound->buffers[i], sound->format, buf,
|
|
length * AUD_SAMPLE_SIZE(specs), specs.rate);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
AUD_THROW(AUD_ERROR_OPENAL);
|
|
}
|
|
|
|
alGenSources(1, &sound->source);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
AUD_THROW(AUD_ERROR_OPENAL);
|
|
|
|
try
|
|
{
|
|
alSourceQueueBuffers(sound->source, AUD_OPENAL_CYCLE_BUFFERS,
|
|
sound->buffers);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
AUD_THROW(AUD_ERROR_OPENAL);
|
|
}
|
|
catch(AUD_Exception e)
|
|
{
|
|
alDeleteSources(1, &sound->source);
|
|
throw;
|
|
}
|
|
}
|
|
catch(AUD_Exception e)
|
|
{
|
|
alDeleteBuffers(AUD_OPENAL_CYCLE_BUFFERS, sound->buffers);
|
|
throw;
|
|
}
|
|
}
|
|
catch(AUD_Exception e)
|
|
{
|
|
delete sound; AUD_DELETE("handle")
|
|
delete reader; AUD_DELETE("reader")
|
|
alcProcessContext(m_context);
|
|
unlock();
|
|
throw;
|
|
}
|
|
|
|
// play sound
|
|
m_playingSounds->push_back(sound);
|
|
alSourcei(sound->source, AL_SOURCE_RELATIVE, 1);
|
|
|
|
start();
|
|
|
|
alcProcessContext(m_context);
|
|
unlock();
|
|
|
|
return sound;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::pause(AUD_Handle* handle)
|
|
{
|
|
// only songs that are played can be paused
|
|
lock();
|
|
for(AUD_HandleIterator i = m_playingSounds->begin();
|
|
i != m_playingSounds->end(); i++)
|
|
{
|
|
if(*i == handle)
|
|
{
|
|
m_pausedSounds->push_back(*i);
|
|
alSourcePause((*i)->source);
|
|
m_playingSounds->erase(i);
|
|
unlock();
|
|
return true;
|
|
}
|
|
}
|
|
unlock();
|
|
return false;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::resume(AUD_Handle* handle)
|
|
{
|
|
lock();
|
|
|
|
// only songs that are paused can be resumed
|
|
for(AUD_HandleIterator i = m_pausedSounds->begin();
|
|
i != m_pausedSounds->end(); i++)
|
|
{
|
|
if(*i == handle)
|
|
{
|
|
m_playingSounds->push_back(*i);
|
|
start();
|
|
m_pausedSounds->erase(i);
|
|
unlock();
|
|
return true;
|
|
}
|
|
}
|
|
unlock();
|
|
return false;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::stop(AUD_Handle* handle)
|
|
{
|
|
AUD_OpenALHandle* sound;
|
|
|
|
lock();
|
|
for(AUD_HandleIterator i = m_playingSounds->begin();
|
|
i != m_playingSounds->end(); i++)
|
|
{
|
|
if(*i == handle)
|
|
{
|
|
sound = *i;
|
|
alDeleteSources(1, &sound->source);
|
|
if(!sound->isBuffered)
|
|
{
|
|
delete sound->reader; AUD_DELETE("reader")
|
|
alDeleteBuffers(AUD_OPENAL_CYCLE_BUFFERS, sound->buffers);
|
|
}
|
|
delete *i; AUD_DELETE("handle")
|
|
m_playingSounds->erase(i);
|
|
unlock();
|
|
return true;
|
|
}
|
|
}
|
|
for(AUD_HandleIterator i = m_pausedSounds->begin();
|
|
i != m_pausedSounds->end(); i++)
|
|
{
|
|
if(*i == handle)
|
|
{
|
|
sound = *i;
|
|
alDeleteSources(1, &sound->source);
|
|
if(!sound->isBuffered)
|
|
{
|
|
delete sound->reader; AUD_DELETE("reader")
|
|
alDeleteBuffers(AUD_OPENAL_CYCLE_BUFFERS, sound->buffers);
|
|
}
|
|
delete *i; AUD_DELETE("handle")
|
|
m_pausedSounds->erase(i);
|
|
unlock();
|
|
return true;
|
|
}
|
|
}
|
|
unlock();
|
|
return false;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::setKeep(AUD_Handle* handle, bool keep)
|
|
{
|
|
lock();
|
|
if(isValid(handle))
|
|
{
|
|
((AUD_OpenALHandle*)handle)->keep = keep;
|
|
unlock();
|
|
return true;
|
|
}
|
|
unlock();
|
|
return false;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::sendMessage(AUD_Handle* handle, AUD_Message &message)
|
|
{
|
|
lock();
|
|
|
|
bool result = false;
|
|
|
|
if(handle == 0)
|
|
{
|
|
for(AUD_HandleIterator i = m_playingSounds->begin();
|
|
i != m_playingSounds->end(); i++)
|
|
if(!(*i)->isBuffered)
|
|
result |= (*i)->reader->notify(message);
|
|
for(AUD_HandleIterator i = m_pausedSounds->begin();
|
|
i != m_pausedSounds->end(); i++)
|
|
if(!(*i)->isBuffered)
|
|
result |= (*i)->reader->notify(message);
|
|
}
|
|
else if(isValid(handle))
|
|
if(!((AUD_OpenALHandle*)handle)->isBuffered)
|
|
result = ((AUD_OpenALHandle*)handle)->reader->notify(message);
|
|
unlock();
|
|
return result;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::seek(AUD_Handle* handle, float position)
|
|
{
|
|
lock();
|
|
|
|
if(isValid(handle))
|
|
{
|
|
AUD_OpenALHandle* alhandle = (AUD_OpenALHandle*)handle;
|
|
if(alhandle->isBuffered)
|
|
alSourcef(alhandle->source, AL_SEC_OFFSET, position);
|
|
else
|
|
{
|
|
alhandle->reader->seek((int)(position *
|
|
alhandle->reader->getSpecs().rate));
|
|
alhandle->data_end = false;
|
|
|
|
ALint info;
|
|
|
|
alGetSourcei(alhandle->source, AL_SOURCE_STATE, &info);
|
|
|
|
if(info != AL_PLAYING)
|
|
{
|
|
if(info != AL_STOPPED)
|
|
alSourceStop(alhandle->source);
|
|
|
|
alSourceUnqueueBuffers(alhandle->source,
|
|
AUD_OPENAL_CYCLE_BUFFERS,
|
|
alhandle->buffers);
|
|
if(alGetError() == AL_NO_ERROR)
|
|
{
|
|
sample_t* buf;
|
|
int length;
|
|
AUD_Specs specs = alhandle->reader->getSpecs();
|
|
|
|
for(int i = 0; i < AUD_OPENAL_CYCLE_BUFFERS; i++)
|
|
{
|
|
length = m_buffersize;
|
|
alhandle->reader->read(length, buf);
|
|
alBufferData(alhandle->buffers[i], alhandle->format,
|
|
buf, length * AUD_SAMPLE_SIZE(specs),
|
|
specs.rate);
|
|
|
|
if(alGetError() != AL_NO_ERROR)
|
|
break;
|
|
}
|
|
|
|
alSourceQueueBuffers(alhandle->source,
|
|
AUD_OPENAL_CYCLE_BUFFERS,
|
|
alhandle->buffers);
|
|
}
|
|
|
|
alSourceRewind(alhandle->source);
|
|
}
|
|
}
|
|
unlock();
|
|
return true;
|
|
}
|
|
|
|
unlock();
|
|
return false;
|
|
}
|
|
|
|
float AUD_OpenALDevice::getPosition(AUD_Handle* handle)
|
|
{
|
|
lock();
|
|
|
|
float position = 0.0;
|
|
|
|
if(isValid(handle))
|
|
{
|
|
AUD_OpenALHandle* h = (AUD_OpenALHandle*)handle;
|
|
if(h->isBuffered)
|
|
alGetSourcef(h->source, AL_SEC_OFFSET, &position);
|
|
else
|
|
position = h->reader->getPosition() /
|
|
(float)h->reader->getSpecs().rate;
|
|
}
|
|
|
|
unlock();
|
|
return position;
|
|
}
|
|
|
|
AUD_Status AUD_OpenALDevice::getStatus(AUD_Handle* handle)
|
|
{
|
|
lock();
|
|
for(AUD_HandleIterator i = m_playingSounds->begin();
|
|
i != m_playingSounds->end(); i++)
|
|
{
|
|
if(*i == handle)
|
|
{
|
|
unlock();
|
|
return AUD_STATUS_PLAYING;
|
|
}
|
|
}
|
|
for(AUD_HandleIterator i = m_pausedSounds->begin();
|
|
i != m_pausedSounds->end(); i++)
|
|
{
|
|
if(*i == handle)
|
|
{
|
|
unlock();
|
|
return AUD_STATUS_PAUSED;
|
|
}
|
|
}
|
|
unlock();
|
|
return AUD_STATUS_INVALID;
|
|
}
|
|
|
|
void AUD_OpenALDevice::lock()
|
|
{
|
|
pthread_mutex_lock(&m_mutex);
|
|
}
|
|
|
|
void AUD_OpenALDevice::unlock()
|
|
{
|
|
pthread_mutex_unlock(&m_mutex);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**************************** Capabilities Code *******************************/
|
|
/******************************************************************************/
|
|
|
|
bool AUD_OpenALDevice::checkCapability(int capability)
|
|
{
|
|
return capability == AUD_CAPS_3D_DEVICE ||
|
|
capability == AUD_CAPS_VOLUME ||
|
|
capability == AUD_CAPS_SOURCE_VOLUME ||
|
|
capability == AUD_CAPS_SOURCE_PITCH ||
|
|
capability == AUD_CAPS_BUFFERED_FACTORY;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::setCapability(int capability, void *value)
|
|
{
|
|
switch(capability)
|
|
{
|
|
case AUD_CAPS_VOLUME:
|
|
alListenerf(AL_GAIN, *((float*)value));
|
|
return true;
|
|
case AUD_CAPS_SOURCE_VOLUME:
|
|
{
|
|
AUD_SourceCaps* caps = (AUD_SourceCaps*) value;
|
|
lock();
|
|
if(isValid(caps->handle))
|
|
{
|
|
alSourcef(((AUD_OpenALHandle*)caps->handle)->source,
|
|
AL_GAIN, caps->value);
|
|
unlock();
|
|
return true;
|
|
}
|
|
unlock();
|
|
}
|
|
break;
|
|
case AUD_CAPS_SOURCE_PITCH:
|
|
{
|
|
AUD_SourceCaps* caps = (AUD_SourceCaps*) value;
|
|
lock();
|
|
if(isValid(caps->handle))
|
|
{
|
|
alSourcef(((AUD_OpenALHandle*)caps->handle)->source,
|
|
AL_PITCH, caps->value);
|
|
unlock();
|
|
return true;
|
|
}
|
|
unlock();
|
|
}
|
|
break;
|
|
case AUD_CAPS_BUFFERED_FACTORY:
|
|
{
|
|
AUD_IFactory* factory = (AUD_IFactory*) value;
|
|
|
|
// load the factory into an OpenAL buffer
|
|
if(factory)
|
|
{
|
|
lock();
|
|
for(AUD_BFIterator i = m_bufferedFactories->begin();
|
|
i != m_bufferedFactories->end(); i++)
|
|
{
|
|
if((*i)->factory == factory)
|
|
{
|
|
unlock();
|
|
return true;
|
|
}
|
|
}
|
|
unlock();
|
|
|
|
AUD_IReader* reader = factory->createReader();
|
|
|
|
if(reader == NULL)
|
|
return false;
|
|
|
|
AUD_Specs specs;
|
|
|
|
specs = reader->getSpecs();
|
|
|
|
// determine format
|
|
bool valid = reader->getType() == AUD_TYPE_BUFFER;
|
|
|
|
if(valid)
|
|
{
|
|
if(specs.format == AUD_FORMAT_INVALID)
|
|
valid = false;
|
|
else if(specs.format == AUD_FORMAT_S24 ||
|
|
specs.format == AUD_FORMAT_S32 ||
|
|
specs.format == AUD_FORMAT_FLOAT32 ||
|
|
specs.format == AUD_FORMAT_FLOAT64)
|
|
{
|
|
m_converter->setReader(reader);
|
|
reader = m_converter->createReader();
|
|
specs = reader->getSpecs();
|
|
}
|
|
}
|
|
|
|
ALenum format;
|
|
|
|
if(valid)
|
|
valid = getFormat(format, specs);
|
|
|
|
if(!valid)
|
|
{
|
|
delete reader; AUD_DELETE("reader")
|
|
return false;
|
|
}
|
|
|
|
// load into a buffer
|
|
lock();
|
|
alcSuspendContext(m_context);
|
|
|
|
AUD_OpenALBufferedFactory* bf = new AUD_OpenALBufferedFactory;
|
|
AUD_NEW("bufferedfactory");
|
|
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_SAMPLE_SIZE(specs),
|
|
specs.rate);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
AUD_THROW(AUD_ERROR_OPENAL);
|
|
}
|
|
catch(AUD_Exception e)
|
|
{
|
|
alDeleteBuffers(1, &bf->buffer);
|
|
throw;
|
|
}
|
|
}
|
|
catch(AUD_Exception e)
|
|
{
|
|
delete bf; AUD_DELETE("bufferedfactory")
|
|
delete reader; AUD_DELETE("reader")
|
|
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();
|
|
AUD_DELETE("bufferedfactory");
|
|
m_bufferedFactories->erase(m_bufferedFactories->begin());
|
|
}
|
|
unlock();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::getCapability(int capability, void *value)
|
|
{
|
|
switch(capability)
|
|
{
|
|
case AUD_CAPS_VOLUME:
|
|
alGetListenerf(AL_GAIN, (float*)value);
|
|
return true;
|
|
case AUD_CAPS_SOURCE_VOLUME:
|
|
{
|
|
AUD_SourceCaps* caps = (AUD_SourceCaps*) value;
|
|
lock();
|
|
if(isValid(caps->handle))
|
|
{
|
|
alGetSourcef(((AUD_OpenALHandle*)caps->handle)->source,
|
|
AL_GAIN, &caps->value);
|
|
unlock();
|
|
return true;
|
|
}
|
|
unlock();
|
|
}
|
|
break;
|
|
case AUD_CAPS_SOURCE_PITCH:
|
|
{
|
|
AUD_SourceCaps* caps = (AUD_SourceCaps*) value;
|
|
lock();
|
|
if(isValid(caps->handle))
|
|
{
|
|
alGetSourcef(((AUD_OpenALHandle*)caps->handle)->source,
|
|
AL_PITCH, &caps->value);
|
|
unlock();
|
|
return true;
|
|
}
|
|
unlock();
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**************************** 3D Device Code **********************************/
|
|
/******************************************************************************/
|
|
|
|
AUD_Handle* AUD_OpenALDevice::play3D(AUD_IFactory* factory, bool keep)
|
|
{
|
|
AUD_OpenALHandle* handle = (AUD_OpenALHandle*)play(factory, keep);
|
|
if(handle)
|
|
alSourcei(handle->source, AL_SOURCE_RELATIVE, 0);
|
|
return handle;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::updateListener(AUD_3DData &data)
|
|
{
|
|
alListenerfv(AL_POSITION, (ALfloat*)data.position);
|
|
alListenerfv(AL_VELOCITY, (ALfloat*)data.velocity);
|
|
alListenerfv(AL_ORIENTATION, (ALfloat*)&(data.orientation[3]));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::setSetting(AUD_3DSetting setting, float value)
|
|
{
|
|
switch(setting)
|
|
{
|
|
case AUD_3DS_DISTANCE_MODEL:
|
|
if(value == AUD_DISTANCE_MODEL_NONE)
|
|
alDistanceModel(AL_NONE);
|
|
else if(value == AUD_DISTANCE_MODEL_INVERSE)
|
|
alDistanceModel(AL_INVERSE_DISTANCE);
|
|
else if(value == AUD_DISTANCE_MODEL_INVERSE_CLAMPED)
|
|
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
|
|
else if(value == AUD_DISTANCE_MODEL_LINEAR)
|
|
alDistanceModel(AL_LINEAR_DISTANCE);
|
|
else if(value == AUD_DISTANCE_MODEL_LINEAR_CLAMPED)
|
|
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
|
|
else if(value == AUD_DISTANCE_MODEL_EXPONENT)
|
|
alDistanceModel(AL_EXPONENT_DISTANCE);
|
|
else if(value == AUD_DISTANCE_MODEL_EXPONENT_CLAMPED)
|
|
alDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);
|
|
else
|
|
return false;
|
|
return true;
|
|
case AUD_3DS_DOPPLER_FACTOR:
|
|
alDopplerFactor(value);
|
|
return true;
|
|
case AUD_3DS_SPEED_OF_SOUND:
|
|
alSpeedOfSound(value);
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
float AUD_OpenALDevice::getSetting(AUD_3DSetting setting)
|
|
{
|
|
switch(setting)
|
|
{
|
|
case AUD_3DS_DISTANCE_MODEL:
|
|
switch(alGetInteger(AL_DISTANCE_MODEL))
|
|
{
|
|
case AL_NONE:
|
|
return AUD_DISTANCE_MODEL_NONE;
|
|
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;
|
|
}
|
|
case AUD_3DS_DOPPLER_FACTOR:
|
|
return alGetFloat(AL_DOPPLER_FACTOR);
|
|
case AUD_3DS_SPEED_OF_SOUND:
|
|
return alGetFloat(AL_SPEED_OF_SOUND);
|
|
default:
|
|
return std::numeric_limits<float>::quiet_NaN();
|
|
}
|
|
}
|
|
|
|
bool AUD_OpenALDevice::updateSource(AUD_Handle* handle, AUD_3DData &data)
|
|
{
|
|
lock();
|
|
|
|
if(isValid(handle))
|
|
{
|
|
int source = ((AUD_OpenALHandle*)handle)->source;
|
|
alSourcefv(source, AL_POSITION, (ALfloat*)data.position);
|
|
alSourcefv(source, AL_VELOCITY, (ALfloat*)data.velocity);
|
|
alSourcefv(source, AL_DIRECTION, (ALfloat*)&(data.orientation[3]));
|
|
unlock();
|
|
return true;
|
|
}
|
|
|
|
unlock();
|
|
return false;
|
|
}
|
|
|
|
bool AUD_OpenALDevice::setSourceSetting(AUD_Handle* handle,
|
|
AUD_3DSourceSetting setting,
|
|
float value)
|
|
{
|
|
lock();
|
|
|
|
bool result = false;
|
|
|
|
if(isValid(handle))
|
|
{
|
|
int source = ((AUD_OpenALHandle*)handle)->source;
|
|
|
|
switch(setting)
|
|
{
|
|
case AUD_3DSS_CONE_INNER_ANGLE:
|
|
alSourcef(source, AL_CONE_INNER_ANGLE, value);
|
|
result = true;
|
|
break;
|
|
case AUD_3DSS_CONE_OUTER_ANGLE:
|
|
alSourcef(source, AL_CONE_OUTER_ANGLE, value);
|
|
result = true;
|
|
break;
|
|
case AUD_3DSS_CONE_OUTER_GAIN:
|
|
alSourcef(source, AL_CONE_OUTER_GAIN, value);
|
|
result = true;
|
|
break;
|
|
case AUD_3DSS_IS_RELATIVE:
|
|
alSourcei(source, AL_SOURCE_RELATIVE, value > 0.0);
|
|
result = true;
|
|
break;
|
|
case AUD_3DSS_MAX_DISTANCE:
|
|
alSourcef(source, AL_MAX_DISTANCE, value);
|
|
result = true;
|
|
break;
|
|
case AUD_3DSS_MAX_GAIN:
|
|
alSourcef(source, AL_MAX_GAIN, value);
|
|
result = true;
|
|
break;
|
|
case AUD_3DSS_MIN_GAIN:
|
|
alSourcef(source, AL_MIN_GAIN, value);
|
|
result = true;
|
|
break;
|
|
case AUD_3DSS_REFERENCE_DISTANCE:
|
|
alSourcef(source, AL_REFERENCE_DISTANCE, value);
|
|
result = true;
|
|
break;
|
|
case AUD_3DSS_ROLLOFF_FACTOR:
|
|
alSourcef(source, AL_ROLLOFF_FACTOR, value);
|
|
result = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
unlock();
|
|
return result;
|
|
}
|
|
|
|
float AUD_OpenALDevice::getSourceSetting(AUD_Handle* handle,
|
|
AUD_3DSourceSetting setting)
|
|
{
|
|
float result = std::numeric_limits<float>::quiet_NaN();;
|
|
|
|
lock();
|
|
|
|
if(isValid(handle))
|
|
{
|
|
int source = ((AUD_OpenALHandle*)handle)->source;
|
|
|
|
switch(setting)
|
|
{
|
|
case AUD_3DSS_CONE_INNER_ANGLE:
|
|
alGetSourcef(source, AL_CONE_INNER_ANGLE, &result);
|
|
break;
|
|
case AUD_3DSS_CONE_OUTER_ANGLE:
|
|
alGetSourcef(source, AL_CONE_OUTER_ANGLE, &result);
|
|
break;
|
|
case AUD_3DSS_CONE_OUTER_GAIN:
|
|
alGetSourcef(source, AL_CONE_OUTER_GAIN, &result);
|
|
break;
|
|
case AUD_3DSS_IS_RELATIVE:
|
|
{
|
|
ALint i;
|
|
alGetSourcei(source, AL_SOURCE_RELATIVE, &i);
|
|
result = i ? 1.0 : 0.0;
|
|
break;
|
|
}
|
|
case AUD_3DSS_MAX_DISTANCE:
|
|
alGetSourcef(source, AL_MAX_DISTANCE, &result);
|
|
break;
|
|
case AUD_3DSS_MAX_GAIN:
|
|
alGetSourcef(source, AL_MAX_GAIN, &result);
|
|
break;
|
|
case AUD_3DSS_MIN_GAIN:
|
|
alGetSourcef(source, AL_MIN_GAIN, &result);
|
|
break;
|
|
case AUD_3DSS_REFERENCE_DISTANCE:
|
|
alGetSourcef(source, AL_REFERENCE_DISTANCE, &result);
|
|
break;
|
|
case AUD_3DSS_ROLLOFF_FACTOR:
|
|
alGetSourcef(source, AL_ROLLOFF_FACTOR, &result);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
unlock();
|
|
return result;
|
|
}
|