PublicUtility/CAAudioFileFormats.cpp
/* |
File: CAAudioFileFormats.cpp |
Abstract: CAAudioFileFormats.h |
Version: 1.01 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2012 Apple Inc. All Rights Reserved. |
*/ |
#include "CAAudioFileFormats.h" |
#include <algorithm> |
#include <ctype.h> |
CAAudioFileFormats *CAAudioFileFormats::sInstance = NULL; |
CAAudioFileFormats *CAAudioFileFormats::Instance(bool loadDataFormats) |
{ |
if (sInstance == NULL) |
sInstance = new CAAudioFileFormats(loadDataFormats); |
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(bool loadDataFormats) : |
mNumFileFormats(0), mFileFormats(NULL) |
{ |
OSStatus err; |
UInt32 size; |
UInt32 *fileTypes = NULL; |
// 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 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 (err == noErr && 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; |
if (loadDataFormats) |
ffi->LoadDataFormats(); |
} |
// sort file formats by name |
qsort(mFileFormats, mNumFileFormats, sizeof(FileFormatInfo), CompareFileFormatNames); |
bail: |
delete[] fileTypes; |
} |
void CAAudioFileFormats::FileFormatInfo::LoadDataFormats() |
{ |
if (mDataFormats != NULL) return; |
UInt32 *writableFormats = NULL, *readableFormats = NULL; |
int nWritableFormats, nReadableFormats; |
// get all writable formats |
UInt32 size; |
OSStatus 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; |
err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_AvailableFormatIDs, sizeof(UInt32), &mFileTypeID, &size); |
if (err == noErr) { |
mNumDataFormats = size / sizeof(OSType); |
OSType *formatIDs = new OSType[mNumDataFormats]; |
err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_AvailableFormatIDs, |
sizeof(UInt32), &mFileTypeID, &size, formatIDs); |
if (err == noErr) { |
mDataFormats = new DataFormatInfo[mNumDataFormats]; |
for (int j = 0; j < mNumDataFormats; ++j) { |
int k; |
bool anyBigEndian = false, anyLittleEndian = false; |
DataFormatInfo *dfi = &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 = { mFileTypeID, 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; |
} |
bail: |
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]; |
ffi->LoadDataFormats(); |
if (ffi->mFileTypeID == filetype && ffi->mNumDataFormats > 0) { |
DataFormatInfo *dfi = &ffi->mDataFormats[0]; |
if (ffi->mNumDataFormats > 1) { |
// file can contain multiple data formats. Take PCM if it's there. |
for (int j = 0; j < ffi->mNumDataFormats; ++j) { |
if (ffi->mDataFormats[j].mFormatID == kAudioFormatLinearPCM) { |
dfi = &ffi->mDataFormats[j]; |
break; |
} |
} |
} |
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::InferFileFormatFromFilename(CFStringRef filename, AudioFileTypeID &filetype) |
{ |
bool result = false; |
CFRange range = CFStringFind(filename, CFSTR("."), kCFCompareBackwards); |
if (range.location == kCFNotFound) return false; |
range.location += 1; |
range.length = CFStringGetLength(filename) - range.location; |
CFStringRef ext = CFStringCreateWithSubstring(NULL, filename, range); |
for (int i = 0; i < mNumFileFormats; ++i) { |
FileFormatInfo *ffi = &mFileFormats[i]; |
if (ffi->MatchExtension(ext)) { |
filetype = ffi->mFileTypeID; |
result = true; |
break; |
} |
} |
CFRelease(ext); |
return result; |
} |
bool CAAudioFileFormats::InferFileFormatFromFilename(const char *filename, AudioFileTypeID &filetype) |
{ |
if (filename == NULL) return false; |
CFStringRef cfname = CFStringCreateWithCString(NULL, filename, kCFStringEncodingUTF8); |
bool result = InferFileFormatFromFilename(cfname, filetype); |
CFRelease(cfname); |
return result; |
} |
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]; |
ffi->LoadDataFormats(); |
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]; |
ffi->LoadDataFormats(); |
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() |
{ |
LoadDataFormats(); |
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] = {0}, *q = str; |
*(UInt32 *)str = CFSwapInt32HostToBig(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') { |
// special-case for 'aac ': if we only got three characters, assume the last was a space |
if (i == 3) { |
--p; |
buf[i] = ' '; |
break; |
} |
goto fail; |
} |
} else { |
if (*++p != 'x') goto fail; |
if (sscanf(++p, "%02X", &x) != 1) goto fail; |
buf[i] = x; |
p += 2; |
} |
} |
t = CFSwapInt32BigToHost(*(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); |
} |
LoadDataFormats(); |
printf("\n Formats:\n"); |
for (i = 0; i < mNumDataFormats; ++i) |
mDataFormats[i].DebugPrint(); |
} |
void CAAudioFileFormats::DataFormatInfo::DebugPrint() |
{ |
char buf[20]; |
static const 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 |
Copyright © 2012 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2012-07-05