/*============================================================================= CAAudioFileFormats.cpp $Log: CAAudioFileFormats.cpp,v $ Revision 1.3 2005/02/25 02:02:18 dwyatt [4006370 ] fix InferDataFormatFromFileFormat Revision 1.2 2004/09/03 03:55:45 jcm10 fix various header issues Revision 1.1 2004/05/25 23:30:10 dwyatt moved from Source/Tests/AudioFileTools/Utility Revision 1.2 2004/05/15 01:20:10 dwyatt track changes to CAAudioFile etc. Revision 1.1 2004/01/14 00:09:51 dwyatt moved from Source/Tests/AudioFileUtility/Utility Revision 1.2 2003/08/04 23:45:20 dwyatt track extensions, add OSTypeToStr Revision 1.1 2003/06/23 23:07:37 dwyatt initial checkin created Fri Jun 20 2003, Doug Wyatt Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved $NoKeywords: $ =============================================================================*/ #include "CAAudioFileFormats.h" #include #include CAAudioFileFormats *CAAudioFileFormats::sInstance = NULL; CAAudioFileFormats *CAAudioFileFormats::Instance() { if (sInstance == NULL) sInstance = new CAAudioFileFormats; return sInstance; } /* class CompareFileFormatNames { public: bool operator() (const CAAudioFileFormats::FileFormatInfo &a, const CAAudioFileFormats::FileFormatInfo &b) { return CFStringCompare(a.mFileTypeName, b.mFileTypeName, kCFCompareCaseInsensitive | kCFCompareLocalized) == kCFCompareLessThan; } };*/ static int CompareFileFormatNames(const void *va, const void *vb) { CAAudioFileFormats::FileFormatInfo *a = (CAAudioFileFormats::FileFormatInfo *)va, *b = (CAAudioFileFormats::FileFormatInfo *)vb; return CFStringCompare(a->mFileTypeName, b->mFileTypeName, kCFCompareCaseInsensitive | kCFCompareLocalized); } CAAudioFileFormats::CAAudioFileFormats() : mNumFileFormats(0), mFileFormats(NULL) { OSStatus err; UInt32 size; UInt32 *fileTypes = NULL, *writableFormats = NULL, *readableFormats = NULL; int nWritableFormats, nReadableFormats; // get all file types err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_WritableTypes, 0, NULL, &size); if (err != noErr) goto bail; mNumFileFormats = size / sizeof(UInt32); mFileFormats = new FileFormatInfo[mNumFileFormats]; fileTypes = new UInt32[mNumFileFormats]; err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_WritableTypes, 0, NULL, &size, fileTypes); if (err != noErr) goto bail; // get all writable formats err = AudioFormatGetPropertyInfo(kAudioFormatProperty_EncodeFormatIDs, 0, NULL, &size); if (err != noErr) goto bail; nWritableFormats = size / sizeof(UInt32); writableFormats = new UInt32[nWritableFormats]; err = AudioFormatGetProperty(kAudioFormatProperty_EncodeFormatIDs, 0, NULL, &size, writableFormats); if (err != noErr) goto bail; // get all readable formats err = AudioFormatGetPropertyInfo(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size); if (err != noErr) goto bail; nReadableFormats = size / sizeof(UInt32); readableFormats = new UInt32[nReadableFormats]; err = AudioFormatGetProperty(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size, readableFormats); if (err != noErr) goto bail; // get info for each file type for (int i = 0; i < mNumFileFormats; ++i) { FileFormatInfo *ffi = &mFileFormats[i]; OSType filetype = fileTypes[i]; ffi->mFileTypeID = filetype; // file type name ffi->mFileTypeName = NULL; size = sizeof(CFStringRef); err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_FileTypeName, sizeof(UInt32), &filetype, &size, &ffi->mFileTypeName); if (ffi->mFileTypeName) CFRetain(ffi->mFileTypeName); // file extensions size = sizeof(CFArrayRef); err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType, sizeof(OSType), &filetype, &size, &ffi->mExtensions); if (err) ffi->mExtensions = NULL; // file data formats ffi->mNumDataFormats = 0; ffi->mDataFormats = NULL; err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_AvailableFormatIDs, sizeof(UInt32), &filetype, &size); if (err == noErr) { ffi->mNumDataFormats = size / sizeof(OSType); OSType *formatIDs = new OSType[ffi->mNumDataFormats]; err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_AvailableFormatIDs, sizeof(UInt32), &filetype, &size, formatIDs); if (err == noErr) { ffi->mDataFormats = new DataFormatInfo[ffi->mNumDataFormats]; for (int j = 0; j < ffi->mNumDataFormats; ++j) { int k; bool anyBigEndian = false, anyLittleEndian = false; DataFormatInfo *dfi = &ffi->mDataFormats[j]; dfi->mFormatID = formatIDs[j]; dfi->mReadable = (dfi->mFormatID == kAudioFormatLinearPCM); dfi->mWritable = (dfi->mFormatID == kAudioFormatLinearPCM); for (k = 0; k < nReadableFormats; ++k) if (readableFormats[k] == dfi->mFormatID) { dfi->mReadable = true; break; } for (k = 0; k < nWritableFormats; ++k) if (writableFormats[k] == dfi->mFormatID) { dfi->mWritable = true; break; } dfi->mNumVariants = 0; AudioFileTypeAndFormatID tf = { filetype, dfi->mFormatID }; err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_AvailableStreamDescriptionsForFormat, sizeof(AudioFileTypeAndFormatID), &tf, &size); if (err == noErr) { dfi->mNumVariants = size / sizeof(AudioStreamBasicDescription); dfi->mVariants = new AudioStreamBasicDescription[dfi->mNumVariants]; err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_AvailableStreamDescriptionsForFormat, sizeof(AudioFileTypeAndFormatID), &tf, &size, dfi->mVariants); if (err) { dfi->mNumVariants = 0; delete[] dfi->mVariants; dfi->mVariants = NULL; } else { for (k = 0; k < dfi->mNumVariants; ++k) { AudioStreamBasicDescription *desc = &dfi->mVariants[k]; if (desc->mBitsPerChannel > 8) { if (desc->mFormatFlags & kAudioFormatFlagIsBigEndian) anyBigEndian = true; else anyLittleEndian = true; } } } } dfi->mEitherEndianPCM = (anyBigEndian && anyLittleEndian); } } delete[] formatIDs; } } // sort file formats by name qsort(mFileFormats, mNumFileFormats, sizeof(FileFormatInfo), CompareFileFormatNames); bail: delete[] fileTypes; delete[] readableFormats; delete[] writableFormats; } // note that the outgoing format will have zero for the sample rate, channels per frame, bytesPerPacket, bytesPerFrame bool CAAudioFileFormats::InferDataFormatFromFileFormat(AudioFileTypeID filetype, CAStreamBasicDescription &fmt) { // if the file format only supports one data format for (int i = 0; i < mNumFileFormats; ++i) { FileFormatInfo *ffi = &mFileFormats[i]; if (ffi->mFileTypeID == filetype && ffi->mNumDataFormats == 1) { DataFormatInfo *dfi = &ffi->mDataFormats[0]; memset(&fmt, 0, sizeof(fmt)); fmt.mFormatID = dfi->mFormatID; if (dfi->mNumVariants > 0) { // take the first variant as a default fmt = dfi->mVariants[0]; if (dfi->mNumVariants > 1 && dfi->mFormatID == kAudioFormatLinearPCM) { // look for a 16-bit variant as a better default for (int j = 0; j < dfi->mNumVariants; ++j) { AudioStreamBasicDescription *desc = &dfi->mVariants[j]; if (desc->mBitsPerChannel == 16) { fmt = *desc; break; } } } } return true; } } return false; } bool CAAudioFileFormats::InferFileFormatFromDataFormat(const CAStreamBasicDescription &fmt, AudioFileTypeID &filetype) { // if there's exactly one file format that supports this data format FileFormatInfo *theFileFormat = NULL; for (int i = 0; i < mNumFileFormats; ++i) { FileFormatInfo *ffi = &mFileFormats[i]; DataFormatInfo *dfi = ffi->mDataFormats, *dfiend = dfi + ffi->mNumDataFormats; for ( ; dfi < dfiend; ++dfi) if (dfi->mFormatID == fmt.mFormatID) { if (theFileFormat != NULL) return false; // ambiguous theFileFormat = ffi; // got a candidate } } if (theFileFormat == NULL) return false; filetype = theFileFormat->mFileTypeID; return true; } bool CAAudioFileFormats::IsKnownDataFormat(OSType dataFormat) { for (int i = 0; i < mNumFileFormats; ++i) { FileFormatInfo *ffi = &mFileFormats[i]; DataFormatInfo *dfi = ffi->mDataFormats, *dfiend = dfi + ffi->mNumDataFormats; for ( ; dfi < dfiend; ++dfi) if (dfi->mFormatID == dataFormat) return true; } return false; } CAAudioFileFormats::FileFormatInfo * CAAudioFileFormats::FindFileFormat(UInt32 formatID) { for (int i = 0; i < mNumFileFormats; ++i) { FileFormatInfo *ffi = &mFileFormats[i]; if (ffi->mFileTypeID == formatID) return ffi; } return NULL; } bool CAAudioFileFormats::FileFormatInfo::AnyWritableFormats() { DataFormatInfo *dfi = mDataFormats, *dfiend = dfi + mNumDataFormats; for ( ; dfi < dfiend; ++dfi) if (dfi->mWritable) return true; return false; } char *OSTypeToStr(char *buf, OSType t) { char *p = buf; char str[4], *q = str; *(UInt32 *)str = EndianU32_NtoB(t); for (int i = 0; i < 4; ++i) { if (isprint(*q) && *q != '\\') *p++ = *q++; else { sprintf(p, "\\x%02x", *q++); p += 4; } } *p = '\0'; return buf; } int StrToOSType(const char *str, OSType &t) { char buf[4]; const char *p = str; int x; for (int i = 0; i < 4; ++i) { if (*p != '\\') { if ((buf[i] = *p++) == '\0') goto fail; } else { if (*++p != 'x') goto fail; if (sscanf(++p, "%02X", &x) != 1) goto fail; buf[i] = x; p += 2; } } t = EndianU32_BtoN(*(UInt32 *)buf); return p - str; fail: return 0; } #if DEBUG void CAAudioFileFormats::DebugPrint() { for (int i = 0; i < mNumFileFormats; ++i) mFileFormats[i].DebugPrint(); } void CAAudioFileFormats::FileFormatInfo::DebugPrint() { char ftype[20]; char ftypename[64]; CFStringGetCString(mFileTypeName, ftypename, sizeof(ftypename), kCFStringEncodingUTF8); printf("File type: '%s' = %s\n Extensions:", OSTypeToStr(ftype, mFileTypeID), ftypename); int i, n = NumberOfExtensions(); for (i = 0; i < n; ++i) { GetExtension(i, ftype, sizeof(ftype)); printf(" .%s", ftype); } printf("\n Formats:\n"); for (i = 0; i < mNumDataFormats; ++i) mDataFormats[i].DebugPrint(); } void CAAudioFileFormats::DataFormatInfo::DebugPrint() { char buf[20]; static char *ny[] = { "not ", "" }; printf(" '%s': %sreadable %swritable\n", OSTypeToStr(buf, mFormatID), ny[mReadable], ny[mWritable]); for (int i = 0; i < mNumVariants; ++i) { CAStreamBasicDescription desc(mVariants[i]); desc.PrintFormat(stdout, " ", ""); //printf(" %d bytes/frame\n", desc.mBytesPerFrame); } } #endif