diff options
Diffstat (limited to 'distrho/src/CoreAudio106/PublicUtility/CAAudioFileFormats.cpp')
-rwxr-xr-x | distrho/src/CoreAudio106/PublicUtility/CAAudioFileFormats.cpp | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/distrho/src/CoreAudio106/PublicUtility/CAAudioFileFormats.cpp b/distrho/src/CoreAudio106/PublicUtility/CAAudioFileFormats.cpp new file mode 100755 index 00000000..67f716d3 --- /dev/null +++ b/distrho/src/CoreAudio106/PublicUtility/CAAudioFileFormats.cpp @@ -0,0 +1,415 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#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 (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], *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 + |