Jamoma API  0.6.0.a19
CAAudioFileFormats.cpp
1 /* Copyright 2007 Apple Inc. All Rights Reserved.
2 
3  Disclaimer: IMPORTANT: This Apple software is supplied to you by
4  Apple Inc. ("Apple") in consideration of your agreement to the
5  following terms, and your use, installation, modification or
6  redistribution of this Apple software constitutes acceptance of these
7  terms. If you do not agree with these terms, please do not use,
8  install, modify or redistribute this Apple software.
9 
10  In consideration of your agreement to abide by the following terms, and
11  subject to these terms, Apple grants you a personal, non-exclusive
12  license, under Apple's copyrights in this original Apple software (the
13  "Apple Software"), to use, reproduce, modify and redistribute the Apple
14  Software, with or without modifications, in source and/or binary forms;
15  provided that if you redistribute the Apple Software in its entirety and
16  without modifications, you must retain this notice and the following
17  text and disclaimers in all such redistributions of the Apple Software.
18  Neither the name, trademarks, service marks or logos of Apple Inc.
19  may be used to endorse or promote products derived from the Apple
20  Software without specific prior written permission from Apple. Except
21  as expressly stated in this notice, no other rights or licenses, express
22  or implied, are granted by Apple herein, including but not limited to
23  any patent rights that may be infringed by your derivative works or by
24  other works in which the Apple Software may be incorporated.
25 
26  The Apple Software is provided by Apple on an "AS IS" basis. APPLE
27  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
28  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
29  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
30  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
31 
32  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
33  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
36  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
37  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
38  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
39  POSSIBILITY OF SUCH DAMAGE.
40 */
41 #include "CAAudioFileFormats.h"
42 #include <algorithm>
43 #include <ctype.h>
44 
45 CAAudioFileFormats *CAAudioFileFormats::sInstance = NULL;
46 
47 CAAudioFileFormats *CAAudioFileFormats::Instance(bool loadDataFormats)
48 {
49  if (sInstance == NULL)
50  sInstance = new CAAudioFileFormats(loadDataFormats);
51  return sInstance;
52 }
53 
54 /*
55 class CompareFileFormatNames {
56 public:
57  bool operator() (const CAAudioFileFormats::FileFormatInfo &a, const CAAudioFileFormats::FileFormatInfo &b)
58  {
59  return CFStringCompare(a.mFileTypeName, b.mFileTypeName,
60  kCFCompareCaseInsensitive | kCFCompareLocalized) == kCFCompareLessThan;
61  }
62 };*/
63 
64 static int CompareFileFormatNames(const void *va, const void *vb)
65 {
66  CAAudioFileFormats::FileFormatInfo *a = (CAAudioFileFormats::FileFormatInfo *)va,
67  *b = (CAAudioFileFormats::FileFormatInfo *)vb;
68  return CFStringCompare(a->mFileTypeName, b->mFileTypeName,
69  kCFCompareCaseInsensitive | kCFCompareLocalized);
70 }
71 
72 CAAudioFileFormats::CAAudioFileFormats(bool loadDataFormats) :
73  mNumFileFormats(0), mFileFormats(NULL)
74 {
75  OSStatus err;
76  UInt32 size;
77  UInt32 *fileTypes = NULL;
78 
79  // get all file types
80  err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_WritableTypes, 0, NULL, &size);
81  if (err != noErr) goto bail;
82  mNumFileFormats = size / sizeof(UInt32);
83  mFileFormats = new FileFormatInfo[mNumFileFormats];
84  fileTypes = new UInt32[mNumFileFormats];
85  err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_WritableTypes, 0, NULL, &size, fileTypes);
86  if (err != noErr) goto bail;
87 
88  // get info for each file type
89  for (int i = 0; i < mNumFileFormats; ++i) {
90  FileFormatInfo *ffi = &mFileFormats[i];
91  OSType filetype = fileTypes[i];
92 
93  ffi->mFileTypeID = filetype;
94 
95  // file type name
96  ffi->mFileTypeName = NULL;
97  size = sizeof(CFStringRef);
98  err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_FileTypeName, sizeof(UInt32), &filetype, &size, &ffi->mFileTypeName);
99  if (ffi->mFileTypeName)
100  CFRetain(ffi->mFileTypeName);
101 
102  // file extensions
103  size = sizeof(CFArrayRef);
104  err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType,
105  sizeof(OSType), &filetype, &size, &ffi->mExtensions);
106  if (err)
107  ffi->mExtensions = NULL;
108 
109  // file data formats
110  ffi->mNumDataFormats = 0;
111  ffi->mDataFormats = NULL;
112 
113  if (loadDataFormats)
114  ffi->LoadDataFormats();
115  }
116 
117  // sort file formats by name
118  qsort(mFileFormats, mNumFileFormats, sizeof(FileFormatInfo), CompareFileFormatNames);
119 bail:
120  delete[] fileTypes;
121 }
122 
123 void CAAudioFileFormats::FileFormatInfo::LoadDataFormats()
124 {
125  if (mDataFormats != NULL) return;
126 
127  UInt32 *writableFormats = NULL, *readableFormats = NULL;
128  int nWritableFormats, nReadableFormats;
129  // get all writable formats
130  UInt32 size;
131  OSStatus err = AudioFormatGetPropertyInfo(kAudioFormatProperty_EncodeFormatIDs, 0, NULL, &size);
132  if (err != noErr) goto bail;
133  nWritableFormats = size / sizeof(UInt32);
134  writableFormats = new UInt32[nWritableFormats];
135  err = AudioFormatGetProperty(kAudioFormatProperty_EncodeFormatIDs, 0, NULL, &size, writableFormats);
136  if (err != noErr) goto bail;
137 
138  // get all readable formats
139  err = AudioFormatGetPropertyInfo(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size);
140  if (err != noErr) goto bail;
141  nReadableFormats = size / sizeof(UInt32);
142  readableFormats = new UInt32[nReadableFormats];
143  err = AudioFormatGetProperty(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size, readableFormats);
144  if (err != noErr) goto bail;
145 
146  err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_AvailableFormatIDs, sizeof(UInt32), &mFileTypeID, &size);
147  if (err == noErr) {
148  mNumDataFormats = size / sizeof(OSType);
149  OSType *formatIDs = new OSType[mNumDataFormats];
150  err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_AvailableFormatIDs,
151  sizeof(UInt32), &mFileTypeID, &size, formatIDs);
152  if (err == noErr) {
153  mDataFormats = new DataFormatInfo[mNumDataFormats];
154  for (int j = 0; j < mNumDataFormats; ++j) {
155  int k;
156  bool anyBigEndian = false, anyLittleEndian = false;
157  DataFormatInfo *dfi = &mDataFormats[j];
158  dfi->mFormatID = formatIDs[j];
159  dfi->mReadable = (dfi->mFormatID == kAudioFormatLinearPCM);
160  dfi->mWritable = (dfi->mFormatID == kAudioFormatLinearPCM);
161  for (k = 0; k < nReadableFormats; ++k)
162  if (readableFormats[k] == dfi->mFormatID) {
163  dfi->mReadable = true;
164  break;
165  }
166  for (k = 0; k < nWritableFormats; ++k)
167  if (writableFormats[k] == dfi->mFormatID) {
168  dfi->mWritable = true;
169  break;
170  }
171 
172  dfi->mNumVariants = 0;
173  AudioFileTypeAndFormatID tf = { mFileTypeID, dfi->mFormatID };
174  err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_AvailableStreamDescriptionsForFormat,
175  sizeof(AudioFileTypeAndFormatID), &tf, &size);
176  if (err == noErr) {
177  dfi->mNumVariants = size / sizeof(AudioStreamBasicDescription);
178  dfi->mVariants = new AudioStreamBasicDescription[dfi->mNumVariants];
179  err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_AvailableStreamDescriptionsForFormat,
180  sizeof(AudioFileTypeAndFormatID), &tf, &size, dfi->mVariants);
181  if (err) {
182  dfi->mNumVariants = 0;
183  delete[] dfi->mVariants;
184  dfi->mVariants = NULL;
185  } else {
186  for (k = 0; k < dfi->mNumVariants; ++k) {
187  AudioStreamBasicDescription *desc = &dfi->mVariants[k];
188  if (desc->mBitsPerChannel > 8) {
189  if (desc->mFormatFlags & kAudioFormatFlagIsBigEndian)
190  anyBigEndian = true;
191  else
192  anyLittleEndian = true;
193  }
194  }
195  }
196  }
197 
198  dfi->mEitherEndianPCM = (anyBigEndian && anyLittleEndian);
199  }
200  }
201  delete[] formatIDs;
202  }
203 bail:
204  delete[] readableFormats;
205  delete[] writableFormats;
206 }
207 
208 // note that the outgoing format will have zero for the sample rate, channels per frame, bytesPerPacket, bytesPerFrame
209 bool CAAudioFileFormats::InferDataFormatFromFileFormat(AudioFileTypeID filetype, CAStreamBasicDescription &fmt)
210 {
211  // if the file format only supports one data format
212  for (int i = 0; i < mNumFileFormats; ++i) {
213  FileFormatInfo *ffi = &mFileFormats[i];
214  ffi->LoadDataFormats();
215  if (ffi->mFileTypeID == filetype && ffi->mNumDataFormats > 0) {
216  DataFormatInfo *dfi = &ffi->mDataFormats[0];
217  if (ffi->mNumDataFormats > 1) {
218  // file can contain multiple data formats. Take PCM if it's there.
219  for (int j = 0; j < ffi->mNumDataFormats; ++j) {
220  if (ffi->mDataFormats[j].mFormatID == kAudioFormatLinearPCM) {
221  dfi = &ffi->mDataFormats[j];
222  break;
223  }
224  }
225  }
226  memset(&fmt, 0, sizeof(fmt));
227  fmt.mFormatID = dfi->mFormatID;
228  if (dfi->mNumVariants > 0) {
229  // take the first variant as a default
230  fmt = dfi->mVariants[0];
231  if (dfi->mNumVariants > 1 && dfi->mFormatID == kAudioFormatLinearPCM) {
232  // look for a 16-bit variant as a better default
233  for (int j = 0; j < dfi->mNumVariants; ++j) {
234  AudioStreamBasicDescription *desc = &dfi->mVariants[j];
235  if (desc->mBitsPerChannel == 16) {
236  fmt = *desc;
237  break;
238  }
239  }
240  }
241  }
242  return true;
243  }
244  }
245  return false;
246 }
247 
248 bool CAAudioFileFormats::InferFileFormatFromFilename(CFStringRef filename, AudioFileTypeID &filetype)
249 {
250  bool result = false;
251  CFRange range = CFStringFind(filename, CFSTR("."), kCFCompareBackwards);
252  if (range.location == kCFNotFound) return false;
253  range.location += 1;
254  range.length = CFStringGetLength(filename) - range.location;
255  CFStringRef ext = CFStringCreateWithSubstring(NULL, filename, range);
256  for (int i = 0; i < mNumFileFormats; ++i) {
257  FileFormatInfo *ffi = &mFileFormats[i];
258  if (ffi->MatchExtension(ext)) {
259  filetype = ffi->mFileTypeID;
260  result = true;
261  break;
262  }
263  }
264  CFRelease(ext);
265  return result;
266 }
267 
268 bool CAAudioFileFormats::InferFileFormatFromFilename(const char *filename, AudioFileTypeID &filetype)
269 {
270  if (filename == NULL) return false;
271  CFStringRef cfname = CFStringCreateWithCString(NULL, filename, kCFStringEncodingUTF8);
272  bool result = InferFileFormatFromFilename(cfname, filetype);
273  CFRelease(cfname);
274  return result;
275 }
276 
277 bool CAAudioFileFormats::InferFileFormatFromDataFormat(const CAStreamBasicDescription &fmt,
278  AudioFileTypeID &filetype)
279 {
280  // if there's exactly one file format that supports this data format
281  FileFormatInfo *theFileFormat = NULL;
282  for (int i = 0; i < mNumFileFormats; ++i) {
283  FileFormatInfo *ffi = &mFileFormats[i];
284  ffi->LoadDataFormats();
285  DataFormatInfo *dfi = ffi->mDataFormats, *dfiend = dfi + ffi->mNumDataFormats;
286  for ( ; dfi < dfiend; ++dfi)
287  if (dfi->mFormatID == fmt.mFormatID) {
288  if (theFileFormat != NULL)
289  return false; // ambiguous
290  theFileFormat = ffi; // got a candidate
291  }
292  }
293  if (theFileFormat == NULL)
294  return false;
295  filetype = theFileFormat->mFileTypeID;
296  return true;
297 }
298 
299 bool CAAudioFileFormats::IsKnownDataFormat(OSType dataFormat)
300 {
301  for (int i = 0; i < mNumFileFormats; ++i) {
302  FileFormatInfo *ffi = &mFileFormats[i];
303  ffi->LoadDataFormats();
304  DataFormatInfo *dfi = ffi->mDataFormats, *dfiend = dfi + ffi->mNumDataFormats;
305  for ( ; dfi < dfiend; ++dfi)
306  if (dfi->mFormatID == dataFormat)
307  return true;
308  }
309  return false;
310 }
311 
312 CAAudioFileFormats::FileFormatInfo * CAAudioFileFormats::FindFileFormat(UInt32 formatID)
313 {
314  for (int i = 0; i < mNumFileFormats; ++i) {
315  FileFormatInfo *ffi = &mFileFormats[i];
316  if (ffi->mFileTypeID == formatID)
317  return ffi;
318  }
319  return NULL;
320 }
321 
322 bool CAAudioFileFormats::FileFormatInfo::AnyWritableFormats()
323 {
324  LoadDataFormats();
325  DataFormatInfo *dfi = mDataFormats, *dfiend = dfi + mNumDataFormats;
326  for ( ; dfi < dfiend; ++dfi)
327  if (dfi->mWritable)
328  return true;
329  return false;
330 }
331 
332 char *OSTypeToStr(char *buf, OSType t)
333 {
334  char *p = buf;
335  char str[4], *q = str;
336  *(UInt32 *)str = CFSwapInt32HostToBig(t);
337  for (int i = 0; i < 4; ++i) {
338  if (isprint(*q) && *q != '\\')
339  *p++ = *q++;
340  else {
341  sprintf(p, "\\x%02x", *q++);
342  p += 4;
343  }
344  }
345  *p = '\0';
346  return buf;
347 }
348 
349 int StrToOSType(const char *str, OSType &t)
350 {
351  char buf[4];
352  const char *p = str;
353  int x;
354  for (int i = 0; i < 4; ++i) {
355  if (*p != '\\') {
356  if ((buf[i] = *p++) == '\0') {
357  // special-case for 'aac ': if we only got three characters, assume the last was a space
358  if (i == 3) {
359  --p;
360  buf[i] = ' ';
361  break;
362  }
363  goto fail;
364  }
365  } else {
366  if (*++p != 'x') goto fail;
367  if (sscanf(++p, "%02X", &x) != 1) goto fail;
368  buf[i] = x;
369  p += 2;
370  }
371  }
372  t = CFSwapInt32BigToHost(*(UInt32 *)buf);
373  return p - str;
374 fail:
375  return 0;
376 }
377 
378 #if DEBUG
379 
380 void CAAudioFileFormats::DebugPrint()
381 {
382  for (int i = 0; i < mNumFileFormats; ++i)
383  mFileFormats[i].DebugPrint();
384 }
385 
386 void CAAudioFileFormats::FileFormatInfo::DebugPrint()
387 {
388  char ftype[20];
389  char ftypename[64];
390  CFStringGetCString(mFileTypeName, ftypename, sizeof(ftypename), kCFStringEncodingUTF8);
391  printf("File type: '%s' = %s\n Extensions:", OSTypeToStr(ftype, mFileTypeID), ftypename);
392  int i, n = NumberOfExtensions();
393  for (i = 0; i < n; ++i) {
394  GetExtension(i, ftype, sizeof(ftype));
395  printf(" .%s", ftype);
396  }
397  LoadDataFormats();
398  printf("\n Formats:\n");
399  for (i = 0; i < mNumDataFormats; ++i)
400  mDataFormats[i].DebugPrint();
401 }
402 
403 void CAAudioFileFormats::DataFormatInfo::DebugPrint()
404 {
405  char buf[20];
406  static const char *ny[] = { "not ", "" };
407  printf(" '%s': %sreadable %swritable\n", OSTypeToStr(buf, mFormatID), ny[mReadable], ny[mWritable]);
408  for (int i = 0; i < mNumVariants; ++i) {
409  CAStreamBasicDescription desc(mVariants[i]);
410  desc.PrintFormat(stdout, " ", "");
411  //printf(" %d bytes/frame\n", desc.mBytesPerFrame);
412  }
413 }
414 #endif
415