| /* 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 <CoreFoundation/CFByteOrder.h> |
| #else |
| #include <CFByteOrder.h> |
| #endif |
| |
| #pragma mark This file needs to compile on more 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; |
| } |
| |
| 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, "%2ld ch, %6.0f Hz, '%-4.4s' (0x%08lX) ", |
| NumberChannels(), mSampleRate, formatID, |
| 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, "%ld-bit%s%s %s%s%s%s%s\n", |
| 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, "%ld frames/packet\n", mFramesPerPacket); |
| } |
| else |
| fprintf(f, "%ld bits/channel, %ld bytes/packet, %ld frames/packet, %ld bytes/frame\n", |
| mBitsPerChannel, mBytesPerPacket, mFramesPerPacket, 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 SanityCheck(const AudioStreamBasicDescription& x) |
| { |
| return (x.mSampleRate >= 0.); |
| } |