From 26f135cb8826fb26895be6706855dd2fd96da1a2 Mon Sep 17 00:00:00 2001 From: Matthias Neeracher Date: Fri, 17 Nov 2006 04:03:14 +0000 Subject: [PATCH] Added unfinished audio code, switching machines --- Sources/CoreAudioSDK/AUOutputBL.cpp | 160 ++++++ Sources/CoreAudioSDK/AUOutputBL.h | 116 ++++ Sources/CoreAudioSDK/CAAudioFileFormats.cpp | 393 +++++++++++++ Sources/CoreAudioSDK/CAAudioFileFormats.h | 142 +++++ .../CoreAudioSDK/CAStreamBasicDescription.cpp | 528 ++++++++++++++++++ .../CoreAudioSDK/CAStreamBasicDescription.h | 226 ++++++++ Sources/VLSoundOut.cpp | 214 ++++++- Sources/VLSoundOut.h | 2 + Vocalese.xcodeproj/project.pbxproj | 8 + 9 files changed, 1775 insertions(+), 14 deletions(-) create mode 100644 Sources/CoreAudioSDK/AUOutputBL.cpp create mode 100644 Sources/CoreAudioSDK/AUOutputBL.h create mode 100644 Sources/CoreAudioSDK/CAAudioFileFormats.cpp create mode 100644 Sources/CoreAudioSDK/CAAudioFileFormats.h create mode 100644 Sources/CoreAudioSDK/CAStreamBasicDescription.cpp create mode 100644 Sources/CoreAudioSDK/CAStreamBasicDescription.h diff --git a/Sources/CoreAudioSDK/AUOutputBL.cpp b/Sources/CoreAudioSDK/AUOutputBL.cpp new file mode 100644 index 0000000..f88aa74 --- /dev/null +++ b/Sources/CoreAudioSDK/AUOutputBL.cpp @@ -0,0 +1,160 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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. +*/ +/*============================================================================= + AUOutputBL.h + +=============================================================================*/ +#include "AUOutputBL.h" + +/* +struct AudioBufferList +{ + UInt32 mNumberBuffers; + AudioBuffer mBuffers[1]; +}; +struct AudioBuffer +{ + UInt32 mNumberChannels; // number of interleaved channels in the buffer + UInt32 mDataByteSize; // the size of the buffer pointed to by mData + void* mData; // the pointer to the buffer +}; +*/ + +AUOutputBL::AUOutputBL (const CAStreamBasicDescription &inDesc, UInt32 inDefaultNumFrames) + : mFormat (inDesc), + mBufferMemory(NULL), + mBufferList (NULL), + mNumberBuffers (0), // keep this here, so can ensure integrity of ABL + mBufferSize (0), + mFrames(inDefaultNumFrames) +{ + mNumberBuffers = mFormat.IsInterleaved() ? 1 : mFormat.NumberChannels(); + mBufferList = reinterpret_cast(new Byte[sizeof(UInt32) + (mNumberBuffers * sizeof(AudioBuffer))]); +} + +AUOutputBL::~AUOutputBL() +{ + if (mBufferMemory) + delete[] mBufferMemory; + + if (mBufferList) + delete [] mBufferList; +} + +void AUOutputBL::Prepare (UInt32 inNumFrames, bool inWantNullBufferIfAllocated) +{ + UInt32 channelsPerBuffer = mFormat.IsInterleaved() ? mFormat.NumberChannels() : 1; + + if (mBufferMemory == NULL || inWantNullBufferIfAllocated) + { + mBufferList->mNumberBuffers = mNumberBuffers; + AudioBuffer *buf = &mBufferList->mBuffers[0]; + for (UInt32 i = 0; i < mNumberBuffers; ++i, ++buf) { + buf->mNumberChannels = channelsPerBuffer; + buf->mDataByteSize = mFormat.FramesToBytes (inNumFrames); + buf->mData = NULL; + } + } + else + { + UInt32 nBytes = mFormat.FramesToBytes (inNumFrames); + if ((nBytes * mNumberBuffers) > AllocatedBytes()) + throw OSStatus(-10874);//(kAudioUnitErr_TooManyFramesToProcess); + + mBufferList->mNumberBuffers = mNumberBuffers; + AudioBuffer *buf = &mBufferList->mBuffers[0]; + Byte* p = mBufferMemory; + for (UInt32 i = 0; i < mNumberBuffers; ++i, ++buf) { + buf->mNumberChannels = channelsPerBuffer; + buf->mDataByteSize = nBytes; + buf->mData = p; + p += mBufferSize; + } + } +} + + +void AUOutputBL::Allocate (UInt32 inNumFrames) +{ + if (inNumFrames) + { + UInt32 nBytes = mFormat.FramesToBytes (inNumFrames); + + if (nBytes <= AllocatedBytes()) + return; + + // align successive buffers for Altivec and to take alternating + // cache line hits by spacing them by odd multiples of 16 + if (mNumberBuffers > 1) + nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10; + + mBufferSize = nBytes; + + UInt32 memorySize = mBufferSize * mNumberBuffers; + Byte *newMemory = new Byte[memorySize]; + memset(newMemory, 0, memorySize); // make buffer "hot" + + Byte *oldMemory = mBufferMemory; + mBufferMemory = newMemory; + delete[] oldMemory; + + mFrames = inNumFrames; + } + else + { + if (mBufferMemory) { + delete [] mBufferMemory; + mBufferMemory = NULL; + } + mBufferSize = 0; + mFrames = 0; + } +} + +#if DEBUG +void AUOutputBL::Print() +{ + printf ("AUOutputBL::Print\n"); + mFormat.Print(); + printf ("Num Buffers:%d, mFrames:%d, allocatedMemory:%c\n", (int)mBufferList->mNumberBuffers, (int)mFrames, (mBufferMemory != NULL ? 'T' : 'F')); + AudioBuffer *buf = &mBufferList->mBuffers[0]; + for (UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i, ++buf) + printf ("\tBuffer:%d, Size:%d, Chans:%d, Buffer:%p\n", (int)i, (int)buf->mDataByteSize, (int)buf->mNumberChannels, buf->mData); +} +#endif + diff --git a/Sources/CoreAudioSDK/AUOutputBL.h b/Sources/CoreAudioSDK/AUOutputBL.h new file mode 100644 index 0000000..7fb506b --- /dev/null +++ b/Sources/CoreAudioSDK/AUOutputBL.h @@ -0,0 +1,116 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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. +*/ +/*============================================================================= + AUOutputBL.h + +=============================================================================*/ + +#ifndef __AUOutputBL_h__ +#define __AUOutputBL_h__ + +#include "CAStreamBasicDescription.h" +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +// ____________________________________________________________________________ +// +// AUOutputBL - Simple Buffer List wrapper targetted to use with retrieving AU output +// Works in one of two ways (both adjustable)... Can use it with NULL pointers, or allocate +// memory to receive the data in. + +// Before using this with any call to AudioUnitRender, it needs to be Prepared +// as some calls to AudioUnitRender can reset the ABL + +class AUOutputBL { +public: + + // you CANNOT use one of these - it will crash! +// AUOutputBL (); + + // this is the constructor that you use + // it can't be reset once you've constructed it + AUOutputBL (const CAStreamBasicDescription &inDesc, UInt32 inDefaultNumFrames = 512); + ~AUOutputBL(); + + void Prepare () + { + Prepare (mFrames); + } + + // this version can throw if this is an allocted ABL and inNumFrames is > AllocatedFrames() + // you can set the bool to true if you want a NULL buffer list even if allocated + // inNumFrames must be a valid number (will throw if inNumFrames is 0) + void Prepare (UInt32 inNumFrames, bool inWantNullBufferIfAllocated = false); + + AudioBufferList* ABL() { return mBufferList; } + + // You only need to call this if you want to allocate a buffer list + // if you want an empty buffer list, just call Prepare() + // if you want to dispose previously allocted memory, pass in 0 + // then you either have an empty buffer list, or you can re-allocate + // Memory is kept around if an Allocation request is less than what is currently allocated + void Allocate (UInt32 inNumberFrames); + + UInt32 AllocatedFrames() const { return mFrames; } + + const CAStreamBasicDescription& GetFormat() const { return mFormat; } + +#if DEBUG + void Print(); +#endif + +private: + UInt32 AllocatedBytes () const { return (mBufferSize * mNumberBuffers); } + + CAStreamBasicDescription mFormat; + Byte* mBufferMemory; + AudioBufferList* mBufferList; + UInt32 mNumberBuffers; + UInt32 mBufferSize; + UInt32 mFrames; + +// don't want to copy these.. can if you want, but more code to write! + AUOutputBL () {} + AUOutputBL (const AUOutputBL &c) {} + AUOutputBL& operator= (const AUOutputBL& c) { return *this; } +}; + +#endif // __AUOutputBL_h__ diff --git a/Sources/CoreAudioSDK/CAAudioFileFormats.cpp b/Sources/CoreAudioSDK/CAAudioFileFormats.cpp new file mode 100644 index 0000000..bfda712 --- /dev/null +++ b/Sources/CoreAudioSDK/CAAudioFileFormats.cpp @@ -0,0 +1,393 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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. +*/ +/*============================================================================= + CAAudioFileFormats.cpp + +=============================================================================*/ + +#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::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]; + 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') { + // 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 = 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 + diff --git a/Sources/CoreAudioSDK/CAAudioFileFormats.h b/Sources/CoreAudioSDK/CAAudioFileFormats.h new file mode 100644 index 0000000..a684501 --- /dev/null +++ b/Sources/CoreAudioSDK/CAAudioFileFormats.h @@ -0,0 +1,142 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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. +*/ +/*============================================================================= + CAAudioFileFormats.h + +=============================================================================*/ + +#ifndef __CAAudioFileFormats_h__ +#define __CAAudioFileFormats_h__ + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif +#include "CAStreamBasicDescription.h" + +class CAAudioFileFormats { +public: + struct DataFormatInfo { + DataFormatInfo() : mVariants(NULL) { } + ~DataFormatInfo() { delete[] mVariants; } + + UInt32 mFormatID; + int mNumVariants; + AudioStreamBasicDescription * mVariants; + bool mReadable; + bool mWritable; + bool mEitherEndianPCM; + +#if DEBUG + void DebugPrint(); +#endif + }; + + struct FileFormatInfo { + FileFormatInfo() : mFileTypeName(NULL), mExtensions(NULL), mDataFormats(NULL) { } + ~FileFormatInfo() { + delete[] mDataFormats; + if (mFileTypeName) + CFRelease(mFileTypeName); + if (mExtensions) + CFRelease(mExtensions); + } + + AudioFileTypeID mFileTypeID; + CFStringRef mFileTypeName; + CFArrayRef mExtensions; + int mNumDataFormats; + DataFormatInfo * mDataFormats; + + int NumberOfExtensions() { return mExtensions ? CFArrayGetCount(mExtensions) : 0; } + char * GetExtension(int index, char *buf, int buflen) { + CFStringRef cfext = (CFStringRef)CFArrayGetValueAtIndex(mExtensions, index); + CFStringGetCString(cfext, buf, buflen, kCFStringEncodingUTF8); + return buf; + } + bool MatchExtension(CFStringRef testExt) { // testExt should not include "." + CFIndex n = NumberOfExtensions(); + for (CFIndex i = 0; i < n; ++i) { + CFStringRef ext = (CFStringRef)CFArrayGetValueAtIndex(mExtensions, i); + if (CFStringCompare(ext, testExt, kCFCompareCaseInsensitive) == kCFCompareEqualTo) + return true; + } + return false; + } + bool AnyWritableFormats(); + +#if DEBUG + void DebugPrint(); +#endif + }; + +private: // use Instance() + CAAudioFileFormats(); + ~CAAudioFileFormats(); +public: + + bool InferDataFormatFromFileFormat(AudioFileTypeID filetype, CAStreamBasicDescription &fmt); + + bool InferFileFormatFromFilename(const char *filename, AudioFileTypeID &filetype); + + bool InferFileFormatFromFilename(CFStringRef filename, AudioFileTypeID &filetype); + + bool InferFileFormatFromDataFormat(const CAStreamBasicDescription &fmt, AudioFileTypeID &filetype); + + bool IsKnownDataFormat(UInt32 dataFormat); + +#if DEBUG + void DebugPrint(); +#endif + + int mNumFileFormats; + FileFormatInfo * mFileFormats; + + FileFormatInfo * FindFileFormat(UInt32 formatID); + + static CAAudioFileFormats * Instance(); + +private: + static CAAudioFileFormats * sInstance; +}; + +char * OSTypeToStr(char *buf, UInt32 t); +int StrToOSType(const char *str, UInt32 &t); + +#endif // __CAAudioFileFormats_h__ diff --git a/Sources/CoreAudioSDK/CAStreamBasicDescription.cpp b/Sources/CoreAudioSDK/CAStreamBasicDescription.cpp new file mode 100644 index 0000000..0c9c588 --- /dev/null +++ b/Sources/CoreAudioSDK/CAStreamBasicDescription.cpp @@ -0,0 +1,528 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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. +*/ +/*============================================================================= + CAStreamBasicDescription.cpp + +=============================================================================*/ + +#include "CAConditionalMacros.h" + +#include "CAStreamBasicDescription.h" +#include "CAMath.h" + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +#pragma mark This file needs to compile on earlier versions of the OS, so please keep that in mind when editing it + +const AudioStreamBasicDescription CAStreamBasicDescription::sEmpty = { 0.0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +CAStreamBasicDescription::CAStreamBasicDescription(double inSampleRate, UInt32 inFormatID, + UInt32 inBytesPerPacket, UInt32 inFramesPerPacket, + UInt32 inBytesPerFrame, UInt32 inChannelsPerFrame, + UInt32 inBitsPerChannel, UInt32 inFormatFlags) +{ + mSampleRate = inSampleRate; + mFormatID = inFormatID; + mBytesPerPacket = inBytesPerPacket; + mFramesPerPacket = inFramesPerPacket; + mBytesPerFrame = inBytesPerFrame; + mChannelsPerFrame = inChannelsPerFrame; + mBitsPerChannel = inBitsPerChannel; + mFormatFlags = inFormatFlags; + mReserved = 0; +} + +void CAStreamBasicDescription::PrintFormat(FILE *f, const char *indent, const char *name) const +{ + fprintf(f, "%s%s ", indent, name); + char formatID[5]; + *(UInt32 *)formatID = CFSwapInt32HostToBig(mFormatID); + formatID[4] = '\0'; + fprintf(f, "%2d ch, %6.0f Hz, '%-4.4s' (0x%08X) ", + (int)NumberChannels(), mSampleRate, formatID, + (int)mFormatFlags); + if (mFormatID == kAudioFormatLinearPCM) { + bool isInt = !(mFormatFlags & kLinearPCMFormatFlagIsFloat); + int wordSize = SampleWordSize(); + const char *endian = (wordSize > 1) ? + ((mFormatFlags & kLinearPCMFormatFlagIsBigEndian) ? " big-endian" : " little-endian" ) : ""; + const char *sign = isInt ? + ((mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) ? " signed" : " unsigned") : ""; + const char *floatInt = isInt ? "integer" : "float"; + char packed[32]; + if (wordSize > 0 && PackednessIsSignificant()) { + if (mFormatFlags & kLinearPCMFormatFlagIsPacked) + sprintf(packed, "packed in %d bytes", wordSize); + else + sprintf(packed, "unpacked in %d bytes", wordSize); + } else + packed[0] = '\0'; + const char *align = (wordSize > 0 && AlignmentIsSignificant()) ? + ((mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) ? " high-aligned" : " low-aligned") : ""; + const char *deinter = (mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? ", deinterleaved" : ""; + const char *commaSpace = (packed[0]!='\0') || (align[0]!='\0') ? ", " : ""; + + fprintf(f, "%d-bit%s%s %s%s%s%s%s\n", + (int)mBitsPerChannel, endian, sign, floatInt, + commaSpace, packed, align, deinter); + } else if (mFormatID == 'alac') { // kAudioFormatAppleLossless + int sourceBits = 0; + switch (mFormatFlags) + { + case 1: // kAppleLosslessFormatFlag_16BitSourceData + sourceBits = 16; + break; + case 2: // kAppleLosslessFormatFlag_20BitSourceData + sourceBits = 20; + break; + case 3: // kAppleLosslessFormatFlag_24BitSourceData + sourceBits = 24; + break; + case 4: // kAppleLosslessFormatFlag_32BitSourceData + sourceBits = 32; + break; + } + if (sourceBits) + fprintf(f, "from %d-bit source, ", sourceBits); + else + fprintf(f, "from UNKNOWN source bit depth, "); + + fprintf(f, "%d frames/packet\n", (int)mFramesPerPacket); + } + else + fprintf(f, "%d bits/channel, %d bytes/packet, %d frames/packet, %d bytes/frame\n", + (int)mBitsPerChannel, (int)mBytesPerPacket, (int)mFramesPerPacket, (int)mBytesPerFrame); +} + +void CAStreamBasicDescription::NormalizeLinearPCMFormat(AudioStreamBasicDescription& ioDescription) +{ + // the only thing that changes is to make mixable linear PCM into the canonical linear PCM format + if((ioDescription.mFormatID == kAudioFormatLinearPCM) && ((ioDescription.mFormatFlags & kIsNonMixableFlag) == 0)) + { + // the canonical linear PCM format is 32 bit native endian floats + ioDescription.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; + ioDescription.mBytesPerPacket = sizeof(Float32) * ioDescription.mChannelsPerFrame; + ioDescription.mFramesPerPacket = 1; + ioDescription.mBytesPerFrame = sizeof(Float32) * ioDescription.mChannelsPerFrame; + ioDescription.mBitsPerChannel = 8 * sizeof(Float32); + } +} + +void CAStreamBasicDescription::ResetFormat(AudioStreamBasicDescription& ioDescription) +{ + ioDescription.mSampleRate = 0; + ioDescription.mFormatID = 0; + ioDescription.mBytesPerPacket = 0; + ioDescription.mFramesPerPacket = 0; + ioDescription.mBytesPerFrame = 0; + ioDescription.mChannelsPerFrame = 0; + ioDescription.mBitsPerChannel = 0; + ioDescription.mFormatFlags = 0; +} + +void CAStreamBasicDescription::FillOutFormat(AudioStreamBasicDescription& ioDescription, const AudioStreamBasicDescription& inTemplateDescription) +{ + if(fiszero(ioDescription.mSampleRate)) + { + ioDescription.mSampleRate = inTemplateDescription.mSampleRate; + } + if(ioDescription.mFormatID == 0) + { + ioDescription.mFormatID = inTemplateDescription.mFormatID; + } + if(ioDescription.mFormatFlags == 0) + { + ioDescription.mFormatFlags = inTemplateDescription.mFormatFlags; + } + if(ioDescription.mBytesPerPacket == 0) + { + ioDescription.mBytesPerPacket = inTemplateDescription.mBytesPerPacket; + } + if(ioDescription.mFramesPerPacket == 0) + { + ioDescription.mFramesPerPacket = inTemplateDescription.mFramesPerPacket; + } + if(ioDescription.mBytesPerFrame == 0) + { + ioDescription.mBytesPerFrame = inTemplateDescription.mBytesPerFrame; + } + if(ioDescription.mChannelsPerFrame == 0) + { + ioDescription.mChannelsPerFrame = inTemplateDescription.mChannelsPerFrame; + } + if(ioDescription.mBitsPerChannel == 0) + { + ioDescription.mBitsPerChannel = inTemplateDescription.mBitsPerChannel; + } +} + +void CAStreamBasicDescription::GetSimpleName(const AudioStreamBasicDescription& inDescription, char* outName, bool inAbbreviate) +{ + switch(inDescription.mFormatID) + { + case kAudioFormatLinearPCM: + { + const char* theEndianString = NULL; + if((inDescription.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) + { + #if TARGET_RT_LITTLE_ENDIAN + theEndianString = "Big Endian"; + #endif + } + else + { + #if TARGET_RT_BIG_ENDIAN + theEndianString = "Little Endian"; + #endif + } + + const char* theKindString = NULL; + if((inDescription.mFormatFlags & kAudioFormatFlagIsFloat) != 0) + { + theKindString = (inAbbreviate ? "Float" : "Floating Point"); + } + else if((inDescription.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0) + { + theKindString = (inAbbreviate ? "SInt" : "Signed Integer"); + } + else + { + theKindString = (inAbbreviate ? "UInt" : "Unsigned Integer"); + } + + const char* thePackingString = NULL; + if((inDescription.mFormatFlags & kAudioFormatFlagIsPacked) == 0) + { + if((inDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) != 0) + { + thePackingString = "High"; + } + else + { + thePackingString = "Low"; + } + } + + const char* theMixabilityString = NULL; + if((inDescription.mFormatFlags & kIsNonMixableFlag) == 0) + { + theMixabilityString = "Mixable"; + } + else + { + theMixabilityString = "Unmixable"; + } + + if(inAbbreviate) + { + if(theEndianString != NULL) + { + if(thePackingString != NULL) + { + sprintf(outName, "%s %d Ch %s %s %s%d/%s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, theEndianString, thePackingString, theKindString, (int)inDescription.mBitsPerChannel, theKindString, (int)(inDescription.mBytesPerFrame / inDescription.mChannelsPerFrame) * 8); + } + else + { + sprintf(outName, "%s %d Ch %s %s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, theEndianString, theKindString, (int)inDescription.mBitsPerChannel); + } + } + else + { + if(thePackingString != NULL) + { + sprintf(outName, "%s %d Ch %s %s%d/%s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, thePackingString, theKindString, (int)inDescription.mBitsPerChannel, theKindString, (int)((inDescription.mBytesPerFrame / inDescription.mChannelsPerFrame) * 8)); + } + else + { + sprintf(outName, "%s %d Ch %s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, theKindString, (int)inDescription.mBitsPerChannel); + } + } + } + else + { + if(theEndianString != NULL) + { + if(thePackingString != NULL) + { + sprintf(outName, "%s %d Channel %d Bit %s %s Aligned %s in %d Bits", theMixabilityString, (int)inDescription.mChannelsPerFrame, (int)inDescription.mBitsPerChannel, theEndianString, theKindString, thePackingString, (int)(inDescription.mBytesPerFrame / inDescription.mChannelsPerFrame) * 8); + } + else + { + sprintf(outName, "%s %d Channel %d Bit %s %s", theMixabilityString, (int)inDescription.mChannelsPerFrame, (int)inDescription.mBitsPerChannel, theEndianString, theKindString); + } + } + else + { + if(thePackingString != NULL) + { + sprintf(outName, "%s %d Channel %d Bit %s Aligned %s in %d Bits", theMixabilityString, (int)inDescription.mChannelsPerFrame, (int)inDescription.mBitsPerChannel, theKindString, thePackingString, (int)(inDescription.mBytesPerFrame / inDescription.mChannelsPerFrame) * 8); + } + else + { + sprintf(outName, "%s %d Channel %d Bit %s", theMixabilityString, (int)inDescription.mChannelsPerFrame, (int)inDescription.mBitsPerChannel, theKindString); + } + } + } + } + break; + + case kAudioFormatAC3: + strcpy(outName, "AC-3"); + break; + + case kAudioFormat60958AC3: + strcpy(outName, "AC-3 for SPDIF"); + break; + + default: + { + char* the4CCString = (char*)&inDescription.mFormatID; + outName[0] = the4CCString[0]; + outName[1] = the4CCString[1]; + outName[2] = the4CCString[2]; + outName[3] = the4CCString[3]; + outName[4] = 0; + } + break; + }; +} + +#if CoreAudio_Debug +#include "CALogMacros.h" + +void CAStreamBasicDescription::PrintToLog(const AudioStreamBasicDescription& inDesc) +{ + PrintFloat (" Sample Rate: ", inDesc.mSampleRate); + Print4CharCode (" Format ID: ", inDesc.mFormatID); + PrintHex (" Format Flags: ", inDesc.mFormatFlags); + PrintInt (" Bytes per Packet: ", inDesc.mBytesPerPacket); + PrintInt (" Frames per Packet: ", inDesc.mFramesPerPacket); + PrintInt (" Bytes per Frame: ", inDesc.mBytesPerFrame); + PrintInt (" Channels per Frame: ", inDesc.mChannelsPerFrame); + PrintInt (" Bits per Channel: ", inDesc.mBitsPerChannel); +} +#endif + +bool operator<(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) +{ + bool theAnswer = false; + bool isDone = false; + + // note that if either side is 0, that field is skipped + + // format ID is the first order sort + if((!isDone) && ((x.mFormatID != 0) && (y.mFormatID != 0))) + { + if(x.mFormatID != y.mFormatID) + { + // formats are sorted numerically except that linear + // PCM is always first + if(x.mFormatID == kAudioFormatLinearPCM) + { + theAnswer = true; + } + else if(y.mFormatID == kAudioFormatLinearPCM) + { + theAnswer = false; + } + else + { + theAnswer = x.mFormatID < y.mFormatID; + } + isDone = true; + } + } + + + // mixable is always better than non-mixable for linear PCM and should be the second order sort item + if((!isDone) && ((x.mFormatID == kAudioFormatLinearPCM) && (y.mFormatID == kAudioFormatLinearPCM))) + { + if(((x.mFormatFlags & kIsNonMixableFlag) == 0) && ((y.mFormatFlags & kIsNonMixableFlag) != 0)) + { + theAnswer = true; + isDone = true; + } + else if(((x.mFormatFlags & kIsNonMixableFlag) != 0) && ((y.mFormatFlags & kIsNonMixableFlag) == 0)) + { + theAnswer = false; + isDone = true; + } + } + + // floating point vs integer for linear PCM only + if((!isDone) && ((x.mFormatID == kAudioFormatLinearPCM) && (y.mFormatID == kAudioFormatLinearPCM))) + { + if((x.mFormatFlags & kAudioFormatFlagIsFloat) != (y.mFormatFlags & kAudioFormatFlagIsFloat)) + { + // floating point is better than integer + theAnswer = y.mFormatFlags & kAudioFormatFlagIsFloat; + isDone = true; + } + } + + // bit depth + if((!isDone) && ((x.mBitsPerChannel != 0) && (y.mBitsPerChannel != 0))) + { + if(x.mBitsPerChannel != y.mBitsPerChannel) + { + // deeper bit depths are higher quality + theAnswer = x.mBitsPerChannel < y.mBitsPerChannel; + isDone = true; + } + } + + // sample rate + if((!isDone) && fnonzero(x.mSampleRate) && fnonzero(y.mSampleRate)) + { + if(fnotequal(x.mSampleRate, y.mSampleRate)) + { + // higher sample rates are higher quality + theAnswer = x.mSampleRate < y.mSampleRate; + isDone = true; + } + } + + // number of channels + if((!isDone) && ((x.mChannelsPerFrame != 0) && (y.mChannelsPerFrame != 0))) + { + if(x.mChannelsPerFrame != y.mChannelsPerFrame) + { + // more channels is higher quality + theAnswer = x.mChannelsPerFrame < y.mChannelsPerFrame; + isDone = true; + } + } + + return theAnswer; +} + +static bool MatchFormatFlags(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) +{ + UInt32 xFlags = x.mFormatFlags; + UInt32 yFlags = y.mFormatFlags; + + // match wildcards + if (x.mFormatID == 0 || y.mFormatID == 0 || xFlags == 0 || yFlags == 0) + return true; + + if (x.mFormatID == kAudioFormatLinearPCM) + { + // knock off the all clear flag + xFlags = xFlags & ~kAudioFormatFlagsAreAllClear; + yFlags = yFlags & ~kAudioFormatFlagsAreAllClear; + + // if both kAudioFormatFlagIsPacked bits are set, then we don't care about the kAudioFormatFlagIsAlignedHigh bit. + if (xFlags & yFlags & kAudioFormatFlagIsPacked) { + xFlags = xFlags & ~kAudioFormatFlagIsAlignedHigh; + yFlags = yFlags & ~kAudioFormatFlagIsAlignedHigh; + } + + // if both kAudioFormatFlagIsFloat bits are set, then we don't care about the kAudioFormatFlagIsSignedInteger bit. + if (xFlags & yFlags & kAudioFormatFlagIsFloat) { + xFlags = xFlags & ~kAudioFormatFlagIsSignedInteger; + yFlags = yFlags & ~kAudioFormatFlagIsSignedInteger; + } + + // if the bit depth is 8 bits or less and the format is packed, we don't care about endianness + if((x.mBitsPerChannel <= 8) && ((xFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked)) + { + xFlags = xFlags & ~kAudioFormatFlagIsBigEndian; + } + if((y.mBitsPerChannel <= 8) && ((yFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked)) + { + yFlags = yFlags & ~kAudioFormatFlagIsBigEndian; + } + + // if the number of channels is 0 or 1, we don't care about non-interleavedness + if (x.mChannelsPerFrame <= 1 && y.mChannelsPerFrame <= 1) { + xFlags &= ~kLinearPCMFormatFlagIsNonInterleaved; + yFlags &= ~kLinearPCMFormatFlagIsNonInterleaved; + } + } + return xFlags == yFlags; +} + +bool operator==(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) +{ + // the semantics for equality are: + // 1) Values must match exactly + // 2) wildcard's are ignored in the comparison + +#define MATCH(name) ((x.name) == 0 || (y.name) == 0 || (x.name) == (y.name)) + + return + // check the sample rate + (fiszero(x.mSampleRate) || fiszero(y.mSampleRate) || fequal(x.mSampleRate, y.mSampleRate)) + + // check the format ids + && MATCH(mFormatID) + + // check the format flags + && MatchFormatFlags(x, y) + + // check the bytes per packet + && MATCH(mBytesPerPacket) + + // check the frames per packet + && MATCH(mFramesPerPacket) + + // check the bytes per frame + && MATCH(mBytesPerFrame) + + // check the channels per frame + && MATCH(mChannelsPerFrame) + + // check the channels per frame + && MATCH(mBitsPerChannel) ; +} + +bool CAStreamBasicDescription::IsEqual(const AudioStreamBasicDescription &other, bool interpretingWildcards) const +{ + if (interpretingWildcards) + return *this == other; + return memcmp(this, &other, offsetof(AudioStreamBasicDescription, mReserved)) == 0; +} + +bool SanityCheck(const AudioStreamBasicDescription& x) +{ + return (x.mSampleRate >= 0.); +} diff --git a/Sources/CoreAudioSDK/CAStreamBasicDescription.h b/Sources/CoreAudioSDK/CAStreamBasicDescription.h new file mode 100644 index 0000000..cc927dd --- /dev/null +++ b/Sources/CoreAudioSDK/CAStreamBasicDescription.h @@ -0,0 +1,226 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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. +*/ +/*============================================================================= + CAStreamBasicDescription.h + +=============================================================================*/ + +#ifndef __CAStreamBasicDescription_h__ +#define __CAStreamBasicDescription_h__ + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include + #include +#else + #include "CoreAudioTypes.h" + #include "CoreFoundation.h" +#endif + +#include "CADebugMacros.h" +#include // for memset, memcpy +#include // for FILE * + +#pragma mark This file needs to compile on more earlier versions of the OS, so please keep that in mind when editing it + +// define the IsMixable format flag for all versions of the system +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3) + enum { kIsNonMixableFlag = kAudioFormatFlagIsNonMixable }; +#else + enum { kIsNonMixableFlag = (1L << 6) }; +#endif + +//============================================================================= +// CAStreamBasicDescription +// +// This is a wrapper class for the AudioStreamBasicDescription struct. +// It adds a number of convenience routines, but otherwise adds nothing +// to the footprint of the original struct. +//============================================================================= +class CAStreamBasicDescription : + public AudioStreamBasicDescription +{ + +// Constants +public: + static const AudioStreamBasicDescription sEmpty; + +// Construction/Destruction +public: + CAStreamBasicDescription() { memset (this, 0, sizeof(AudioStreamBasicDescription)); } + + CAStreamBasicDescription(const AudioStreamBasicDescription &desc) + { + SetFrom(desc); + } + + CAStreamBasicDescription( double inSampleRate, UInt32 inFormatID, + UInt32 inBytesPerPacket, UInt32 inFramesPerPacket, + UInt32 inBytesPerFrame, UInt32 inChannelsPerFrame, + UInt32 inBitsPerChannel, UInt32 inFormatFlags); + +// Assignment + CAStreamBasicDescription& operator=(const AudioStreamBasicDescription& v) { SetFrom(v); return *this; } + + void SetFrom(const AudioStreamBasicDescription &desc) + { + memcpy(this, &desc, sizeof(AudioStreamBasicDescription)); + } + + // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + // + // interrogation + + bool IsPCM() const { return mFormatID == kAudioFormatLinearPCM; } + + bool PackednessIsSignificant() const + { + Assert(IsPCM(), "PackednessIsSignificant only applies for PCM"); + return (SampleWordSize() << 3) != mBitsPerChannel; + } + + bool AlignmentIsSignificant() const + { + return PackednessIsSignificant() || (mBitsPerChannel & 7) != 0; + } + + bool IsInterleaved() const + { + return !IsPCM() || !(mFormatFlags & kAudioFormatFlagIsNonInterleaved); + } + + // for sanity with interleaved/deinterleaved possibilities, never access mChannelsPerFrame, use these: + UInt32 NumberInterleavedChannels() const { return IsInterleaved() ? mChannelsPerFrame : 1; } + UInt32 NumberChannelStreams() const { return IsInterleaved() ? 1 : mChannelsPerFrame; } + UInt32 NumberChannels() const { return mChannelsPerFrame; } + UInt32 SampleWordSize() const { return (mBytesPerFrame > 0) ? mBytesPerFrame / NumberInterleavedChannels() : 0;} + + UInt32 FramesToBytes(UInt32 nframes) const { return nframes * mBytesPerFrame; } + UInt32 BytesToFrames(UInt32 nbytes) const { + Assert(mBytesPerFrame > 0, "bytesPerFrame must be > 0 in BytesToFrames"); + return nbytes / mBytesPerFrame; + } + + bool SameChannelsAndInterleaving(const CAStreamBasicDescription &a) const + { + return this->NumberChannels() == a.NumberChannels() && this->IsInterleaved() == a.IsInterleaved(); + } + + // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + // + // manipulation + + void SetCanonical(UInt32 nChannels, bool interleaved) + // note: leaves sample rate untouched + { + mFormatID = kAudioFormatLinearPCM; + mFormatFlags = kAudioFormatFlagsNativeFloatPacked; + mBitsPerChannel = 32; + mChannelsPerFrame = nChannels; + mFramesPerPacket = 1; + if (interleaved) + mBytesPerPacket = mBytesPerFrame = nChannels * sizeof(Float32); + else { + mBytesPerPacket = mBytesPerFrame = sizeof(Float32); + mFormatFlags |= kAudioFormatFlagIsNonInterleaved; + } + } + + void ChangeNumberChannels(UInt32 nChannels, bool interleaved) + // alter an existing format + { + Assert(IsPCM(), "ChangeNumberChannels only works for PCM formats"); + UInt32 wordSize = SampleWordSize(); // get this before changing ANYTHING + if (wordSize == 0) + wordSize = (mBitsPerChannel + 7) / 8; + mChannelsPerFrame = nChannels; + mFramesPerPacket = 1; + if (interleaved) { + mBytesPerPacket = mBytesPerFrame = nChannels * wordSize; + mFormatFlags &= ~kAudioFormatFlagIsNonInterleaved; + } else { + mBytesPerPacket = mBytesPerFrame = wordSize; + mFormatFlags |= kAudioFormatFlagIsNonInterleaved; + } + } + + // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + // + // other + + bool IsEqual(const AudioStreamBasicDescription &other, bool interpretingWildcards=true) const; + + void Print() const + { + Print (stdout); + } + + void Print(FILE* file) const + { + PrintFormat (file, "", "AudioStreamBasicDescription:"); + } + + void PrintFormat(FILE *f, const char *indent, const char *name) const; + + OSStatus Save(CFPropertyListRef *outData) const; + + OSStatus Restore(CFPropertyListRef &inData); + +// Operations + static bool IsMixable(const AudioStreamBasicDescription& inDescription) { return (inDescription.mFormatID == kAudioFormatLinearPCM) && ((inDescription.mFormatFlags & kIsNonMixableFlag) == 0); } + static void NormalizeLinearPCMFormat(AudioStreamBasicDescription& ioDescription); + static void ResetFormat(AudioStreamBasicDescription& ioDescription); + static void FillOutFormat(AudioStreamBasicDescription& ioDescription, const AudioStreamBasicDescription& inTemplateDescription); + static void GetSimpleName(const AudioStreamBasicDescription& inDescription, char* outName, bool inAbbreviate); +#if CoreAudio_Debug + static void PrintToLog(const AudioStreamBasicDescription& inDesc); +#endif +}; + +bool operator<(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y); +bool operator==(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y); +#if TARGET_OS_MAC || (TARGET_OS_WIN32 && (_MSC_VER > 600)) +inline bool operator!=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !(x == y); } +inline bool operator<=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return (x < y) || (x == y); } +inline bool operator>=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !(x < y); } +inline bool operator>(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !((x < y) || (x == y)); } +#endif + +bool SanityCheck(const AudioStreamBasicDescription& x); + + +#endif // __CAStreamBasicDescription_h__ diff --git a/Sources/VLSoundOut.cpp b/Sources/VLSoundOut.cpp index a49fe9a..70f7d5b 100644 --- a/Sources/VLSoundOut.cpp +++ b/Sources/VLSoundOut.cpp @@ -19,21 +19,40 @@ class VLAUSoundOut : public VLSoundOut { public: VLAUSoundOut(); - virtual void PlayNote(const VLNote & note); - virtual void PlayChord(const VLChord & chord); - virtual void PlayFile(CFDataRef file); + virtual void PlayNote(const VLNote & note); + virtual void PlayChord(const VLChord & chord); + virtual void PlayFile(CFDataRef file); - virtual ~VLAUSoundOut(); + virtual ~VLAUSoundOut(); + + void Stop(); +protected: + VLAUSoundOut(bool fileOutput); + + void InitSoundOut(bool fileOutput); + virtual void SetupOutput(AUNode outputNode); + virtual void PlaySequence(MusicSequence music); + MusicTimeStamp SequenceLength(MusicSequence music); - void Stop(); -private: AUGraph fGraph; +private: MusicPlayer fPlayer; MusicSequence fMusic; bool fRunning; - void PlaySequence(MusicSequence music); - void Play(const int8_t * note, size_t numNotes = 1); + void Play(const int8_t * note, size_t numNotes = 1); +}; + +class VLAUFileSoundOut : public VLAUSoundOut { +public: + VLAUFileSoundOut(CFURLRef file); + ~VLAUFileSoundOut(); +protected: + virtual void SetupOutput(AUNode outputNode); + virtual void PlaySequence(MusicSequence music); +private: + AudioUnit fOutput; + CFURLRef fFile; }; VLSoundEvent::~VLSoundEvent() @@ -64,12 +83,35 @@ void VLSoundOut::SetScheduler(VLSoundScheduler * scheduler) sSoundScheduler.reset(scheduler); } +VLSoundOut * VLSoundOut::FileWriter(CFURLRef file) +{ + return new VLAUFileSoundOut(file); +} + VLSoundOut::~VLSoundOut() { } VLAUSoundOut::VLAUSoundOut() : fRunning(false), fMusic(0) +{ + InitSoundOutput(false); +} + +VLAUSoundOut::VLAUSoundOut(bool fileOutput) + : fRunning(false), fMusic(0) +{ + InitSoundOutput(fileOutput); +} + +VLAUSoundOut::~VLAUSoundOut() +{ + DisposeMusicPlayer(fPlayer); + Stop(); + DisposeAUGraph(fGraph); +} + +void VLAUSoundOut::InitSoundOutput(bool fileOutput) { AUNode synthNode, limiterNode, outNode; ComponentDescription cd; @@ -91,24 +133,35 @@ VLAUSoundOut::VLAUSoundOut() AUGraphNewNode (fGraph, &cd, 0, NULL, &limiterNode); cd.componentType = kAudioUnitType_Output; - cd.componentSubType = kAudioUnitSubType_DefaultOutput; + if (fileOutput) + cd.componentSubType = kAudioUnitSubType_GenericOutput; + else + cd.componentSubType = kAudioUnitSubType_DefaultOutput; AUGraphNewNode(fGraph, &cd, 0, NULL, &outNode); - + AUGraphOpen(fGraph); AUGraphConnectNodeInput(fGraph, synthNode, 0, limiterNode, 0); AUGraphConnectNodeInput(fGraph, limiterNode, 0, outNode, 0); + if (fileOutput) { + UInt32 value = 1; + AudioUnit synth; + AUGraphGetNodeInfo(fGraph, synthNode, 0, 0, 0, &synth) + AudioUnitSetProperty(synth, + kAudioUnitProperty_OfflineRender, + kAudioUnitScope_Global, 0, + &value, sizeof(value)); + } + SetupOutput(outNode); + AUGraphInitialize(fGraph); NewMusicPlayer(&fPlayer); } -VLAUSoundOut::~VLAUSoundOut() +void VLAUSoundOut::SetupOutput(AUNode) { - DisposeMusicPlayer(fPlayer); - Stop(); - DisposeAUGraph(fGraph); } void VLAUSoundOut::PlaySequence(MusicSequence music) @@ -183,3 +236,136 @@ void VLAUSoundOut::PlayFile(CFDataRef file) PlaySequence(music); } +MusicTimeStamp VLAUSoundOut::SequenceLength(MusicSequence music) +{ + UInt32 ntracks; + MusicSequenceGetTrackCount(sequence, &ntracks); + MusicTimeStamp sequenceLength = 0; + for (UInt32 i = 0; i < ntracks; ++i) { + MusicTrack track; + MusicTimeStamp trackLength; + UInt32 propsize = sizeof(MusicTimeStamp); + MusicSequenceGetIndTrack(sequence, i, &track); + MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, + &trackLength, &propsize); + sequenceLength = std::max(sequenceLength, trackLength); + } + return sequenceLength; +} + +VLAUFileSoundOut::VLAUFileSoundOut(CFURLRef file) + : VLAUSoundOut(true), fFile(file) +{ + CFRetain(fFile); +} + +VLAUFileSoundOut::~VLAUFileSoundOut() +{ + CFRelease(fFile); +} + +void VLAUFileSoundOut::SetupOutput(AUNode outputNode) +{ + AUGraphGetNodeInfo(fGraph, outputNode, 0, 0, 0, &fOutput); + Float64 sampleRate = 22050.0; + AudioUnitSetProperty(fOutput, + kAudioUnitProperty_SampleRate, + kAudioUnitScope_Output, 0, + &sampleRate, sizeof(sampleRate)); +} + +void VLAUFileSoundOut::PlaySequence(MusicSequence music) +{ + SInt32 urlErr; + CFURLDestroyResource(fFile, &urlErr); + + OSStatus result = 0; + UInt32 size; + MusicTimeStamp musicLen = SequenceLength(music)+8; + CFStringRef name = + CFURLCopyLastPathComponent(fFile); + CAAudioFileFormats formats = CAAudioFileFormats::Instance(); + + AudioFileTypeID fileType; + formats->InferFileFormatFromFilename(name, fileType); + + CAStreamBasicDescription outputFormat; + formats->InferDataFormatFromFileFormat(fileType, outputFormat); + outputFormat.mChannelsPerFrame = 2; + + if (outputFormat.mFormatID == kAudioFormatLinearPCM) { + outputFormat.mBytesPerPacket = outputFormat.mChannelsPerFrame * 2; + outputFormat.mFramesPerPacket = 1; + outputFormat.mBytesPerFrame = outputFormat.mBytesPerPacket; + outputFormat.mBitsPerChannel = 16; + + if (fileType == kAudioFileWAVEType) + outputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger + | kLinearPCMFormatFlagIsPacked; + else + outputFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian + | kLinearPCMFormatFlagIsSignedInteger + | kLinearPCMFormatFlagIsPacked; + } else { + // use AudioFormat API to fill out the rest. + size = sizeof(outputFormat); + AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, + &size, &outputFormat); + } + + CFURLRef dir = + CFURLCreateCopyDeletingLastPathComponent(NULL, fFile); + FSRef parentDir; + CFURLGetFSRef(dir, &parentDir); + CFRelease(dir); + + ExtAudioFileRef outfile; + ExtAudioFileCreateNew(&parentDir, name, + fileType, &outputFormat, NULL, &outfile); + CFRelease(name); + + { + CAStreamBasicDescription clientFormat; + size = sizeof(clientFormat); + require_noerr (result = AudioUnitGetProperty (outputUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, 0, + &clientFormat, &size), fail); + size = sizeof(clientFormat); + require_noerr (result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat), fail); + + { + MusicTimeStamp currentTime; + AUOutputBL outputBuffer (clientFormat, numFrames); + AudioTimeStamp tStamp; + memset (&tStamp, 0, sizeof(AudioTimeStamp)); + tStamp.mFlags = kAudioTimeStampSampleTimeValid; + int i = 0; + int numTimesFor10Secs = (int)(10. / (numFrames / srate)); + do { + outputBuffer.Prepare(); + AudioUnitRenderActionFlags actionFlags = 0; + require_noerr (result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL()), fail); + + tStamp.mSampleTime += numFrames; + + require_noerr (result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()), fail); + + require_noerr (result = MusicPlayerGetTime (player, ¤tTime), fail); + if (shouldPrint && (++i % numTimesFor10Secs == 0)) + printf ("current time: %6.2f beats\n", currentTime); + } while (currentTime < sequenceLength); + } + } + +// close + ExtAudioFileDispose(outfile); + + return; + +fail: + printf ("Problem: %ld\n", result); + exit(1); +} + + diff --git a/Sources/VLSoundOut.h b/Sources/VLSoundOut.h index 5b191a4..6299804 100644 --- a/Sources/VLSoundOut.h +++ b/Sources/VLSoundOut.h @@ -29,6 +29,8 @@ public: class VLSoundOut { public: static VLSoundOut * Instance(); + static VLSoundOut * FileWriter(CFURLRef file); + static void SetScheduler(VLSoundScheduler * scheduler); virtual void PlayNote(const VLNote & note) = 0; diff --git a/Vocalese.xcodeproj/project.pbxproj b/Vocalese.xcodeproj/project.pbxproj index da7fd95..3fc1211 100644 --- a/Vocalese.xcodeproj/project.pbxproj +++ b/Vocalese.xcodeproj/project.pbxproj @@ -265,6 +265,7 @@ isa = PBXGroup; children = ( 2A37F4ABFDCFA73011CA2CEA /* Classes */, + 9546A1090B08B47A0028503B /* CoreAudio SDK Sources */, 2A37F4AFFDCFA73011CA2CEA /* Other Sources */, 95C461D40B04403600649F92 /* Tools */, 95C461DC0B0442EB00649F92 /* MMA */, @@ -362,6 +363,13 @@ name = Frameworks; sourceTree = ""; }; + 9546A1090B08B47A0028503B /* CoreAudio SDK Sources */ = { + isa = PBXGroup; + children = ( + ); + name = "CoreAudio SDK Sources"; + sourceTree = ""; + }; 955E59560957C0C50045FDA5 /* Tests */ = { isa = PBXGroup; children = (