this repo has no description
at fixPythonPipStalling 1241 lines 46 kB view raw
1/* Copyright: � Copyright 2005 Apple Computer, Inc. All rights reserved. 2 3 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. 4 ("Apple") in consideration of your agreement to the following terms, and your 5 use, installation, modification or redistribution of this Apple software 6 constitutes acceptance of these terms. If you do not agree with these terms, 7 please do not use, install, modify or redistribute this Apple software. 8 9 In consideration of your agreement to abide by the following terms, and subject 10 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s 11 copyrights in this original Apple software (the "Apple Software"), to use, 12 reproduce, modify and redistribute the Apple Software, with or without 13 modifications, in source and/or binary forms; provided that if you redistribute 14 the Apple Software in its entirety and without modifications, you must retain 15 this notice and the following text and disclaimers in all such redistributions of 16 the Apple Software. Neither the name, trademarks, service marks or logos of 17 Apple Computer, Inc. may be used to endorse or promote products derived from the 18 Apple Software without specific prior written permission from Apple. Except as 19 expressly stated in this notice, no other rights or licenses, express or implied, 20 are granted by Apple herein, including but not limited to any patent rights that 21 may be infringed by your derivative works or by other works in which the Apple 22 Software may be incorporated. 23 24 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO 25 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED 26 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR 27 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN 28 COMBINATION WITH YOUR PRODUCTS. 29 30 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR 31 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 32 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION 34 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT 35 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN 36 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37*/ 38/*============================================================================= 39 CAAudioFile.cpp 40 41=============================================================================*/ 42 43#include "CAAudioFile.h" 44 45#if !CAAF_USE_EXTAUDIOFILE 46 47#include "CAXException.h" 48#include <algorithm> 49#include "CAHostTimeBase.h" 50#include "CADebugMacros.h" 51 52#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) 53 #include <AudioToolbox/AudioToolbox.h> 54#else 55 #include <AudioFormat.h> 56#endif 57 58#if DEBUG 59 //#define VERBOSE_IO 1 60 //#define VERBOSE_CONVERTER 1 61 //#define VERBOSE_CHANNELMAP 1 62 //#define LOG_FUNCTION_ENTRIES 1 63 64 #if VERBOSE_CHANNELMAP 65 #include "CAChannelLayouts.h" // this is in Source/Tests/AudioFileTools/Utility 66 #endif 67#endif 68 69#if LOG_FUNCTION_ENTRIES 70 class FunctionLogger { 71 public: 72 FunctionLogger(const char *name, const char *fmt=NULL, ...) : mName(name) { 73 Indent(); 74 printf("-> %s ", name); 75 if (fmt) { 76 va_list args; 77 va_start(args, fmt); 78 vprintf(fmt, args); 79 va_end(args); 80 } 81 printf("\n"); 82 ++sIndent; 83 } 84 ~FunctionLogger() { 85 --sIndent; 86 Indent(); 87 printf("<- %s\n", mName); 88 if (sIndent == 0) 89 printf("\n"); 90 } 91 92 static void Indent() { 93 for (int i = sIndent; --i >= 0; ) { 94 putchar(' '); putchar(' '); 95 } 96 } 97 98 const char *mName; 99 static int sIndent; 100 }; 101 int FunctionLogger::sIndent = 0; 102 103 #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__); 104#else 105 #define LOG_FUNCTION(name, format, foo) 106#endif 107 108static const UInt32 kDefaultIOBufferSizeBytes = 0x10000; 109 110#if CAAUDIOFILE_PROFILE 111 #define StartTiming(af, starttime) UInt64 starttime = af->mProfiling ? CAHostTimeBase::GetTheCurrentTime() : 0 112 #define ElapsedTime(af, starttime, counter) if (af->mProfiling) counter += (CAHostTimeBase::GetTheCurrentTime() - starttime) 113#else 114 #define StartTiming(af, starttime) 115 #define ElapsedTime(af, starttime, counter) 116#endif 117 118#define kNoMoreInputRightNow 'nein' 119 120// _______________________________________________________________________________________ 121// 122CAAudioFile::CAAudioFile() : 123 mAudioFile(0), 124 mUseCache(false), 125 mFinishingEncoding(false), 126 mMode(kClosed), 127 mFileDataOffset(-1), 128 mFramesToSkipFollowingSeek(0), 129 130 mClientOwnsIOBuffer(false), 131 mPacketDescs(NULL), 132 mNumPacketDescs(0), 133 mConverter(NULL), 134 mMagicCookie(NULL), 135 mWriteBufferList(NULL) 136#if CAAUDIOFILE_PROFILE 137 , 138 mProfiling(false), 139 mTicksInConverter(0), 140 mTicksInReadInConverter(0), 141 mTicksInIO(0), 142 mInConverter(false) 143#endif 144{ 145 mIOBufferList.mBuffers[0].mData = NULL; 146 mIOBufferList.mBuffers[0].mDataByteSize = 0; 147 mClientMaxPacketSize = 0; 148 mIOBufferSizeBytes = kDefaultIOBufferSizeBytes; 149} 150 151// _______________________________________________________________________________________ 152// 153CAAudioFile::~CAAudioFile() 154{ 155 Close(); 156} 157 158// _______________________________________________________________________________________ 159// 160void CAAudioFile::Close() 161{ 162 LOG_FUNCTION("CAAudioFile::Close", NULL, NULL); 163 if (mMode == kClosed) 164 return; 165 if (mMode == kWriting) 166 FlushEncoder(); 167 CloseConverter(); 168 if (mAudioFile != 0 && mOwnOpenFile) { 169 AudioFileClose(mAudioFile); 170 mAudioFile = 0; 171 } 172 if (!mClientOwnsIOBuffer) { 173 delete[] (Byte *)mIOBufferList.mBuffers[0].mData; 174 mIOBufferList.mBuffers[0].mData = NULL; 175 mIOBufferList.mBuffers[0].mDataByteSize = 0; 176 } 177 delete[] mPacketDescs; mPacketDescs = NULL; mNumPacketDescs = 0; 178 delete[] mMagicCookie; mMagicCookie = NULL; 179 delete mWriteBufferList; mWriteBufferList = NULL; 180 mMode = kClosed; 181} 182 183// _______________________________________________________________________________________ 184// 185void CAAudioFile::CloseConverter() 186{ 187 if (mConverter) { 188#if VERBOSE_CONVERTER 189 printf("CAAudioFile %p : CloseConverter\n", this); 190#endif 191 AudioConverterDispose(mConverter); 192 mConverter = NULL; 193 } 194} 195 196// ======================================================================================= 197 198// _______________________________________________________________________________________ 199// 200void CAAudioFile::Open(const FSRef &fsref) 201{ 202 LOG_FUNCTION("CAAudioFile::Open", "%p", this); 203 XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); 204 mFSRef = fsref; 205 XThrowIfError(AudioFileOpen(&mFSRef, fsRdPerm, 0, &mAudioFile), "open audio file"); 206 mOwnOpenFile = true; 207 mMode = kReading; 208 GetExistingFileInfo(); 209} 210 211// _______________________________________________________________________________________ 212// 213void CAAudioFile::Wrap(AudioFileID fileID, bool forWriting) 214{ 215 LOG_FUNCTION("CAAudioFile::Wrap", "%p", this); 216 XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); 217 218 mAudioFile = fileID; 219 mOwnOpenFile = false; 220 mMode = forWriting ? kPreparingToWrite : kReading; 221 GetExistingFileInfo(); 222 if (forWriting) 223 FileFormatChanged(); 224} 225 226// _______________________________________________________________________________________ 227// 228void CAAudioFile::CreateNew(const FSRef &parentDir, CFStringRef filename, AudioFileTypeID filetype, const AudioStreamBasicDescription &dataFormat, const AudioChannelLayout *layout) 229{ 230 LOG_FUNCTION("CAAudioFile::CreateNew", "%p", this); 231 XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); 232 233 mFileDataFormat = dataFormat; 234 if (layout) { 235 mFileChannelLayout = layout; 236#if VERBOSE_CHANNELMAP 237 printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); 238#endif 239 } 240 mMode = kPreparingToCreate; 241 FileFormatChanged(&parentDir, filename, filetype); 242} 243 244// _______________________________________________________________________________________ 245// 246// called to create the file -- or update its format/channel layout/properties based on an encoder 247// setting change 248void CAAudioFile::FileFormatChanged(const FSRef *parentDir, CFStringRef filename, AudioFileTypeID filetype) 249{ 250 LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this); 251 XThrowIf(mMode != kPreparingToCreate && mMode != kPreparingToWrite, kExtAudioFileError_InvalidOperationOrder, "new file not prepared"); 252 253 UInt32 propertySize; 254 OSStatus err; 255 AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat; 256 257#if VERBOSE_CONVERTER 258 mFileDataFormat.PrintFormat(stdout, "", "Specified file data format"); 259#endif 260 261 // Find out the actual format the converter will produce. This is necessary in 262 // case the bitrate has forced a lower sample rate, which needs to be set correctly 263 // in the stream description passed to AudioFileCreate. 264 if (mConverter != NULL) { 265 propertySize = sizeof(AudioStreamBasicDescription); 266 Float64 origSampleRate = mFileDataFormat.mSampleRate; 267 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCurrentOutputStreamDescription, &propertySize, &mFileDataFormat), "get audio converter's output stream description"); 268 // do the same for the channel layout being output by the converter 269#if VERBOSE_CONVERTER 270 mFileDataFormat.PrintFormat(stdout, "", "Converter output"); 271#endif 272 if (fiszero(mFileDataFormat.mSampleRate)) 273 mFileDataFormat.mSampleRate = origSampleRate; 274 err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterOutputChannelLayout, &propertySize, NULL); 275 if (err == noErr && propertySize > 0) { 276 AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize)); 277 err = AudioConverterGetProperty(mConverter, kAudioConverterOutputChannelLayout, &propertySize, layout); 278 if (err) { 279 free(layout); 280 XThrow(err, "couldn't get audio converter's output channel layout"); 281 } 282 mFileChannelLayout = layout; 283#if VERBOSE_CHANNELMAP 284 printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); 285#endif 286 free(layout); 287 } 288 } 289 290 // create the output file 291 if (mMode == kPreparingToCreate) { 292 CAStreamBasicDescription newFileDataFormat = mFileDataFormat; 293 if (fiszero(newFileDataFormat.mSampleRate)) 294 newFileDataFormat.mSampleRate = 44100; // just make something up for now 295#if VERBOSE_CONVERTER 296 newFileDataFormat.PrintFormat(stdout, "", "Applied to new file"); 297#endif 298 XThrowIfError(AudioFileCreate(parentDir, filename, filetype, &newFileDataFormat, 0, &mFSRef, &mAudioFile), "create audio file"); 299 mMode = kPreparingToWrite; 300 mOwnOpenFile = true; 301 } else if (saveFileDataFormat != mFileDataFormat || fnotequal(saveFileDataFormat.mSampleRate, mFileDataFormat.mSampleRate)) { 302 // second check must be explicit since operator== on ASBD treats SR of zero as "don't care" 303 if (fiszero(mFileDataFormat.mSampleRate)) 304 mFileDataFormat.mSampleRate = mClientDataFormat.mSampleRate; 305#if VERBOSE_CONVERTER 306 mFileDataFormat.PrintFormat(stdout, "", "Applied to new file"); 307#endif 308 XThrowIf(fiszero(mFileDataFormat.mSampleRate), kExtAudioFileError_InvalidDataFormat, "file's sample rate is 0"); 309 XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyDataFormat, sizeof(AudioStreamBasicDescription), &mFileDataFormat), "couldn't update file's data format"); 310 } 311 312 UInt32 deferSizeUpdates = 1; 313 err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates); 314 315 if (mConverter != NULL) { 316 // encoder 317 // get the magic cookie, if any, from the converter 318 delete[] mMagicCookie; mMagicCookie = NULL; 319 mMagicCookieSize = 0; 320 321 err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, NULL); 322 323 // we can get a noErr result and also a propertySize == 0 324 // -- if the file format does support magic cookies, but this file doesn't have one. 325 if (err == noErr && propertySize > 0) { 326 mMagicCookie = new Byte[propertySize]; 327 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, mMagicCookie), "get audio converter's magic cookie"); 328 mMagicCookieSize = propertySize; // the converter lies and tell us the wrong size 329 // now set the magic cookie on the output file 330 UInt32 willEatTheCookie = false; 331 // the converter wants to give us one; will the file take it? 332 err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, 333 NULL, &willEatTheCookie); 334 if (err == noErr && willEatTheCookie) { 335#if VERBOSE_CONVERTER 336 printf("Setting cookie on encoded file\n"); 337#endif 338 XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, mMagicCookieSize, mMagicCookie), "set audio file's magic cookie"); 339 } 340 } 341 342 // get maximum packet size 343 propertySize = sizeof(UInt32); 344 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propertySize, &mFileMaxPacketSize), "get audio converter's maximum output packet size"); 345 346 AllocateBuffers(true /* okToFail */); 347 } else { 348 InitFileMaxPacketSize(); 349 } 350 351 if (mFileChannelLayout.IsValid() && mFileChannelLayout.NumberChannels() > 2) { 352 // don't bother tagging mono/stereo files 353 UInt32 isWritable; 354 err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, NULL, &isWritable); 355 if (!err && isWritable) { 356#if VERBOSE_CHANNELMAP 357 printf("writing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); 358#endif 359 err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyChannelLayout, 360 mFileChannelLayout.Size(), &mFileChannelLayout.Layout()); 361 if (err) 362 CAXException::Warning("could not set the file's channel layout", err); 363 } else { 364#if VERBOSE_CHANNELMAP 365 printf("file won't accept a channel layout (write)\n"); 366#endif 367 } 368 } 369 370 UpdateClientMaxPacketSize(); // also sets mFrame0Offset 371 mPacketMark = 0; 372 mFrameMark = 0; 373} 374 375// _______________________________________________________________________________________ 376// 377void CAAudioFile::InitFileMaxPacketSize() 378{ 379 LOG_FUNCTION("CAAudioFile::InitFileMaxPacketSize", "%p", this); 380 UInt32 propertySize = sizeof(UInt32); 381 OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyMaximumPacketSize, 382 &propertySize, &mFileMaxPacketSize); 383 if (err) { 384 // workaround for 3361377: not all file formats' maximum packet sizes are supported 385 if (!mFileDataFormat.IsPCM()) 386 XThrowIfError(err, "get audio file's maximum packet size"); 387 mFileMaxPacketSize = mFileDataFormat.mBytesPerFrame; 388 } 389 AllocateBuffers(true /* okToFail */); 390} 391 392 393// _______________________________________________________________________________________ 394// 395SInt64 CAAudioFile::FileDataOffset() 396{ 397 if (mFileDataOffset < 0) { 398 UInt32 propertySize = sizeof(SInt64); 399 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataOffset, &propertySize, &mFileDataOffset), "couldn't get file's data offset"); 400 } 401 return mFileDataOffset; 402} 403 404// _______________________________________________________________________________________ 405// 406SInt64 CAAudioFile::GetNumberFrames() const 407{ 408 AudioFilePacketTableInfo pti; 409 UInt32 propertySize = sizeof(pti); 410 OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); 411 if (err == noErr) 412 return pti.mNumberValidFrames; 413 return mFileDataFormat.mFramesPerPacket * GetNumberPackets() - mFrame0Offset; 414} 415 416// _______________________________________________________________________________________ 417// 418void CAAudioFile::SetNumberFrames(SInt64 nFrames) 419{ 420 XThrowIf(mFileDataFormat.mFramesPerPacket != 1, kExtAudioFileError_InvalidDataFormat, "SetNumberFrames only supported for PCM"); 421 XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, sizeof(SInt64), &nFrames), "Couldn't set number of packets on audio file"); 422} 423 424// _______________________________________________________________________________________ 425// 426// call for existing file, NOT new one - from Open() or Wrap() 427void CAAudioFile::GetExistingFileInfo() 428{ 429 LOG_FUNCTION("CAAudioFile::GetExistingFileInfo", "%p", this); 430 UInt32 propertySize; 431 OSStatus err; 432 433 // get mFileDataFormat 434 propertySize = sizeof(AudioStreamBasicDescription); 435 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataFormat, &propertySize, &mFileDataFormat), "get audio file's data format"); 436 437 // get mFileChannelLayout 438 err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, NULL); 439 if (err == noErr && propertySize > 0) { 440 AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize)); 441 err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, layout); 442 if (err == noErr) { 443 mFileChannelLayout = layout; 444#if VERBOSE_CHANNELMAP 445 printf("existing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); 446#endif 447 } 448 free(layout); 449 XThrowIfError(err, "get audio file's channel layout"); 450 } 451 if (mMode != kReading) 452 return; 453 454#if 0 455 // get mNumberPackets 456 propertySize = sizeof(mNumberPackets); 457 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &mNumberPackets), "get audio file's packet count"); 458#if VERBOSE_IO 459 printf("CAAudioFile::GetExistingFileInfo: %qd packets\n", mNumberPackets); 460#endif 461#endif 462 463 // get mMagicCookie 464 err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL); 465 if (err == noErr && propertySize > 0) { 466 mMagicCookie = new Byte[propertySize]; 467 mMagicCookieSize = propertySize; 468 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, mMagicCookie), "get audio file's magic cookie"); 469 } 470 InitFileMaxPacketSize(); 471 mPacketMark = 0; 472 mFrameMark = 0; 473 474 UpdateClientMaxPacketSize(); 475} 476 477// ======================================================================================= 478 479// _______________________________________________________________________________________ 480// 481void CAAudioFile::SetFileChannelLayout(const CAAudioChannelLayout &layout) 482{ 483 LOG_FUNCTION("CAAudioFile::SetFileChannelLayout", "%p", this); 484 mFileChannelLayout = layout; 485#if VERBOSE_CHANNELMAP 486 printf("file channel layout set explicitly (%s): %s\n", mMode == kReading ? "read" : "write", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); 487#endif 488 if (mMode != kReading) 489 FileFormatChanged(); 490} 491 492// _______________________________________________________________________________________ 493// 494void CAAudioFile::SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout) 495{ 496 LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this); 497 XThrowIf(!dataFormat.IsPCM(), kExtAudioFileError_NonPCMClientFormat, "non-PCM client format on audio file"); 498 499 bool dataFormatChanging = (mClientDataFormat.mFormatID == 0 || mClientDataFormat != dataFormat); 500 501 if (dataFormatChanging) { 502 CloseConverter(); 503 if (mWriteBufferList) { 504 delete mWriteBufferList; 505 mWriteBufferList = NULL; 506 } 507 mClientDataFormat = dataFormat; 508 } 509 510 if (layout && layout->IsValid()) { 511 XThrowIf(layout->NumberChannels() != mClientDataFormat.NumberChannels(), kExtAudioFileError_InvalidChannelMap, "inappropriate channel map"); 512 mClientChannelLayout = *layout; 513 } 514 515 bool differentLayouts; 516 if (mClientChannelLayout.IsValid()) { 517 if (mFileChannelLayout.IsValid()) { 518 differentLayouts = mClientChannelLayout.Tag() != mFileChannelLayout.Tag(); 519#if VERBOSE_CHANNELMAP 520 printf("two valid layouts, %s\n", differentLayouts ? "different" : "same"); 521#endif 522 } else { 523 differentLayouts = false; 524#if VERBOSE_CHANNELMAP 525 printf("valid client layout, unknown file layout\n"); 526#endif 527 } 528 } else { 529 differentLayouts = false; 530#if VERBOSE_CHANNELMAP 531 if (mFileChannelLayout.IsValid()) 532 printf("valid file layout, unknown client layout\n"); 533 else 534 printf("two invalid layouts\n"); 535#endif 536 } 537 538 if (mClientDataFormat != mFileDataFormat || differentLayouts) { 539 // We need an AudioConverter. 540 if (mMode == kReading) { 541 // file -> client (decode) 542//mFileDataFormat.PrintFormat( stdout, "", "File: "); 543//mClientDataFormat.PrintFormat(stdout, "", "Client: "); 544 545 if (mConverter == NULL) 546 XThrowIfError(AudioConverterNew(&mFileDataFormat, &mClientDataFormat, &mConverter), 547 "create audio converter"); 548 549#if VERBOSE_CONVERTER 550 printf("CAAudioFile %p -- created converter\n", this); 551 CAShow(mConverter); 552#endif 553 // set the magic cookie, if any (for decode) 554 if (mMagicCookie) 555 SetConverterProperty(kAudioConverterDecompressionMagicCookie, mMagicCookieSize, mMagicCookie, mFileDataFormat.IsPCM()); 556 // we get cookies from some AIFF's but the converter barfs on them, 557 // so we set canFail to true for PCM 558 559 SetConverterChannelLayout(false, mFileChannelLayout); 560 SetConverterChannelLayout(true, mClientChannelLayout); 561 562 // propagate leading/trailing frame counts 563 if (mFileDataFormat.mBitsPerChannel == 0) { 564 UInt32 propertySize; 565 OSStatus err; 566 AudioFilePacketTableInfo pti; 567 propertySize = sizeof(pti); 568 err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); 569 if (err == noErr && (pti.mPrimingFrames > 0 || pti.mRemainderFrames > 0)) { 570 AudioConverterPrimeInfo primeInfo; 571 primeInfo.leadingFrames = pti.mPrimingFrames; 572 primeInfo.trailingFrames = pti.mRemainderFrames; 573 /* ignore any error. better to play it at all than not. */ 574 /*err = */AudioConverterSetProperty(mConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo); 575 //XThrowIfError(err, "couldn't set prime info on converter"); 576 } 577 } 578 } else if (mMode == kPreparingToCreate || mMode == kPreparingToWrite) { 579 // client -> file (encode) 580 if (mConverter == NULL) 581 XThrowIfError(AudioConverterNew(&mClientDataFormat, &mFileDataFormat, &mConverter), "create audio converter"); 582 mWriteBufferList = CABufferList::New("", mClientDataFormat); 583 SetConverterChannelLayout(false, mClientChannelLayout); 584 SetConverterChannelLayout(true, mFileChannelLayout); 585 if (mMode == kPreparingToWrite) 586 FileFormatChanged(); 587 } else 588 XThrowIfError(kExtAudioFileError_InvalidOperationOrder, "audio file format not yet known"); 589 } 590 UpdateClientMaxPacketSize(); 591} 592 593// _______________________________________________________________________________________ 594// 595OSStatus CAAudioFile::SetConverterProperty( 596 AudioConverterPropertyID inPropertyID, 597 UInt32 inPropertyDataSize, 598 const void* inPropertyData, 599 bool inCanFail) 600{ 601 OSStatus err = noErr; 602 //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID); 603 if (inPropertyID == kAudioConverterPropertySettings && *(CFPropertyListRef *)inPropertyData == NULL) 604 ; 605 else { 606 err = AudioConverterSetProperty(mConverter, inPropertyID, inPropertyDataSize, inPropertyData); 607 if (!inCanFail) { 608 XThrowIfError(err, "set audio converter property"); 609 } 610 } 611 UpdateClientMaxPacketSize(); 612 if (mMode == kPreparingToWrite) 613 FileFormatChanged(); 614 return err; 615} 616 617// _______________________________________________________________________________________ 618// 619void CAAudioFile::SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout) 620{ 621 LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this); 622 OSStatus err; 623 624 if (layout.IsValid()) { 625#if VERBOSE_CHANNELMAP 626 printf("Setting converter's %s channel layout: %s\n", output ? "output" : "input", 627 CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); 628#endif 629 if (output) { 630 err = AudioConverterSetProperty(mConverter, kAudioConverterOutputChannelLayout, 631 layout.Size(), &layout.Layout()); 632 XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's output channel layout"); 633 } else { 634 err = AudioConverterSetProperty(mConverter, kAudioConverterInputChannelLayout, 635 layout.Size(), &layout.Layout()); 636 XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's input channel layout"); 637 } 638 if (mMode == kPreparingToWrite) 639 FileFormatChanged(); 640 } 641} 642 643// _______________________________________________________________________________________ 644// 645CFArrayRef CAAudioFile::GetConverterConfig() 646{ 647 CFArrayRef plist; 648 UInt32 propertySize = sizeof(plist); 649 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertySettings, &propertySize, &plist), "get converter property settings"); 650 return plist; 651} 652 653// _______________________________________________________________________________________ 654// 655void CAAudioFile::UpdateClientMaxPacketSize() 656{ 657 LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this); 658 mFrame0Offset = 0; 659 if (mConverter != NULL) { 660 AudioConverterPropertyID property = (mMode == kReading) ? 661 kAudioConverterPropertyMaximumOutputPacketSize : 662 kAudioConverterPropertyMaximumInputPacketSize; 663 664 UInt32 propertySize = sizeof(UInt32); 665 XThrowIfError(AudioConverterGetProperty(mConverter, property, &propertySize, &mClientMaxPacketSize), 666 "get audio converter's maximum packet size"); 667 668 if (mFileDataFormat.mBitsPerChannel == 0) { 669 AudioConverterPrimeInfo primeInfo; 670 propertySize = sizeof(primeInfo); 671 OSStatus err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo); 672 if (err == noErr) 673 mFrame0Offset = primeInfo.leadingFrames; 674#if VERBOSE_CONVERTER 675 printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err, mFrame0Offset); 676#endif 677 } 678 } else { 679 mClientMaxPacketSize = mFileMaxPacketSize; 680 } 681} 682 683// _______________________________________________________________________________________ 684// Allocates: mIOBufferList, mIOBufferSizePackets, mPacketDescs 685// Dependent on: mFileMaxPacketSize, mIOBufferSizeBytes 686void CAAudioFile::AllocateBuffers(bool okToFail) 687{ 688 LOG_FUNCTION("CAAudioFile::AllocateBuffers", "%p", this); 689 if (mFileMaxPacketSize == 0) { 690 if (okToFail) 691 return; 692 XThrowIf(true, kExtAudioFileError_MaxPacketSizeUnknown, "file's maximum packet size is 0"); 693 } 694 UInt32 bufferSizeBytes = mIOBufferSizeBytes = std::max(mIOBufferSizeBytes, mFileMaxPacketSize); 695 // must be big enough for at least one maximum size packet 696 697 if (mIOBufferList.mBuffers[0].mDataByteSize != bufferSizeBytes) { 698 mIOBufferList.mNumberBuffers = 1; 699 mIOBufferList.mBuffers[0].mNumberChannels = mFileDataFormat.mChannelsPerFrame; 700 if (!mClientOwnsIOBuffer) { 701 //printf("reallocating I/O buffer\n"); 702 delete[] (Byte *)mIOBufferList.mBuffers[0].mData; 703 mIOBufferList.mBuffers[0].mData = new Byte[bufferSizeBytes]; 704 } 705 mIOBufferList.mBuffers[0].mDataByteSize = bufferSizeBytes; 706 mIOBufferSizePackets = bufferSizeBytes / mFileMaxPacketSize; 707 } 708 709 UInt32 propertySize = sizeof(UInt32); 710 UInt32 externallyFramed; 711 XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed, 712 sizeof(AudioStreamBasicDescription), &mFileDataFormat, &propertySize, &externallyFramed), 713 "is format externally framed"); 714 if (mNumPacketDescs != (externallyFramed ? mIOBufferSizePackets : 0)) { 715 delete[] mPacketDescs; 716 mPacketDescs = NULL; 717 mNumPacketDescs = 0; 718 719 if (externallyFramed) { 720 //printf("reallocating packet descs\n"); 721 mPacketDescs = new AudioStreamPacketDescription[mIOBufferSizePackets]; 722 mNumPacketDescs = mIOBufferSizePackets; 723 } 724 } 725} 726 727// _______________________________________________________________________________________ 728// 729void CAAudioFile::SetIOBuffer(void *buf) 730{ 731 if (!mClientOwnsIOBuffer) 732 delete[] (Byte *)mIOBufferList.mBuffers[0].mData; 733 mIOBufferList.mBuffers[0].mData = buf; 734 735 if (buf == NULL) { 736 mClientOwnsIOBuffer = false; 737 SetIOBufferSizeBytes(mIOBufferSizeBytes); 738 } else { 739 mClientOwnsIOBuffer = true; 740 AllocateBuffers(); 741 } 742// printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer); 743} 744 745// =============================================================================== 746 747/* 748For Tiger: 749added kAudioFilePropertyPacketToFrame and kAudioFilePropertyFrameToPacket. 750You pass in an AudioFramePacketTranslation struct, with the appropriate field filled in, to AudioFileGetProperty. 751 752 kAudioFilePropertyPacketToFrame = 'pkfr', 753 // pass a AudioFramePacketTranslation with mPacket filled out and get mFrame back. mFrameOffsetInPacket is ignored. 754 kAudioFilePropertyFrameToPacket = 'frpk', 755 // pass a AudioFramePacketTranslation with mFrame filled out and get mPacket and mFrameOffsetInPacket back. 756 757struct AudioFramePacketTranslation 758{ 759 SInt64 mFrame; 760 SInt64 mPacket; 761 UInt32 mFrameOffsetInPacket; 762}; 763*/ 764 765SInt64 CAAudioFile::PacketToFrame(SInt64 packet) const 766{ 767 AudioFramePacketTranslation trans; 768 UInt32 propertySize; 769 770 switch (mFileDataFormat.mFramesPerPacket) { 771 case 1: 772 return packet; 773 case 0: 774 trans.mPacket = packet; 775 propertySize = sizeof(trans); 776 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketToFrame, &propertySize, &trans), 777 "packet <-> frame translation unimplemented for format with variable frames/packet"); 778 return trans.mFrame; 779 } 780 return packet * mFileDataFormat.mFramesPerPacket; 781} 782 783SInt64 CAAudioFile::FrameToPacket(SInt64 inFrame) const 784{ 785 AudioFramePacketTranslation trans; 786 UInt32 propertySize; 787 788 switch (mFileDataFormat.mFramesPerPacket) { 789 case 1: 790 return inFrame; 791 case 0: 792 trans.mFrame = inFrame; 793 propertySize = sizeof(trans); 794 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyFrameToPacket, &propertySize, &trans), 795 "packet <-> frame translation unimplemented for format with variable frames/packet"); 796 return trans.mPacket; 797 } 798 return inFrame / mFileDataFormat.mFramesPerPacket; 799} 800 801// _______________________________________________________________________________________ 802// 803 804SInt64 CAAudioFile::Tell() const // frameNumber 805{ 806 return mFrameMark - mFrame0Offset; 807} 808 809void CAAudioFile::SeekToPacket(SInt64 packetNumber) 810{ 811#if VERBOSE_IO 812 printf("CAAudioFile::SeekToPacket: %qd\n", packetNumber); 813#endif 814 XThrowIf(mMode != kReading || packetNumber < 0 /*|| packetNumber >= mNumberPackets*/ , kExtAudioFileError_InvalidSeek, "seek to packet in audio file"); 815 if (mPacketMark == packetNumber) 816 return; // already there! don't reset converter 817 mPacketMark = packetNumber; 818 819 mFrameMark = PacketToFrame(packetNumber) - mFrame0Offset; 820 mFramesToSkipFollowingSeek = 0; 821 if (mConverter) 822 // must reset -- if we reached end of stream. converter will no longer work otherwise 823 AudioConverterReset(mConverter); 824} 825 826/* 827 Example: AAC, 1024 frames/packet, 2112 frame offset 828 829 2112 830 | 831 Absolute frames: 0 1024 2048 | 3072 832 +---------+---------+--|------+---------+---------+ 833 Packets: | 0 | 1 | | 2 | 3 | 4 | 834 +---------+---------+--|------+---------+---------+ 835 Client frames: -2112 -1088 -64 | 960 SeekToFrame, TellFrame 836 | 837 0 838 839 * Offset between absolute and client frames is mFrame0Offset. 840 *** mFrameMark is in client frames *** 841 842 Examples: 843 clientFrame 0 960 1000 1024 844 absoluteFrame 2112 3072 3112 3136 845 packet 0 0 0 1 846 tempFrameMark* -2112 -2112 -2112 -1088 847 mFramesToSkipFollowingSeek 2112 3072 3112 2112 848*/ 849void CAAudioFile::Seek(SInt64 clientFrame) 850{ 851 if (clientFrame == mFrameMark) 852 return; // already there! don't reset converter 853 854 //SInt64 absoluteFrame = clientFrame + mFrame0Offset; 855 XThrowIf(mMode != kReading || clientFrame < 0 || !mClientDataFormat.IsPCM(), kExtAudioFileError_InvalidSeek, "seek to frame in audio file"); 856 857#if VERBOSE_IO 858 SInt64 prevFrameMark = mFrameMark; 859#endif 860 861 SInt64 packet; 862 packet = FrameToPacket(clientFrame); 863 if (packet < 0) 864 packet = 0; 865 SeekToPacket(packet); 866 // this will have backed up mFrameMark to match the beginning of the packet 867 mFramesToSkipFollowingSeek = std::max(UInt32(clientFrame - mFrameMark), UInt32(0)); 868 mFrameMark = clientFrame; 869 870#if VERBOSE_IO 871 printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark, prevFrameMark, packet, mFramesToSkipFollowingSeek); 872#endif 873} 874 875// _______________________________________________________________________________________ 876// 877void CAAudioFile::Read(UInt32 &ioNumPackets, AudioBufferList *ioData) 878 // May read fewer packets than requested if: 879 // buffer is not big enough 880 // file does not contain that many more packets 881 // Note that eofErr is not fatal, just results in 0 packets returned 882 // ioData's buffer sizes may be shortened 883{ 884 XThrowIf(mClientMaxPacketSize == 0, kExtAudioFileError_MaxPacketSizeUnknown, "client maximum packet size is 0"); 885 if (mIOBufferList.mBuffers[0].mData == NULL) { 886#if DEBUG 887 printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n"); 888#endif 889 AllocateBuffers(); 890 } 891 UInt32 bufferSizeBytes = ioData->mBuffers[0].mDataByteSize; 892 UInt32 maxNumPackets = bufferSizeBytes / mClientMaxPacketSize; 893 // older versions of AudioConverterFillComplexBuffer don't do this, so do our own sanity check 894 UInt32 nPackets = std::min(ioNumPackets, maxNumPackets); 895 896 mMaxPacketsToRead = ~0UL; 897 898 if (mClientDataFormat.mFramesPerPacket == 1) { // PCM or equivalent 899 while (mFramesToSkipFollowingSeek > 0) { 900 UInt32 skipFrames = std::min(mFramesToSkipFollowingSeek, maxNumPackets); 901 UInt32 framesPerPacket; 902 if ((framesPerPacket=mFileDataFormat.mFramesPerPacket) > 0) 903 mMaxPacketsToRead = (skipFrames + framesPerPacket - 1) / framesPerPacket; 904 905 if (mConverter == NULL) { 906 XThrowIfError(ReadInputProc(NULL, &skipFrames, ioData, NULL, this), "read audio file"); 907 } else { 908#if CAAUDIOFILE_PROFILE 909 mInConverter = true; 910#endif 911 StartTiming(this, fill); 912 XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &skipFrames, ioData, NULL), "convert audio packets (pcm read)"); 913 ElapsedTime(this, fill, mTicksInConverter); 914#if CAAUDIOFILE_PROFILE 915 mInConverter = false; 916#endif 917 } 918 if (skipFrames == 0) { // hit EOF 919 ioNumPackets = 0; 920 return; 921 } 922 mFrameMark += skipFrames; 923#if VERBOSE_IO 924 printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames); 925#endif 926 927 mFramesToSkipFollowingSeek -= skipFrames; 928 929 // restore mDataByteSize 930 for (int i = ioData->mNumberBuffers; --i >= 0 ; ) 931 ioData->mBuffers[i].mDataByteSize = bufferSizeBytes; 932 } 933 } 934 935 if (mFileDataFormat.mFramesPerPacket > 0) 936 // don't read more packets than we are being asked to produce 937 mMaxPacketsToRead = nPackets / mFileDataFormat.mFramesPerPacket + 1; 938 if (mConverter == NULL) { 939 XThrowIfError(ReadInputProc(NULL, &nPackets, ioData, NULL, this), "read audio file"); 940 } else { 941#if CAAUDIOFILE_PROFILE 942 mInConverter = true; 943#endif 944 StartTiming(this, fill); 945 XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &nPackets, ioData, NULL), "convert audio packets (read)"); 946 ElapsedTime(this, fill, mTicksInConverter); 947#if CAAUDIOFILE_PROFILE 948 mInConverter = false; 949#endif 950 } 951 if (mClientDataFormat.mFramesPerPacket == 1) 952 mFrameMark += nPackets; 953 954 ioNumPackets = nPackets; 955} 956 957// _______________________________________________________________________________________ 958// 959OSStatus CAAudioFile::ReadInputProc( AudioConverterRef inAudioConverter, 960 UInt32* ioNumberDataPackets, 961 AudioBufferList* ioData, 962 AudioStreamPacketDescription** outDataPacketDescription, 963 void* inUserData) 964{ 965 CAAudioFile *This = static_cast<CAAudioFile *>(inUserData); 966 967#if 0 968 SInt64 remainingPacketsInFile = This->mNumberPackets - This->mPacketMark; 969 if (remainingPacketsInFile <= 0) { 970 *ioNumberDataPackets = 0; 971 ioData->mBuffers[0].mDataByteSize = 0; 972 if (outDataPacketDescription) 973 *outDataPacketDescription = This->mPacketDescs; 974#if VERBOSE_IO 975 printf("CAAudioFile::ReadInputProc: EOF\n"); 976#endif 977 return noErr; // not eofErr; EOF is signified by 0 packets/0 bytes 978 } 979#endif 980 981 // determine how much to read 982 AudioBufferList *readBuffer; 983 UInt32 readPackets; 984 if (inAudioConverter != NULL) { 985 // getting called from converter, need to use our I/O buffer 986 readBuffer = &This->mIOBufferList; 987 readPackets = This->mIOBufferSizePackets; 988 } else { 989 // getting called directly from ReadPackets, use supplied buffer 990 if (This->mFileMaxPacketSize == 0) 991 return kExtAudioFileError_MaxPacketSizeUnknown; 992 readBuffer = ioData; 993 readPackets = std::min(*ioNumberDataPackets, readBuffer->mBuffers[0].mDataByteSize / This->mFileMaxPacketSize); 994 // don't attempt to read more packets than will fit in the buffer 995 } 996 // don't try to read past EOF 997// if (readPackets > remainingPacketsInFile) 998// readPackets = remainingPacketsInFile; 999 // don't read more packets than necessary to produce the requested amount of converted data 1000 if (readPackets > This->mMaxPacketsToRead) { 1001#if VERBOSE_IO 1002 printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This->mMaxPacketsToRead, readPackets); 1003#endif 1004 readPackets = This->mMaxPacketsToRead; 1005 } 1006 1007 // read 1008 UInt32 bytesRead; 1009 OSStatus err; 1010 1011 StartTiming(This, read); 1012 StartTiming(This, readinconv); 1013 err = AudioFileReadPackets(This->mAudioFile, This->mUseCache, &bytesRead, This->mPacketDescs, This->mPacketMark, &readPackets, readBuffer->mBuffers[0].mData); 1014#if CAAUDIOFILE_PROFILE 1015 if (This->mInConverter) ElapsedTime(This, readinconv, This->mTicksInReadInConverter); 1016#endif 1017 ElapsedTime(This, read, This->mTicksInIO); 1018 1019 if (err) { 1020 DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err); 1021 return err; 1022 } 1023 1024#if VERBOSE_IO 1025 printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets, This->mPacketMark, This->mPacketMark + readPackets, bytesRead, err); 1026#if VERBOSE_IO >= 2 1027 if (This->mPacketDescs) { 1028 for (UInt32 i = 0; i < readPackets; ++i) { 1029 printf(" read packet %qd : offset %qd, length %ld\n", This->mPacketMark + i, This->mPacketDescs[i].mStartOffset, This->mPacketDescs[i].mDataByteSize); 1030 } 1031 } 1032 printf(" read buffer:"); CAShowAudioBufferList(readBuffer, 0, 4); 1033#endif 1034#endif 1035 if (readPackets == 0) { 1036 *ioNumberDataPackets = 0; 1037 ioData->mBuffers[0].mDataByteSize = 0; 1038 return noErr; 1039 } 1040 1041 if (outDataPacketDescription) 1042 *outDataPacketDescription = This->mPacketDescs; 1043 ioData->mBuffers[0].mDataByteSize = bytesRead; 1044 ioData->mBuffers[0].mData = readBuffer->mBuffers[0].mData; 1045 1046 This->mPacketMark += readPackets; 1047 if (This->mClientDataFormat.mFramesPerPacket != 1) { // for PCM client formats we update in Read 1048 // but for non-PCM client format (weird case) we must update here/now 1049 if (This->mFileDataFormat.mFramesPerPacket > 0) 1050 This->mFrameMark += readPackets * This->mFileDataFormat.mFramesPerPacket; 1051 else { 1052 for (UInt32 i = 0; i < readPackets; ++i) 1053 This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket; 1054 } 1055 } 1056 *ioNumberDataPackets = readPackets; 1057 return noErr; 1058} 1059 1060// _______________________________________________________________________________________ 1061// 1062void CAAudioFile::Write(UInt32 numPackets, const AudioBufferList *data) 1063{ 1064 if (mIOBufferList.mBuffers[0].mData == NULL) { 1065#if DEBUG 1066 printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n"); 1067#endif 1068 AllocateBuffers(); 1069 } 1070 1071 if (mMode == kPreparingToWrite) 1072 mMode = kWriting; 1073 else 1074 XThrowIf(mMode != kWriting, kExtAudioFileError_InvalidOperationOrder, "can't write to this file"); 1075 if (mConverter != NULL) { 1076 mWritePackets = numPackets; 1077 mWriteBufferList->SetFrom(data); 1078 WritePacketsFromCallback(WriteInputProc, this); 1079 } else { 1080 StartTiming(this, write); 1081 XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, data->mBuffers[0].mDataByteSize, 1082 NULL, mPacketMark, &numPackets, data->mBuffers[0].mData), 1083 "write audio file"); 1084 ElapsedTime(this, write, mTicksInIO); 1085#if VERBOSE_IO 1086 printf("CAAudioFile::WritePackets: wrote %ld packets at %qd, %ld bytes\n", numPackets, mPacketMark, data->mBuffers[0].mDataByteSize); 1087#endif 1088 //mNumberPackets = 1089 mPacketMark += numPackets; 1090 if (mFileDataFormat.mFramesPerPacket > 0) 1091 mFrameMark += numPackets * mFileDataFormat.mFramesPerPacket; 1092 // else: shouldn't happen since we're only called when there's no converter 1093 } 1094} 1095 1096// _______________________________________________________________________________________ 1097// 1098void CAAudioFile::FlushEncoder() 1099{ 1100 if (mConverter != NULL) { 1101 mFinishingEncoding = true; 1102 WritePacketsFromCallback(WriteInputProc, this); 1103 mFinishingEncoding = false; 1104 1105 // get priming info from converter, set it on the file 1106 if (mFileDataFormat.mBitsPerChannel == 0) { 1107 UInt32 propertySize; 1108 OSStatus err; 1109 AudioConverterPrimeInfo primeInfo; 1110 propertySize = sizeof(primeInfo); 1111 1112 err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo); 1113 if (err == noErr) { 1114 AudioFilePacketTableInfo pti; 1115 propertySize = sizeof(pti); 1116 err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); 1117 if (err == noErr) { 1118//printf("old packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames); 1119 UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames; 1120 pti.mPrimingFrames = primeInfo.leadingFrames; 1121 pti.mRemainderFrames = primeInfo.trailingFrames; 1122 pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames; 1123//printf("new packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames); 1124 XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti), "couldn't set packet table info on audio file"); 1125 } 1126 } 1127 } 1128 } 1129} 1130 1131// _______________________________________________________________________________________ 1132// 1133OSStatus CAAudioFile::WriteInputProc( AudioConverterRef /*inAudioConverter*/, 1134 UInt32 * ioNumberDataPackets, 1135 AudioBufferList* ioData, 1136 AudioStreamPacketDescription ** outDataPacketDescription, 1137 void* inUserData) 1138{ 1139 CAAudioFile *This = static_cast<CAAudioFile *>(inUserData); 1140 if (This->mFinishingEncoding) { 1141 *ioNumberDataPackets = 0; 1142 ioData->mBuffers[0].mDataByteSize = 0; 1143 ioData->mBuffers[0].mData = NULL; 1144 if (outDataPacketDescription) 1145 *outDataPacketDescription = NULL; 1146 return noErr; 1147 } 1148 UInt32 numPackets = This->mWritePackets; 1149 if (numPackets == 0) { 1150 return kNoMoreInputRightNow; 1151 } 1152 This->mWriteBufferList->ToAudioBufferList(ioData); 1153 This->mWriteBufferList->BytesConsumed(numPackets * This->mClientDataFormat.mBytesPerFrame); 1154 *ioNumberDataPackets = numPackets; 1155 if (outDataPacketDescription) 1156 *outDataPacketDescription = NULL; 1157 This->mWritePackets -= numPackets; 1158 return noErr; 1159} 1160 1161// _______________________________________________________________________________________ 1162// 1163#if VERBOSE_IO 1164static void hexdump(const void *addr, long len) 1165{ 1166 const Byte *p = (Byte *)addr; 1167 UInt32 offset = 0; 1168 1169 if (len > 0x400) len = 0x400; 1170 1171 while (len > 0) { 1172 int n = len > 16 ? 16 : len; 1173 printf("%08lX: ", offset); 1174 for (int i = 0; i < 16; ++i) 1175 if (i < n) 1176 printf("%02X ", p[i]); 1177 else printf(" "); 1178 for (int i = 0; i < 16; ++i) 1179 if (i < n) 1180 putchar(p[i] >= ' ' && p[i] < 127 ? p[i] : '.'); 1181 else putchar(' '); 1182 putchar('\n'); 1183 p += 16; 1184 len -= 16; 1185 offset += 16; 1186 } 1187} 1188#endif 1189 1190// _______________________________________________________________________________________ 1191// 1192void CAAudioFile::WritePacketsFromCallback( 1193 AudioConverterComplexInputDataProc inInputDataProc, 1194 void * inInputDataProcUserData) 1195{ 1196 while (true) { 1197 // keep writing until we exhaust the input (temporary stop), or produce no output (EOF) 1198 UInt32 numEncodedPackets = mIOBufferSizePackets; 1199 mIOBufferList.mBuffers[0].mDataByteSize = mIOBufferSizeBytes; 1200#if CAAUDIOFILE_PROFILE 1201 mInConverter = true; 1202#endif 1203 StartTiming(this, fill); 1204 OSStatus err = AudioConverterFillComplexBuffer(mConverter, inInputDataProc, inInputDataProcUserData, 1205 &numEncodedPackets, &mIOBufferList, mPacketDescs); 1206 ElapsedTime(this, fill, mTicksInConverter); 1207#if CAAUDIOFILE_PROFILE 1208 mInConverter = false; 1209#endif 1210 XThrowIf(err != 0 && err != kNoMoreInputRightNow, err, "convert audio packets (write)"); 1211 if (numEncodedPackets == 0) 1212 break; 1213 Byte *buf = (Byte *)mIOBufferList.mBuffers[0].mData; 1214#if VERBOSE_IO 1215 printf("CAAudioFile::WritePacketsFromCallback: wrote %ld packets, %ld bytes\n", numEncodedPackets, mIOBufferList.mBuffers[0].mDataByteSize); 1216 if (mPacketDescs) { 1217 for (UInt32 i = 0; i < numEncodedPackets; ++i) { 1218 printf(" write packet %qd : offset %qd, length %ld\n", mPacketMark + i, mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize); 1219#if VERBOSE_IO >= 2 1220 hexdump(buf + mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize); 1221#endif 1222 } 1223 } 1224#endif 1225 StartTiming(this, write); 1226 XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, mIOBufferList.mBuffers[0].mDataByteSize, mPacketDescs, mPacketMark, &numEncodedPackets, buf), "write audio file"); 1227 ElapsedTime(this, write, mTicksInIO); 1228 mPacketMark += numEncodedPackets; 1229 //mNumberPackets += numEncodedPackets; 1230 if (mFileDataFormat.mFramesPerPacket > 0) 1231 mFrameMark += numEncodedPackets * mFileDataFormat.mFramesPerPacket; 1232 else { 1233 for (UInt32 i = 0; i < numEncodedPackets; ++i) 1234 mFrameMark += mPacketDescs[i].mVariableFramesInPacket; 1235 } 1236 if (err == kNoMoreInputRightNow) 1237 break; 1238 } 1239} 1240 1241#endif // !CAAF_USE_EXTAUDIOFILE