//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "sfx/openal/sfxALDevice.h" #include "sfx/openal/sfxALBuffer.h" #include "platform/async/asyncUpdate.h" //----------------------------------------------------------------------------- SFXALDevice::SFXALDevice( SFXProvider *provider, const OPENALFNTABLE &openal, String name, bool useHardware, S32 maxBuffers ) : Parent( name, provider, useHardware, maxBuffers ), mOpenAL( openal ), mDevice( NULL ), mContext( NULL ), mRolloffFactor( 1.0f ) { mMaxBuffers = getMax( maxBuffers, 8 ); // TODO: The OpenAL device doesn't set the primary buffer // $pref::SFX::frequency or $pref::SFX::bitrate! mDevice = mOpenAL.alcOpenDevice( name ); mOpenAL.alcGetError( mDevice ); if( mDevice ) { mContext = mOpenAL.alcCreateContext( mDevice, NULL ); if( mContext ) mOpenAL.alcMakeContextCurrent( mContext ); U32 err = mOpenAL.alcGetError( mDevice ); if( err != ALC_NO_ERROR ) Con::errorf( "SFXALDevice - Initialization Error: %s", mOpenAL.alcGetString( mDevice, err ) ); } AssertFatal( mDevice != NULL && mContext != NULL, "Failed to create OpenAL device and/or context!" ); // Start the update thread. #ifndef TORQUE_OS_LINUX if( !Con::getBoolVariable( "$_forceAllMainThread" ) ) { SFXInternal::gUpdateThread = new AsyncPeriodicUpdateThread ( "OpenAL Update Thread", SFXInternal::gBufferUpdateList, Con::getIntVariable( "$pref::SFX::updateInterval", SFXInternal::DEFAULT_UPDATE_INTERVAL ) ); SFXInternal::gUpdateThread->start(); } #endif } //----------------------------------------------------------------------------- SFXALDevice::~SFXALDevice() { _releaseAllResources(); mOpenAL.alcMakeContextCurrent( NULL ); mOpenAL.alcDestroyContext( mContext ); mOpenAL.alcCloseDevice( mDevice ); } //----------------------------------------------------------------------------- SFXBuffer* SFXALDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) { AssertFatal( stream, "SFXALDevice::createBuffer() - Got null stream!" ); AssertFatal( description, "SFXALDevice::createBuffer() - Got null description!" ); SFXALBuffer* buffer = SFXALBuffer::create( mOpenAL, stream, description, mUseHardware ); if ( !buffer ) return NULL; _addBuffer( buffer ); return buffer; } //----------------------------------------------------------------------------- SFXVoice* SFXALDevice::createVoice( bool is3D, SFXBuffer *buffer ) { // Don't bother going any further if we've // exceeded the maximum voices. if ( mVoices.size() >= mMaxBuffers ) return NULL; AssertFatal( buffer, "SFXALDevice::createVoice() - Got null buffer!" ); SFXALBuffer* alBuffer = dynamic_cast( buffer ); AssertFatal( alBuffer, "SFXALDevice::createVoice() - Got bad buffer!" ); SFXALVoice* voice = SFXALVoice::create( this, alBuffer ); if ( !voice ) return NULL; _addVoice( voice ); return voice; } //----------------------------------------------------------------------------- void SFXALDevice::setListener( U32 index, const SFXListenerProperties& listener ) { if( index != 0 ) return; // Torque and OpenAL are both right handed // systems, so no coordinate flipping is needed. const MatrixF &transform = listener.getTransform(); Point3F pos, tupple[2]; transform.getColumn( 3, &pos ); transform.getColumn( 1, &tupple[0] ); transform.getColumn( 2, &tupple[1] ); const VectorF &velocity = listener.getVelocity(); mOpenAL.alListenerfv( AL_POSITION, pos ); mOpenAL.alListenerfv( AL_VELOCITY, velocity ); mOpenAL.alListenerfv( AL_ORIENTATION, (const F32 *)&tupple[0] ); } //----------------------------------------------------------------------------- void SFXALDevice::setDistanceModel( SFXDistanceModel model ) { switch( model ) { case SFXDistanceModelLinear: mOpenAL.alDistanceModel( AL_LINEAR_DISTANCE_CLAMPED ); if( mRolloffFactor != 1.0f ) _setRolloffFactor( 1.0f ); // No rolloff on linear. break; case SFXDistanceModelLogarithmic: mOpenAL.alDistanceModel( AL_INVERSE_DISTANCE_CLAMPED ); if( mUserRolloffFactor != mRolloffFactor ) _setRolloffFactor( mUserRolloffFactor ); break; default: AssertWarn( false, "SFXALDevice::setDistanceModel - distance model not implemented" ); } mDistanceModel = model; } //----------------------------------------------------------------------------- void SFXALDevice::setDopplerFactor( F32 factor ) { mOpenAL.alDopplerFactor( factor ); } //----------------------------------------------------------------------------- void SFXALDevice::_setRolloffFactor( F32 factor ) { mRolloffFactor = factor; for( U32 i = 0, num = mVoices.size(); i < num; ++ i ) mOpenAL.alSourcef( ( ( SFXALVoice* ) mVoices[ i ] )->mSourceName, AL_ROLLOFF_FACTOR, factor ); } //----------------------------------------------------------------------------- void SFXALDevice::setRolloffFactor( F32 factor ) { if( mDistanceModel == SFXDistanceModelLinear && factor != 1.0f ) Con::errorf( "SFXALDevice::setRolloffFactor - rolloff factor <> 1.0f ignored in linear distance model" ); else _setRolloffFactor( factor ); mUserRolloffFactor = factor; }