Newer
Older
* ***** BEGIN GPL LICENSE BLOCK *****
* Copyright 2009-2011 Jörg Hermann Müller
* 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 <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.";
bool AUD_OpenALDevice::AUD_OpenALHandle::pause(bool keep)
{
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 = keep ? AUD_STATUS_STOPPED : AUD_STATUS_PAUSED;
return true;
}
}
}
}
return false;}
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());
if(length == 0)
{
// AUD_XXX: TODO: don't fill all buffers and enqueue them later
length = 1;
memset(m_device->m_buffer.getBuffer(), 0, length * AUD_DEVICE_SAMPLE_SIZE(specs));
}
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);
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()
{
return pause(false);
}
bool AUD_OpenALDevice::AUD_OpenALHandle::resume()
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;
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)
{
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;
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;
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
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());
if(length == 0)
{
// AUD_XXX: TODO: don't fill all buffers and enqueue them later
length = 1;
memset(m_device->m_buffer.getBuffer(), 0, length * AUD_DEVICE_SAMPLE_SIZE(specs));
}
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);
}
}
if(m_status == AUD_STATUS_STOPPED)
m_status = AUD_STATUS_PAUSED;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getPosition()
{
if(!m_status)
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 result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setVolume(float volume)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getPitch()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setPitch(float pitch)
{
if(!m_status)
return false;
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;
if(m_status == AUD_STATUS_STOPPED && (count > m_loopcount || count < 0))
m_status = AUD_STATUS_PAUSED;
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)
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;
return true;
}
AUD_Vector3 AUD_OpenALDevice::AUD_OpenALHandle::getSourceVelocity()
{
AUD_Vector3 result = AUD_Vector3(0, 0, 0);
if(!m_status)
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;
return true;
}
AUD_Quaternion AUD_OpenALDevice::AUD_OpenALHandle::getSourceOrientation()
{
}
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;
AUD_MutexLock lock(*m_device);
if(!m_status)
return false;
alSourcefv(m_source, AL_DIRECTION, direction);
return true;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::isRelative()
{
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setRelative(bool relative)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getVolumeMaximum()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setVolumeMaximum(float volume)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getVolumeMinimum()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setVolumeMinimum(float volume)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getDistanceMaximum()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setDistanceMaximum(float distance)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getDistanceReference()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setDistanceReference(float distance)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getAttenuation()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setAttenuation(float factor)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getConeAngleOuter()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setConeAngleOuter(float angle)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getConeAngleInner()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setConeAngleInner(float angle)
{
if(!m_status)
return false;
return true;
}
float AUD_OpenALDevice::AUD_OpenALHandle::getConeVolumeOuter()
{
float result = std::numeric_limits<float>::quiet_NaN();
if(!m_status)
return result;
}
bool AUD_OpenALDevice::AUD_OpenALHandle::setConeVolumeOuter(float volume)
{
if(!m_status)
return false;
/******************************************************************************/
/**************************** Threading Code **********************************/
/******************************************************************************/
static void *AUD_openalRunThread(void *device)
{
AUD_OpenALDevice* dev = (AUD_OpenALDevice*)device;
dev->updateStreams();
return 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()
{
std::list<boost::shared_ptr<AUD_OpenALHandle> > stopSounds;
std::list<boost::shared_ptr<AUD_OpenALHandle> > pauseSounds;
while(1)
{
lock();
alcSuspendContext(m_context);
for(it = m_playingSounds.begin(); it != m_playingSounds.end(); it++)
alGetSourcei(sound->m_source, AL_BUFFERS_PROCESSED, &info);
m_buffer.assureSize(m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));
// for all empty buffers
while(info--)
{
// if there's still data to play back
sound->m_reader->read(length, sound->m_eos, m_buffer.getBuffer());
sound->m_reader->read(length, sound->m_eos, m_buffer.getBuffer());
// unqueue buffer (warning: this might fail for slow early returning sources (none exist so far) if the buffer was not queued due to recent changes - has to be tested)
alSourceUnqueueBuffers(sound->m_source, 1, &sound->m_buffers[sound->m_current]);
alBufferData(sound->m_buffers[sound->m_current],
sound->m_format,
alSourceQueueBuffers(sound->m_source, 1,
&sound->m_buffers[sound->m_current]);
sound->m_current = (sound->m_current+1) %
AUD_OpenALHandle::CYCLE_BUFFERS;
if(sound->m_eos && info != AL_INITIAL)
if(sound->m_stop)
sound->m_stop(sound->m_stop_data);