this repo has no description
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 ¶ms = 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