sfxALBuffer.cpp
Engine/source/sfx/openal/sfxALBuffer.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "sfx/openal/sfxALBuffer.h" 25#include "sfx/openal/sfxALVoice.h" 26#include "sfx/openal/sfxALDevice.h" 27#include "sfx/sfxDescription.h" 28#include "console/console.h" 29 30 31//#define DEBUG_SPEW 32 33 34SFXALBuffer* SFXALBuffer::create( const OPENALFNTABLE &oalft, 35 const ThreadSafeRef< SFXStream>& stream, 36 SFXDescription* description, 37 bool useHardware ) 38{ 39 if( !_sfxFormatToALFormat( stream->getFormat() ) ) 40 { 41 Con::errorf( "SFXALBuffer::create() - SFXFormat not supported by OpenAL" ); 42 return NULL; 43 } 44 45 SFXALBuffer *buffer = new SFXALBuffer( oalft, 46 stream, 47 description, 48 useHardware ); 49 50 return buffer; 51} 52 53SFXALBuffer::SFXALBuffer( const OPENALFNTABLE &oalft, 54 const ThreadSafeRef< SFXStream>& stream, 55 SFXDescription* description, 56 bool useHardware ) 57 : Parent( stream, description ), 58 mIs3d( description->mIs3D ), 59 mUseHardware( useHardware ), 60 mOpenAL( oalft ) 61{ 62 // Set up device buffers. 63 64 if( !isStreaming() ) 65 mOpenAL.alGenBuffers( 1, &mALBuffer ); 66} 67 68SFXALBuffer::~SFXALBuffer() 69{ 70 if( _getUniqueVoice() ) 71 _getUniqueVoice()->stop(); 72 73 // Release buffers. 74 if ( mOpenAL.alIsBuffer( mALBuffer )) 75 mOpenAL.alDeleteBuffers( 1, &mALBuffer ); 76 77 while( mFreeBuffers.size() ) 78 { 79 ALuint buffer = mFreeBuffers.last(); 80 mOpenAL.alDeleteBuffers( 1, &buffer ); 81 mFreeBuffers.pop_back(); 82 } 83} 84 85void SFXALBuffer::write( SFXInternal::SFXStreamPacket* const* packets, U32 num ) 86{ 87 using namespace SFXInternal; 88 89 if( !num ) 90 return; 91 92 // If this is not a streaming buffer, just load the data into our single 93 // static buffer. 94 95 if( !isStreaming() ) 96 { 97 SFXStreamPacket* packet = packets[ num - 1 ]; 98 99 ALenum alFormat = _sfxFormatToALFormat( getFormat() ); 100 AssertFatal( alFormat != 0, "SFXALBuffer::write() - format unsupported" ); 101 102 mOpenAL.alBufferData( mALBuffer, alFormat, 103 packet->data, packet->mSizeActual, getFormat().getSamplesPerSecond() ); 104 105 destructSingle( packet ); 106 return; 107 } 108 109 MutexHandle mutex; 110 mutex.lock( &_getUniqueVoice()->mMutex, true ); 111 112 // Unqueue processed packets. 113 114 ALuint source = _getUniqueVoice()->mSourceName; 115 ALint numProcessed; 116 mOpenAL.alGetSourcei( source, AL_BUFFERS_PROCESSED, &numProcessed ); 117 118 for( U32 i = 0; i < numProcessed; ++ i ) 119 { 120 // Unqueue the buffer. 121 122 ALuint buffer; 123 mOpenAL.alSourceUnqueueBuffers( source, 1, &buffer ); 124 125 // Update the sample offset on the voice. 126 127 ALint size; 128 mOpenAL.alGetBufferi( buffer, AL_SIZE, &size ); 129 _getUniqueVoice()->mSampleOffset += size / getFormat().getBytesPerSample(); 130 131 // Push the buffer onto the freelist. 132 133 mFreeBuffers.push_back( buffer ); 134 } 135 136 // Queue buffers. 137 138 for( U32 i = 0; i < num; ++ i ) 139 { 140 SFXStreamPacket* packet = packets[ i ]; 141 142 // Allocate a buffer. 143 144 ALuint buffer; 145 if( mFreeBuffers.size() ) 146 { 147 buffer = mFreeBuffers.last(); 148 mFreeBuffers.pop_back(); 149 } 150 else 151 mOpenAL.alGenBuffers( 1, &buffer ); 152 153 // Upload the data. 154 155 ALenum alFormat = _sfxFormatToALFormat( getFormat() ); 156 AssertFatal( alFormat != 0, "SFXALBuffer::write() - format unsupported" ); 157 AssertFatal( mOpenAL.alIsBuffer( buffer ), "SFXALBuffer::write() - buffer invalid" ); 158 159 mOpenAL.alBufferData( buffer, alFormat, 160 packet->data, packet->mSizeActual, getFormat().getSamplesPerSecond() ); 161 162 destructSingle( packet ); 163 164 // Queue the buffer. 165 166 mOpenAL.alSourceQueueBuffers( source, 1, &buffer ); 167 } 168} 169 170void SFXALBuffer::_flush() 171{ 172 AssertFatal( isStreaming(), "SFXALBuffer::_flush() - not a streaming buffer" ); 173 AssertFatal( SFXInternal::isSFXThread(), "SFXALBuffer::_flush() - not on SFX thread" ); 174 175 #ifdef DEBUG_SPEW 176 Platform::outputDebugString( "[SFXALBuffer] Flushing buffer" ); 177 #endif 178 179 _getUniqueVoice()->_stop(); 180 181 MutexHandle mutex; 182 mutex.lock( &_getUniqueVoice()->mMutex, true ); 183 184 ALuint source = _getUniqueVoice()->mSourceName; 185 186 ALint numQueued; 187 mOpenAL.alGetSourcei( source, AL_BUFFERS_QUEUED, &numQueued ); 188 189 for( U32 i = 0; i < numQueued; ++ i ) 190 { 191 ALuint buffer; 192 mOpenAL.alSourceUnqueueBuffers( source, 1, &buffer ); 193 mFreeBuffers.push_back( buffer ); 194 } 195 196 _getUniqueVoice()->mSampleOffset = 0; 197 198 //RD: disabling hack for now; rewritten queueing should be able to cope 199 #if 0 //def TORQUE_OS_MAC 200 201 //WORKAROUND: Ugly hack on Mac. Apparently there's a bug in the OpenAL implementation 202 // that will cause AL_BUFFERS_PROCESSED to not be reset as it should be causing write() 203 // to fail. Brute-force this and just re-create the source. Let's pray that nobody 204 // issues any concurrent state changes on the voice resulting in us losing state here. 205 206 ALuint newSource; 207 alGenSources( 1, &newSource ); 208 209 #define COPY_F( name ) \ 210 { \ 211 F32 val; \ 212 mOpenAL.alGetSourcef( source, name, &val ); \ 213 mOpenAL.alSourcef( source, name, val ); \ 214 } 215 216 #define COPY_FV( name ) \ 217 { \ 218 VectorF val; \ 219 mOpenAL.alGetSourcefv( source, name, val ); \ 220 mOpenAL.alSourcefv( source, name, val ); \ 221 } 222 223 COPY_F( AL_REFERENCE_DISTANCE ); 224 COPY_F( AL_MAX_DISTANCE ); 225 COPY_F( AL_GAIN ); 226 COPY_F( AL_PITCH ); 227 COPY_F( AL_CONE_INNER_ANGLE ); 228 COPY_F( AL_CONE_OUTER_ANGLE ); 229 COPY_F( AL_CONE_OUTER_GAIN ); 230 231 COPY_FV( AL_VELOCITY ); 232 COPY_FV( AL_POSITION ); 233 COPY_FV( AL_DIRECTION ); 234 235 mSourceName = newSource; 236 source ); 237 238 #endif 239} 240