/* * ***** 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 #include #ifdef WIN32 #include #else #include #endif /*struct AUD_OpenALBufferedFactory { /// The factory. AUD_IFactory* factory; /// The OpenAL buffer. ALuint buffer; };*/ //typedef std::list::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 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 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); 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() { return pause(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 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 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()); 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) 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::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::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; if(m_status == AUD_STATUS_STOPPED && (count > m_loopcount || count < 0)) m_status = AUD_STATUS_PAUSED; 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::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::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::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::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::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::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::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::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 sound; int length; ALint info; AUD_DeviceSpecs specs = m_specs; ALCenum cerr; std::list > stopSounds; std::list > 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 (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]); 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 && info != AL_INITIAL) { if(sound->m_stop) sound->m_stop(sound->m_stop_data); // pause or if(sound->m_keep) pauseSounds.push_back(sound); // stop else stopSounds.push_back(sound); } // continue playing else alSourcePlay(sound->m_source); } } for(it = pauseSounds.begin(); it != pauseSounds.end(); it++) (*it)->pause(true); 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(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(); 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_OpenALDevice::play(boost::shared_ptr reader, bool keep) { AUD_Specs specs = reader->getSpecs(); // check format if(specs.channels == AUD_CHANNELS_INVALID) return boost::shared_ptr(); if(m_specs.format != AUD_FORMAT_FLOAT32) reader = boost::shared_ptr(new AUD_ConverterReader(reader, m_specs)); ALenum format; if(!getFormat(format, specs)) return boost::shared_ptr(); AUD_MutexLock lock(*this); alcSuspendContext(m_context); boost::shared_ptr sound; try { // create the handle sound = boost::shared_ptr(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(sound); } boost::shared_ptr AUD_OpenALDevice::play(boost::shared_ptr 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); } }