this repo has no description
at master 592 lines 22 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 CAAudioFileConverter.cpp 40 41=============================================================================*/ 42 43#include "CAAudioFileConverter.h" 44#include "CAChannelLayouts.h" 45#include <sys/stat.h> 46#include <algorithm> 47#include "CAXException.h" 48#include "CAFilePathUtils.h" 49#if !TARGET_OS_MAC 50 #include <AudioCodec.h> 51#endif 52 53char* dirname(const char* str) 54{ 55 static char buffer[4096]; 56 strcpy(buffer, str); 57 return dirname(buffer); 58} 59char* basename(const char* str) 60{ 61 static char buffer[4096]; 62 strcpy(buffer, str); 63 return basename(buffer); 64} 65 66CAAudioFileConverter::ConversionParameters::ConversionParameters() : 67 flags(0) 68{ 69 memset(&input, 0, sizeof(input)); 70 memset(&output, 0, sizeof(output)); 71 output.channels = -1; 72 output.bitRate = -1; 73 output.codecQuality = -1; 74 output.srcQuality = -1; 75 output.strategy = -1; 76 output.primeMethod = -1; 77} 78 79CAAudioFileConverter::CAAudioFileConverter() : 80 mReadBuffer(NULL), 81 mReadPtrs(NULL) 82{ 83 mOutName[0] = '\0'; 84} 85 86CAAudioFileConverter::~CAAudioFileConverter() 87{ 88} 89 90void CAAudioFileConverter::GenerateOutputFileName(const char *inputFilePath, 91 const CAStreamBasicDescription &inputFormat, 92 const CAStreamBasicDescription &outputFormat, OSType outputFileType, 93 char *outName) 94{ 95 struct stat sb; 96 char inputDir[256]; 97 char inputBasename[256]; 98 99 strcpy(inputDir, dirname(inputFilePath)); 100 const char *infname = basename(inputFilePath); 101 const char *inext = strrchr(infname, '.'); 102 if (inext == NULL) strcpy(inputBasename, infname); 103 else { 104 int n; 105 memcpy(inputBasename, infname, n = inext - infname); 106 inputBasename[n] = '\0'; 107 } 108 109 CFArrayRef exts; 110 UInt32 propSize = sizeof(exts); 111 XThrowIfError(AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType, 112 sizeof(OSType), &outputFileType, &propSize, &exts), "generate output file name"); 113 char outputExt[32]; 114 CFStringRef cfext = (CFStringRef)CFArrayGetValueAtIndex(exts, 0); 115 CFStringGetCString(cfext, outputExt, sizeof(outputExt), kCFStringEncodingUTF8); 116 CFRelease(exts); 117 118 // 1. olddir + oldname + newext 119 sprintf(outName, "%s/%s.%s", inputDir, inputBasename, outputExt); 120#if TARGET_OS_MAC 121 if (lstat(outName, &sb)) return; 122#else 123 if (stat(outName, &sb)) return; 124#endif 125 126 if (outputFormat.IsPCM()) { 127 // If sample rate changed: 128 // 2. olddir + oldname + "-SR" + newext 129 if (inputFormat.mSampleRate != outputFormat.mSampleRate && outputFormat.mSampleRate != 0.) { 130 sprintf(outName, "%s/%s-%.0fk.%s", inputDir, inputBasename, outputFormat.mSampleRate/1000., outputExt); 131#if TARGET_OS_MAC 132 if (lstat(outName, &sb)) return; 133#else 134 if (stat(outName, &sb)) return; 135#endif 136 } 137 // If bit depth changed: 138 // 3. olddir + oldname + "-bit" + newext 139 if (inputFormat.mBitsPerChannel != outputFormat.mBitsPerChannel) { 140 sprintf(outName, "%s/%s-%ldbit.%s", inputDir, inputBasename, outputFormat.mBitsPerChannel, outputExt); 141#if TARGET_OS_MAC 142 if (lstat(outName, &sb)) return; 143#else 144 if (stat(outName, &sb)) return; 145#endif 146 } 147 } 148 149 // maybe more with channels/layouts? $$$ 150 151 // now just append digits 152 for (int i = 1; ; ++i) { 153 sprintf(outName, "%s/%s-%d.%s", inputDir, inputBasename, i, outputExt); 154#if TARGET_OS_MAC 155 if (lstat(outName, &sb)) return; 156#else 157 if (stat(outName, &sb)) return; 158#endif 159 } 160} 161 162void CAAudioFileConverter::PrintFormats(const CAAudioChannelLayout *origSrcFileLayout) 163{ 164 const CAAudioChannelLayout &srcFileLayout = mSrcFile.GetFileChannelLayout(); 165 const CAAudioChannelLayout &destFileLayout = mDestFile.GetFileChannelLayout(); 166 167 // see where we've gotten 168 if (mParams.flags & kOpt_Verbose) { 169 printf("Formats:\n"); 170 mSrcFile.GetFileDataFormat().PrintFormat(stdout, " ", "Input file "); 171 if (srcFileLayout.IsValid()) { 172 printf(" %s", 173 CAChannelLayouts::ConstantToString(srcFileLayout.Tag())); 174 if (srcFileLayout.IsValid() && origSrcFileLayout != NULL && 175 srcFileLayout != *origSrcFileLayout) 176 printf(" -- overriding layout %s in file", 177 CAChannelLayouts::ConstantToString(origSrcFileLayout->Tag())); 178 printf("\n"); 179 } 180 mDestFile.GetFileDataFormat().PrintFormat(stdout, " ", "Output file "); 181 if (destFileLayout.IsValid()) 182 printf(" %s\n", 183 CAChannelLayouts::ConstantToString(destFileLayout.Tag())); 184 if (mSrcFile.HasConverter()) { 185 mSrcFile.GetClientDataFormat().PrintFormat(stdout, " ", "Input client "); 186 CAShow(mSrcFile.GetConverter()); 187 } 188 if (mDestFile.HasConverter()) { 189 mDestFile.GetClientDataFormat().PrintFormat(stdout, " ", "Output client"); 190 CAShow(mDestFile.GetConverter()); 191 } 192 } 193} 194 195void CAAudioFileConverter::OpenInputFile() 196{ 197 if (mParams.input.audioFileID) 198 mSrcFile.Wrap(mParams.input.audioFileID, false); 199 else 200 mSrcFile.Open(mParams.input.filePath); 201} 202 203void CAAudioFileConverter::OpenOutputFile(const CAStreamBasicDescription &srcFormat, const CAStreamBasicDescription &destFormat, FSRef &destFSRef, CAAudioChannelLayout &destFileLayout) 204{ 205 const ConversionParameters &params = mParams; 206 207 // output file 208 if (params.output.filePath == NULL) { 209 GenerateOutputFileName(params.input.filePath, srcFormat, 210 destFormat, params.output.fileType, mOutName); 211 } else 212 strcpy(mOutName, params.output.filePath); 213 214 // deal with pre-existing output file 215 if (FSPathMakeRef((UInt8 *)mOutName, &destFSRef, NULL) == noErr) { 216 XThrowIf(!(params.flags & kOpt_OverwriteOutputFile), 1, "overwrite output file"); 217 // not allowed to overwrite 218 // output file exists - delete it 219 XThrowIfError(FSDeleteObject(&destFSRef), "delete output file"); 220 } 221 // get FSRef/CFStringRef for output file 222 FSRef outFolderFSRef; 223 CFStringRef outFileName; 224 XThrowIfError(PosixPathToParentFSRefAndName(mOutName, outFolderFSRef, outFileName), "locate output audio file"); 225 226 // create the output file 227 mDestFile.CreateNew(outFolderFSRef, outFileName, params.output.fileType, destFormat, &destFileLayout.Layout()); 228 CFRelease(outFileName); 229} 230 231 232void CAAudioFileConverter::ConvertFile(const ConversionParameters &_params) 233{ 234 FSRef destFSRef; 235 UInt32 propertySize; 236 CAStreamBasicDescription destFormat; 237 CAAudioChannelLayout origSrcFileLayout, srcFileLayout, destFileLayout; 238 bool openedSourceFile = false, createdOutputFile = false; 239 240 mParams = _params; 241 mReadBuffer = NULL; 242 mReadPtrs = NULL; 243 CABufferList *writeBuffer = NULL; 244 CABufferList *writePtrs = NULL; 245 246 PrepareConversion(); 247 248 try { 249 if (TaggedDecodingFromCAF()) 250 ReadCAFInfo(); 251 OpenInputFile(); 252 openedSourceFile = true; 253 254 // get input file's format 255 const CAStreamBasicDescription &srcFormat = mSrcFile.GetFileDataFormat(); 256 if (mParams.flags & kOpt_Verbose) { 257 printf("Input file: %s, %qd frames\n", mParams.input.filePath ? basename(mParams.input.filePath) : "?", 258 mSrcFile.GetNumberFrames()); 259 } 260 mSrcFormat = srcFormat; 261 262 // prepare output file's format 263 destFormat = mParams.output.dataFormat; 264 265 bool encoding = !destFormat.IsPCM(); 266 bool decoding = !srcFormat.IsPCM(); 267 268 if (!encoding && destFormat.mSampleRate == 0.) 269 // on encode, it's OK to have a 0 sample rate; ExtAudioFile will get the SR from the converter and set it on the file. 270 // on decode or PCM->PCM, a sample rate of 0 is interpreted as using the source sample rate 271 destFormat.mSampleRate = srcFormat.mSampleRate; 272 273 // source channel layout 274 srcFileLayout = mSrcFile.GetFileChannelLayout(); 275 origSrcFileLayout = srcFileLayout; 276 if (mParams.input.channelLayoutTag != 0) { 277 XThrowIf(AudioChannelLayoutTag_GetNumberOfChannels(mParams.input.channelLayoutTag) 278 != srcFormat.mChannelsPerFrame, -1, "input channel layout has wrong number of channels for file"); 279 srcFileLayout = CAAudioChannelLayout(mParams.input.channelLayoutTag); 280 mSrcFile.SetFileChannelLayout(srcFileLayout); 281 } 282 283 // destination channel layout 284 int outChannels = mParams.output.channels; 285 if (mParams.output.channelLayoutTag != 0) { 286 // use the one specified by caller, if any 287 destFileLayout = CAAudioChannelLayout(mParams.output.channelLayoutTag); 288 } else if (srcFileLayout.IsValid()) { 289 // otherwise, assume the same as the source, if any 290 destFileLayout = srcFileLayout; 291 } 292 if (destFileLayout.IsValid()) { 293 // the output channel layout specifies the number of output channels 294 if (outChannels != -1) 295 XThrowIf((unsigned)outChannels != destFileLayout.NumberChannels(), -1, 296 "output channel layout has wrong number of channels"); 297 else 298 outChannels = destFileLayout.NumberChannels(); 299 } 300 301 if (!(mParams.flags & kOpt_NoSanitizeOutputFormat)) { 302 // adjust the output format's channels; output.channels overrides the channels 303 if (outChannels == -1) 304 outChannels = srcFormat.mChannelsPerFrame; 305 if (outChannels > 0) { 306 destFormat.mChannelsPerFrame = outChannels; 307 destFormat.mBytesPerPacket *= outChannels; 308 destFormat.mBytesPerFrame *= outChannels; 309 } 310 311 // use AudioFormat API to clean up the output format 312 propertySize = sizeof(AudioStreamBasicDescription); 313 XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &propertySize, &destFormat), 314 "get destination format info"); 315 } 316 OpenOutputFile(srcFormat, destFormat, destFSRef, destFileLayout); 317 createdOutputFile = true; 318 mDestFormat = destFormat; 319 320 // set up client formats 321 CAStreamBasicDescription srcClientFormat, destClientFormat; 322 { 323 CAAudioChannelLayout srcClientLayout, destClientLayout; 324 325 if (encoding) { 326 if (decoding) { 327 // transcoding 328// XThrowIf(encoding && decoding, -1, "transcoding not currently supported"); 329 330 if (srcFormat.mChannelsPerFrame > 2 || destFormat.mChannelsPerFrame > 2) 331 CAXException::Warning("Transcoding multichannel audio may not handle channel layouts correctly", 0); 332 srcClientFormat.SetCanonical(std::min(srcFormat.mChannelsPerFrame, destFormat.mChannelsPerFrame), true); 333 srcClientFormat.mSampleRate = std::max(srcFormat.mSampleRate, destFormat.mSampleRate); 334 mSrcFile.SetClientFormat(srcClientFormat, NULL); 335 336 destClientFormat = srcClientFormat; 337 } else { 338 // encoding 339 srcClientFormat = srcFormat; 340 destClientFormat = srcFormat; 341 } 342 // by here, destClientFormat will have a valid sample rate 343 destClientLayout = srcFileLayout.IsValid() ? srcFileLayout : destFileLayout; 344 345 mDestFile.SetClientFormat(destClientFormat, &destClientLayout); 346 } else { 347 // decoding or PCM->PCM 348 if (destFormat.mSampleRate == 0.) 349 destFormat.mSampleRate = srcFormat.mSampleRate; 350 351 destClientFormat = destFormat; 352 srcClientFormat = destFormat; 353 srcClientLayout = destFileLayout; 354 355 mSrcFile.SetClientFormat(srcClientFormat, &srcClientLayout); 356 } 357 } 358 359 XThrowIf(srcClientFormat.mBytesPerPacket == 0, -1, "source client format not PCM"); 360 XThrowIf(destClientFormat.mBytesPerPacket == 0, -1, "dest client format not PCM"); 361 if (encoding) { 362 // set the bitrate 363 if (mParams.output.bitRate != -1) { 364 if (mParams.flags & kOpt_Verbose) 365 printf("bitrate = %ld\n", mParams.output.bitRate); 366 mDestFile.SetConverterProperty(kAudioConverterEncodeBitRate, sizeof(UInt32), &mParams.output.bitRate); 367 } 368 369 // set the codec quality 370 if (mParams.output.codecQuality != -1) { 371 if (mParams.flags & kOpt_Verbose) 372 printf("codec quality = %ld\n", mParams.output.codecQuality); 373 mDestFile.SetConverterProperty(kAudioConverterCodecQuality, sizeof(UInt32), &mParams.output.codecQuality); 374 } 375 376 // set the bitrate strategy -- called bitrate format in the codecs since it had already shipped 377 if (mParams.output.strategy != -1) { 378 if (mParams.flags & kOpt_Verbose) 379 printf("strategy = %ld\n", mParams.output.strategy); 380 mDestFile.SetConverterProperty(kAudioCodecBitRateFormat, sizeof(UInt32), &mParams.output.strategy); 381 } 382 } 383 // set the SRC quality 384 if (mParams.output.srcQuality != -1) { 385 if (srcFormat.mSampleRate != 0. && destFormat.mSampleRate != 0. && srcFormat.mSampleRate != destFormat.mSampleRate) { 386 if (mParams.flags & kOpt_Verbose) 387 printf("SRC quality = %ld\n", mParams.output.srcQuality); 388 if (encoding) 389 mDestFile.SetConverterProperty(kAudioConverterSampleRateConverterQuality, sizeof(UInt32), &mParams.output.srcQuality); 390 else 391 mSrcFile.SetConverterProperty(kAudioConverterSampleRateConverterQuality, sizeof(UInt32), &mParams.output.srcQuality); 392 } 393 } 394 if (decoding) { 395 if (mParams.output.primeMethod != -1) 396 mSrcFile.SetConverterProperty(kAudioConverterPrimeMethod, sizeof(UInt32), &mParams.output.primeMethod); 397 } 398 399 PrintFormats(&origSrcFileLayout); 400 401 // prepare I/O buffers 402 UInt32 bytesToRead = 0x10000; 403 UInt32 framesToRead = bytesToRead; // OK, ReadPackets will limit as appropriate 404 ComputeReadSize(srcFormat, destFormat, bytesToRead, framesToRead); 405 406// const SInt64 totalFrames = mSrcFile.GetNumberFrames(); 407//#warning "GetNumberFrames() can be prohibitively slow for some formats" 408 409 mReadBuffer = CABufferList::New("readbuf", srcClientFormat); 410 mReadBuffer->AllocateBuffers(bytesToRead); 411 mReadPtrs = CABufferList::New("readptrs", srcClientFormat); 412 413 BeginConversion(); 414 415 while (true) { 416 //XThrowIf(Progress(mSrcFile.Tell(), totalFrames), userCanceledErr, "user stopped"); 417 // this was commented out for awhile -- performance? make it optional? 418 UInt32 nFrames = framesToRead; 419 mReadPtrs->SetFrom(mReadBuffer); 420 AudioBufferList *readbuf = &mReadPtrs->GetModifiableBufferList(); 421 422 mSrcFile.Read(nFrames, readbuf); 423 //printf("read %ld of %ld frames\n", nFrames, framesToRead); 424 if (nFrames == 0) 425 break; 426 427 mDestFile.Write(nFrames, readbuf); 428 if (ShouldTerminateConversion()) 429 break; 430 } 431 432 if (decoding) { 433 // fix up the destination file's length if necessary and possible 434 SInt64 nframes = mSrcFile.GetNumberFrames(); 435 if (nframes != 0) { 436 // only shorten, don't try to lengthen 437 nframes = SInt64(ceil(nframes * destFormat.mSampleRate / srcFormat.mSampleRate)); 438 if (nframes < mDestFile.GetNumberFrames()) { 439 mDestFile.SetNumberFrames(nframes); 440 } 441 } 442 } 443 EndConversion(); 444 } 445 catch (...) { 446 delete mReadBuffer; 447 delete mReadPtrs; 448 delete writeBuffer; 449 delete writePtrs; 450 try { mSrcFile.Close(); } catch (...) { } 451 try { mDestFile.Close(); } catch (...) { } 452 if (createdOutputFile) 453 unlink(mOutName); 454 throw; 455 } 456 delete mReadBuffer; 457 delete mReadPtrs; 458 delete writeBuffer; 459 delete writePtrs; 460 mSrcFile.Close(); 461 mDestFile.Close(); 462 if (TaggedEncodingToCAF()) 463 WriteCAFInfo(); 464 465 if (mParams.flags & kOpt_Verbose) { 466 // must close to flush encoder; GetNumberFrames() not necessarily valid until afterwards but then 467 // the file is closed 468 CAAudioFile temp; 469 FSRef destFSRef; 470 if (FSPathMakeRef((UInt8 *)mOutName, &destFSRef, NULL) == noErr) { 471 temp.Open(destFSRef); 472 printf("Output file: %s, %qd frames\n", basename(mOutName), temp.GetNumberFrames()); 473 } 474 } 475} 476 477#define kMaxFilename 64 478struct CAFSourceInfo { 479 // our private user data chunk -- careful about compiler laying this out! 480 // big endian 481 char asbd[40]; 482 UInt32 filetype; 483 char filename[kMaxFilename]; 484}; 485 486static void ASBD_NtoB(const AudioStreamBasicDescription *infmt, AudioStreamBasicDescription *outfmt) 487{ 488 *(UInt64 *)&outfmt->mSampleRate = EndianU64_NtoB(*(UInt64 *)&infmt->mSampleRate); 489 outfmt->mFormatID = EndianU32_NtoB(infmt->mFormatID); 490 outfmt->mFormatFlags = EndianU32_NtoB(infmt->mFormatFlags); 491 outfmt->mBytesPerPacket = EndianU32_NtoB(infmt->mBytesPerPacket); 492 outfmt->mFramesPerPacket = EndianU32_NtoB(infmt->mFramesPerPacket); 493 outfmt->mBytesPerFrame = EndianU32_NtoB(infmt->mBytesPerFrame); 494 outfmt->mChannelsPerFrame = EndianU32_NtoB(infmt->mChannelsPerFrame); 495 outfmt->mBitsPerChannel = EndianU32_NtoB(infmt->mBitsPerChannel); 496} 497 498static void ASBD_BtoN(const AudioStreamBasicDescription *infmt, AudioStreamBasicDescription *outfmt) 499{ 500 *(UInt64 *)&outfmt->mSampleRate = EndianU64_BtoN(*(UInt64 *)&infmt->mSampleRate); 501 outfmt->mFormatID = EndianU32_BtoN(infmt->mFormatID); 502 outfmt->mFormatFlags = EndianU32_BtoN(infmt->mFormatFlags); 503 outfmt->mBytesPerPacket = EndianU32_BtoN(infmt->mBytesPerPacket); 504 outfmt->mFramesPerPacket = EndianU32_BtoN(infmt->mFramesPerPacket); 505 outfmt->mBytesPerFrame = EndianU32_BtoN(infmt->mBytesPerFrame); 506 outfmt->mChannelsPerFrame = EndianU32_BtoN(infmt->mChannelsPerFrame); 507 outfmt->mBitsPerChannel = EndianU32_BtoN(infmt->mBitsPerChannel); 508} 509 510void CAAudioFileConverter::WriteCAFInfo() 511{ 512 FSRef fsref; 513 AudioFileID afid = 0; 514 CAFSourceInfo info; 515 UInt32 size; 516 517 try { 518 XThrowIfError(FSPathMakeRef((UInt8 *)mParams.input.filePath, &fsref, NULL), "couldn't locate input file"); 519 XThrowIfError(AudioFileOpen(&fsref, fsRdPerm, 0, &afid), "couldn't open input file"); 520 size = sizeof(AudioFileTypeID); 521 XThrowIfError(AudioFileGetProperty(afid, kAudioFilePropertyFileFormat, &size, &info.filetype), "couldn't get input file's format"); 522 AudioFileClose(afid); 523 afid = 0; 524 525 XThrowIfError(FSPathMakeRef((UInt8 *)mOutName, &fsref, NULL), "couldn't locate output file"); 526 XThrowIfError(AudioFileOpen(&fsref, fsRdWrPerm, 0, &afid), "couldn't open output file"); 527 const char *srcFilename = strrchr(mParams.input.filePath, '/'); 528 if (srcFilename++ == NULL) srcFilename = mParams.input.filePath; 529 ASBD_NtoB(&mSrcFormat, (AudioStreamBasicDescription *)info.asbd); 530 int namelen = std::min(kMaxFilename-1, (int)strlen(srcFilename)); 531 memcpy(info.filename, srcFilename, namelen); 532 info.filename[namelen++] = 0; 533 info.filetype = EndianU32_NtoB(info.filetype); 534 535 XThrowIfError(AudioFileSetUserData(afid, 'srcI', 0, offsetof(CAFSourceInfo, filename) + namelen, &info), "couldn't set CAF file's source info chunk"); 536 AudioFileClose(afid); 537 } 538 catch (...) { 539 if (afid) 540 AudioFileClose(afid); 541 throw; 542 } 543} 544 545void CAAudioFileConverter::ReadCAFInfo() 546{ 547 FSRef fsref; 548 AudioFileID afid = 0; 549 CAFSourceInfo info; 550 UInt32 size; 551 OSStatus err; 552 553 try { 554 XThrowIfError(FSPathMakeRef((UInt8 *)mParams.input.filePath, &fsref, NULL), "couldn't locate input file"); 555 XThrowIfError(AudioFileOpen(&fsref, fsRdPerm, 0, &afid), "couldn't open input file"); 556 size = sizeof(AudioFileTypeID); 557 XThrowIfError(AudioFileGetProperty(afid, kAudioFilePropertyFileFormat, &size, &info.filetype), "couldn't get input file's format"); 558 if (info.filetype == kAudioFileCAFType) { 559 size = sizeof(info); 560 err = AudioFileGetUserData(afid, 'srcI', 0, &size, &info); 561 if (!err) { 562 // restore the following from the original file info: 563 // filetype 564 // data format 565 // filename 566 AudioStreamBasicDescription destfmt; 567 ASBD_BtoN((AudioStreamBasicDescription *)info.asbd, &destfmt); 568 mParams.output.dataFormat = destfmt; 569 mParams.output.fileType = EndianU32_BtoN(info.filetype); 570 if (mParams.output.filePath == NULL) { 571 int len = strlen(mParams.input.filePath) + strlen(info.filename) + 2; 572 char *newname = (char *)malloc(len); // $$$ leaked 573 574 const char *dir = dirname(mParams.input.filePath); 575 if (dir && (dir[0] !='.' && dir[1] != '/')) 576 sprintf(newname, "%s/%s", dir, info.filename); 577 else 578 strcpy(newname, info.filename); 579 mParams.output.filePath = newname; 580 mParams.flags = (mParams.flags & ~kOpt_OverwriteOutputFile) | kOpt_NoSanitizeOutputFormat; 581 } 582 } 583 } 584 AudioFileClose(afid); 585 } 586 catch (...) { 587 if (afid) 588 AudioFileClose(afid); 589 throw; 590 } 591} 592