1/********************************************************************************************************************************** 2* 3* OpenAL cross platform audio library 4* Copyright (c) 2004, Apple Computer, Inc. All rights reserved. 5* 6* Redistribution and use in source and binary forms, with or without modification, are permitted provided 7* that the following conditions are met: 8* 9* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 11* disclaimer in the documentation and/or other materials provided with the distribution. 12* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its contributors may be used to endorse or promote 13* products derived from this software without specific prior written permission. 14* 15* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 17* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 18* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 19* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 20* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21* 22**********************************************************************************************************************************/ 23 24#include "oalBuffer.h" 25#include "oalSource.h" 26#include "oalImp.h" 27 28#include "CAGuard.h" 29 30#define LOG_VERBOSE 0 31 32// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33#pragma mark _____Support Methods_____ 34// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 36// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 37// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 38// OALBuffers 39// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 40// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 41#pragma mark ***** OALBuffers ***** 42OALBuffer::OALBuffer (ALuint inSelfToken) 43 : mSelfToken (inSelfToken), 44#if USE_SOURCE_LIST_MUTEX 45 mSourceListGuard ("OALBuffer::SourceListGuard"), 46#endif 47 mBufferLock ("OALBuffer::EditLock"), 48 mInUseFlag(0), 49 mData(NULL), 50 mAppOwnsBufferMemory(false), 51 mDataSize (0), 52 mPreConvertedDataSize(0), 53 mDataHasBeenConverted(false), 54 mAttachedSourceList(NULL), 55 mIsInPostRenderMessageQueue(false) 56{ 57#if LOG_VERBOSE 58 DebugMessageN1("OALBuffer::OALBuffer() - OALBuffer = %ld", (long int) mSelfToken); 59#endif 60 mAttachedSourceList = new AttachedSourceList (); // create a source list map 61 mAttachedSourceList->Reserve(128); 62} 63 64// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 65OALBuffer::~OALBuffer() 66{ 67#if LOG_VERBOSE 68 DebugMessageN1("OALBuffer::~OALBuffer() - OALBuffer = %ld", (long int) mSelfToken); 69#endif 70 if (!mAppOwnsBufferMemory && (mData != NULL)) 71 free (mData); 72 73// should never be deleted unless this object said it was ok 74#if USE_SOURCE_LIST_MUTEX 75 bool wasLocked = mSourceListGuard.Lock(); 76#endif 77 78 if (mAttachedSourceList) 79 delete mAttachedSourceList; 80 81#if USE_SOURCE_LIST_MUTEX 82 if (wasLocked) mSourceListGuard.Unlock(); 83#endif 84} 85// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 86bool OALBuffer::CanBeRemovedFromBufferMap() 87{ 88#if LOG_VERBOSE 89 DebugMessageN1("OALBuffer::CanBeRemovedFromBufferMap() - OALBuffer = %ld", (long int) mSelfToken); 90#endif 91 bool returnValue = true; 92 93#if USE_SOURCE_LIST_MUTEX 94 bool wasLocked = mSourceListGuard.Lock(); 95#endif 96 97 // if the buffer object is not attached to a source, it's ok to remove 98 if (mAttachedSourceList->Size() == 0) 99 returnValue = true; 100 else 101 { 102 // if all attached sources are transitioning to flush, it can be removed from buffer map, 103 // but the OALBuffer object must be deleted at a later time 104 for (UInt32 i = 0; i < mAttachedSourceList->Size(); i++) 105 { 106 OALSource *curSource = mAttachedSourceList->GetSourceByIndex(i); 107 if (curSource) 108 { 109 if (!curSource->IsSourceTransitioningToFlushQ()) 110 { 111 returnValue = false; 112 goto Finished; 113 } 114 } 115 } 116 } 117 118Finished: 119 120#if USE_SOURCE_LIST_MUTEX 121 if (wasLocked) mSourceListGuard.Unlock(); 122#endif 123 124 return returnValue; // all attached sources are transitioning to flush 125} 126 127// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 128bool OALBuffer::IsPurgable() 129{ 130#if LOG_VERBOSE 131 DebugMessageN1("OALBuffer::IsPurgable() - OALBuffer = %ld", (long int) mSelfToken); 132#endif 133 bool returnValue = false; 134 135#if USE_SOURCE_LIST_MUTEX 136 bool wasLocked = mSourceListGuard.Lock(); 137#endif 138 139 // make sure that no other source has attached this buffer, and that no other thread is editing it 140 if ((mAttachedSourceList->Size() == 0) && mBufferLock.IsFree() && (mInUseFlag <= 0) && (!IsInPostRenderMessageQueue())) 141 returnValue = true; 142 143#if USE_SOURCE_LIST_MUTEX 144 if (wasLocked) mSourceListGuard.Unlock(); 145#endif 146 147 return returnValue; 148} 149 150// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 151OSStatus OALBuffer::AddAudioDataStatic(char* inAudioData, UInt32 inAudioDataSize, ALenum format, ALsizei freq) 152{ 153#if LOG_VERBOSE 154 DebugMessageN5("OALBuffer::AddAudioDataStatic() - OALBuffer:inAudioData:inAudioDataSize:format:freq = %ld:%p:%ld:%d:%d", (long int) mSelfToken, inAudioData, (long int) inAudioDataSize, format, freq); 155#endif 156 #if LOG_EXTRAS 157 DebugMessage("AddAudioDataStatic called: Converting Data Now"); 158 #endif 159 160 CAGuard::Locker bufferLock(mBufferLock); 161 162 try { 163 164 if (!IsFormatSupported(format)) 165 throw ((OSStatus) AL_INVALID_VALUE); // this is not a valid buffer token or is an invalid format 166 167 // don't allow if the buffer is in a queue 168 if (mAttachedSourceList->Size() > 0) 169 { 170 DebugMessage("AddAudioDataStatic ATTACHMENT > 0"); 171 throw ((OSStatus) AL_INVALID_OPERATION); 172 } 173 174 mPreConvertedDataSize = (UInt32) inAudioDataSize; 175 OSStatus result = noErr; 176 177 // if this buffer was using memory created by the library, free it now and initialize mData 178 if (!mAppOwnsBufferMemory && (mData != NULL)) 179 { 180 free (mData); 181 mData = NULL; 182 } 183 184 mData = (UInt8*) inAudioData; 185 mDataSize = (UInt32) inAudioDataSize; 186 187 result = FillInASBD(mDataFormat, format, freq); 188 THROW_RESULT 189 190 mPreConvertedDataFormat.SetFrom(mDataFormat); // make sure they are the same so original format info can be returned to caller 191 } 192 catch (OSStatus result) { 193 mData = NULL; 194 mAppOwnsBufferMemory = false; 195 DebugMessageN1("AddAudioDataStatic Failed - err = %ld\n", (long int) result); 196 alSetError(result); 197 } 198 catch (...) { 199 mData = NULL; 200 mAppOwnsBufferMemory = false; 201 DebugMessage("AddAudioDataStatic Failed"); 202 alSetError(AL_INVALID_OPERATION); 203 } 204 205 mAppOwnsBufferMemory = true; 206 return noErr; 207} 208 209 210// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 211OSStatus OALBuffer::AddAudioData(char* inAudioData, UInt32 inAudioDataSize, ALenum format, ALsizei freq, bool inPreConvertToHalFormat) 212{ 213#if LOG_VERBOSE 214 DebugMessageN6("OALBuffer::AddAudioData() - OALBuffer:inAudioData:inAudioDataSize:format:freq:inPreConvertToHalFormat = %ld:%p:%ld:%d:%d:%d", (long int) mSelfToken, inAudioData, (long int) inAudioDataSize, format, freq, inPreConvertToHalFormat); 215#endif 216 // creates memory if needed 217 // reallocs if needed 218 // returns an error if buffer is in use 219 220 CAGuard::Locker bufferLock(mBufferLock); 221 222 try { 223 if (!IsFormatSupported(format)) 224 throw ((OSStatus) AL_INVALID_VALUE); // this is not a valid buffer token or is an invalid format 225 226#if USE_SOURCE_LIST_MUTEX 227 bool wasLocked = mSourceListGuard.Lock(); 228#endif 229 // don't allow if the buffer is in a queue 230 UInt32 attachedCount = mAttachedSourceList->Size(); 231 232#if USE_SOURCE_LIST_MUTEX 233 if (wasLocked) mSourceListGuard.Unlock(); 234#endif 235 236 if (attachedCount > 0) 237 { 238 DebugMessage("WAITING: AddAudioData ---> WaitOneRenderCycle"); 239 // Let a render cycle go by and try again 240 241 WaitOneRenderCycle(); 242 243#if USE_SOURCE_LIST_MUTEX 244 wasLocked = mSourceListGuard.Lock(); 245#endif 246 attachedCount = mAttachedSourceList->Size(); 247 248#if USE_SOURCE_LIST_MUTEX 249 if (wasLocked) mSourceListGuard.Unlock(); 250#endif 251 252 if (attachedCount > 0){ 253 DebugMessageN2("OALBuffer::AddAudioData: buffer ATTACHMENT > 0 - mSelfToken:mAttachedSourceList->Size() = %ld:%ld", (long int) mSelfToken, (long int) mAttachedSourceList->Size()); 254 throw ((OSStatus) AL_INVALID_OPERATION); 255 } 256 } 257 258 if (mAppOwnsBufferMemory) 259 { 260 mData = NULL; // we were using the apps memory before so just initialize mData incase we fail 261 mAppOwnsBufferMemory = false; 262 } 263 264 mPreConvertedDataSize = (UInt32) inAudioDataSize; 265 // do not pre-convert stereo sounds, let the AC do the deinterleaving 266 OSStatus result = noErr; 267 if (!inPreConvertToHalFormat || ((format == AL_FORMAT_STEREO16) || (format == AL_FORMAT_STEREO8))) 268 { 269 if (mData != NULL) 270 { 271 if (mDataSize != (UInt32) inAudioDataSize) 272 { 273 mDataSize = (UInt32) inAudioDataSize; 274 void *newDataPtr = realloc(mData, mDataSize); 275 mData = (UInt8 *) newDataPtr; 276 } 277 } 278 else 279 { 280 mDataSize = (UInt32) inAudioDataSize; 281 mData = (UInt8 *) malloc (mDataSize); 282 } 283 284 if (mData) 285 { 286 result = FillInASBD(mDataFormat, format, freq); 287 THROW_RESULT 288 289 mPreConvertedDataFormat.SetFrom(mDataFormat); // make sure they are the same so original format info can be returned to caller 290 memcpy (mData, inAudioData, mDataSize); 291 } 292 } 293 else 294 { 295 #if LOG_EXTRAS 296 DebugMessage("alBufferData called: Converting Data Now"); 297 #endif 298 299 result = ConvertDataForBuffer(inAudioData, inAudioDataSize, format, freq); // convert the data to the mixer's format and copy to the buffer 300 THROW_RESULT 301 } 302 } 303 catch (OSStatus result) { 304 DebugMessageN1("OALBuffer::AddAudioData Failed - err = %ld\n", (long int) result); 305 alSetError(result); 306 throw result; 307 } 308 catch (...) { 309 DebugMessage("OALBuffer::AddAudioData Failed"); 310 alSetError(AL_INVALID_OPERATION); 311 throw -1; 312 } 313 314 return noErr; 315} 316 317// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 318// This currently will only work for cbr formats 319OSStatus OALBuffer::ConvertDataForBuffer(void *inData, UInt32 inDataSize, UInt32 inDataFormat, UInt32 inDataSampleRate) 320{ 321#if LOG_VERBOSE 322 DebugMessageN5("OALBuffer::ConvertDataForBuffer() - OALBuffer:inData:inDataSize:inDataFormat:inDataSampleRate = %ld:%p:%ld:%ld:%ld", (long int) mSelfToken, inData, (long int) inDataSize, (long int) inDataFormat, (long int) inDataSampleRate); 323#endif 324 OSStatus result = noErr; 325 326 try { 327 328 AudioConverterRef converter; 329 CAStreamBasicDescription destFormat; 330 UInt32 framesOfSource = 0; 331 332 if (inData == NULL) 333 throw ((OSStatus) AL_INVALID_OPERATION); 334 335 result = FillInASBD(mPreConvertedDataFormat, inDataFormat, inDataSampleRate); 336 THROW_RESULT 337 338 if (mPreConvertedDataFormat.NumberChannels() == 1) 339 mPreConvertedDataFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 340 341 destFormat.mChannelsPerFrame = mPreConvertedDataFormat.NumberChannels(); 342 destFormat.mSampleRate = mPreConvertedDataFormat.mSampleRate; 343 destFormat.mFormatID = kAudioFormatLinearPCM; 344 345 if (mPreConvertedDataFormat.NumberChannels() == 1) 346 destFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; 347 else 348 destFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; // leave stereo data interleaved, and an AC will be used for deinterleaving later on 349 350 destFormat.mFramesPerPacket = 1; 351 destFormat.mBitsPerChannel = sizeof (Float32) * 8; 352 destFormat.mBytesPerPacket = sizeof (Float32) * destFormat.NumberChannels(); 353 destFormat.mBytesPerFrame = sizeof (Float32) * destFormat.NumberChannels(); 354 355 result = FillInASBD(mDataFormat, inDataFormat, UInt32(destFormat.mSampleRate)); 356 THROW_RESULT 357 358 result = AudioConverterNew(&mPreConvertedDataFormat, &destFormat, &converter); 359 THROW_RESULT 360 361 framesOfSource = inDataSize / mPreConvertedDataFormat.mBytesPerFrame; // THIS ONLY WORKS FOR CBR FORMATS 362 363 UInt32 dataSize = framesOfSource * sizeof(Float32) * destFormat.NumberChannels(); 364 365 mDataSize = (UInt32) dataSize; 366 367 if (mData != NULL) 368 { 369 if (mDataSize != dataSize) 370 { 371 mDataSize = dataSize; 372 void *newDataPtr = realloc(mData, mDataSize); 373 if (newDataPtr == NULL) 374 throw ((OSStatus) AL_INVALID_OPERATION); 375 376 mData = (UInt8 *) newDataPtr; 377 } 378 } 379 else 380 { 381 mDataSize = dataSize; 382 mData = (UInt8 *) malloc (mDataSize); 383 if (mData == NULL) 384 throw ((OSStatus) AL_INVALID_OPERATION); 385 } 386 387 if (mData != NULL) 388 { 389 result = AudioConverterConvertBuffer(converter, inDataSize, inData, &mDataSize, mData); 390 if (result == noErr) 391 { 392 mDataFormat.SetFrom(destFormat); 393 if (mPreConvertedDataFormat.NumberChannels() == 1) 394 mDataHasBeenConverted = true; 395 else 396 mDataHasBeenConverted = false; 397 } 398 } 399 400 AudioConverterDispose(converter); 401 } 402 catch (OSStatus result) { 403 return (result); 404 } 405 catch (...) { 406 result = (OSStatus) AL_INVALID_OPERATION; 407 } 408 409 return (result); 410} 411 412// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 413// Methods called from OALSource objects 414// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 415UInt32 OALBuffer::GetFramesToBytes(UInt32 inOffsetInFrames) 416{ 417#if LOG_VERBOSE 418 DebugMessageN2("OALBuffer::GetFramesToBytes() - OALBuffer:inOffsetInFrames = %ld:%ld", (long int) mSelfToken, (long int) inOffsetInFrames); 419#endif 420 UInt32 returnValue = 0; 421 422 if (GetFramesPerPacket() == 1) 423 { 424 // pcm - it's easy 425 returnValue = inOffsetInFrames * GetBytesPerPacket(); 426 } 427 else 428 { 429 // discover which packet conatins the frame we want to offset to 430 // CURRENTLY UNNECESSARY BECAUSE WE ARE ONLY STORING PCM DATA 431 } 432 433 return returnValue; 434} 435 436// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 437UInt32 OALBuffer::GetFrameCount() 438{ 439#if LOG_VERBOSE 440 DebugMessageN1("OALBuffer::GetFrameCount() - OALBuffer = %ld", (long int) mSelfToken); 441#endif 442 UInt32 totalPackets = (mDataFormat.mBytesPerPacket == 0) ? 0 : mDataSize/mDataFormat.mBytesPerPacket; 443 444 return totalPackets * mDataFormat.mFramesPerPacket; 445} 446 447// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 448bool OALBuffer::UseThisBuffer(OALSource* inSource) 449{ 450#if LOG_VERBOSE 451 DebugMessageN1("OALBuffer::UseThisBuffer() - OALBuffer = %ld", (long int) mSelfToken); 452#endif 453#if USE_SOURCE_LIST_MUTEX 454 bool wasLocked = mSourceListGuard.Lock(); 455#endif 456 457 // see if this source is in list already 458 if (mAttachedSourceList->SourceExists(inSource) == true) 459 mAttachedSourceList->IncreaseAttachmentCount(inSource); 460 else 461 { 462 SourceAttachedInfo nuSourceInfo; 463 464 nuSourceInfo.mSource = inSource; 465 nuSourceInfo.mAttachedCount = 1; 466 467 mAttachedSourceList->Add(nuSourceInfo); 468 } 469 470#if USE_SOURCE_LIST_MUTEX 471 if (wasLocked) mSourceListGuard.Unlock(); 472#endif 473 474#if LOG_EXTRAS 475 DebugMessageN2("OALBuffer::UseThisBuffer - BufferToken:SourceToken = %ld:%ld", (long int)mSelfToken, (long int)inSource->GetToken()); 476#endif 477 478 return noErr; 479} 480 481// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 482// Called by OALSource's BufferQueue::RemoveQueueEntryByIndex() class 483bool OALBuffer::ReleaseBuffer(OALSource* inSource) 484{ 485#if LOG_VERBOSE 486 DebugMessageN2("OALBuffer::ReleaseBuffer() - OALBuffer:inSource = %ld:%ld", (long int) mSelfToken, (long int) inSource->GetToken()); 487#endif 488 bool returnValue = false; 489#if LOG_EXTRAS 490 DebugMessageN2("OALBuffer::ReleaseBuffer - BufferToken:SourceToken = %ld:%ld", mSelfToken, inSource->GetToken()); 491#endif 492 493#if USE_SOURCE_LIST_MUTEX 494 bool wasLocked = mSourceListGuard.Lock(); 495#endif 496 497 // see if this source is in list already 498 if (mAttachedSourceList->SourceExists(inSource) == true) 499 { 500 UInt32 attachmentCount = mAttachedSourceList->DecreaseAttachmentCount(inSource); 501 if (attachmentCount == 0) 502 { 503 //DebugMessageN2("OALBuffer::ReleaseBuffer - BufferToken:SourceToken = %ld:%ld", (long int) mSelfToken, (long int) inSource->GetToken()); 504 mAttachedSourceList->Remove(inSource); 505 returnValue = true; 506 } 507 } 508 509#if USE_SOURCE_LIST_MUTEX 510 if (wasLocked) mSourceListGuard.Unlock(); 511#endif 512 513 return returnValue; 514}