/*============================================================================= CAStreamBasicDescription.cpp $Log: CAStreamBasicDescription.cpp,v $ Revision 1.32 2005/01/12 21:55:59 luke gcc 4.0 warning fixes Revision 1.31 2004/11/16 23:27:05 asynth alac wasn't printing a newline if flags were zero. also print out frames per packet. Revision 1.30 2004/11/11 19:51:08 asynth add Brad's support for lossless Revision 1.29 2004/09/17 22:17:59 dwyatt [3806361] ignore non-interleaved bit when comparing formats with 0 or 1 channels Revision 1.28 2004/08/19 19:08:24 jcm10 make it build and work on a stock Panther system Revision 1.27 2004/06/25 01:14:20 jcm10 fix up NormalizeLinearPCMFormat to support the IsNonMixable flag Revision 1.26 2004/06/07 18:55:11 jcm10 non-mixable should always sort after mixable for LPCM Revision 1.25 2004/05/28 23:01:34 jcm10 make it compile without kAudioFormatFlagIsNonMixable Revision 1.24 2004/05/27 19:48:45 jcm10 support kAudioFormatFlagIsNonMixable Revision 1.23 2004/05/13 22:49:22 dwyatt tweak debug print methods for case of 0 channels in PCM Revision 1.22 2004/04/09 22:44:45 dwyatt the great floating-point-equality-test inquisition Revision 1.21 2003/11/20 22:56:53 dwyatt __COREAUDIO_USE_FLAT_INCLUDES__ Revision 1.20 2003/09/17 23:40:53 asynth bare minimum sanity check. Revision 1.19 2003/09/17 23:05:01 asynth bare minimum sanity check. Revision 1.18 2003/09/17 02:08:11 asynth 3369425 don't limit sample rate Revision 1.17 2003/07/22 00:11:34 ealdrich Make Doug's changes build on Windows. Revision 1.16 2003/07/17 23:54:15 dwyatt need CoreServices.h for Endian.h Revision 1.15 2003/07/17 23:49:30 dwyatt endian vs. formatID display Revision 1.14 2003/07/09 23:38:40 jcm10 ignore the endian flag for comparisons with 8 bit linear PCM data Revision 1.13 2003/07/08 21:41:36 dwyatt cleanup PrintFormat more Revision 1.12 2003/05/16 19:26:42 dwyatt cleanup PrintFormat a little Revision 1.11 2003/04/25 18:40:31 asynth add ASBD SanityCheck Revision 1.10 2003/04/24 21:49:02 jcm10 move CAShowStreamDescription to CAShow.cpp Revision 1.9 2003/04/24 02:31:57 asynth more don't care matching Revision 1.8 2003/04/24 02:10:02 asynth do don't care matching of alignment bit. Revision 1.7 2003/04/23 22:50:31 dwyatt oops, forgot one of the constructors Revision 1.6 2003/04/23 22:33:13 dwyatt more fallout of class merge Revision 1.5 2003/04/23 22:09:04 dwyatt CAAudioStreamBasicDescription merged into CAStreamBasicDescription Revision 1.4 2003/03/17 23:40:34 dwyatt improve display of packed formats Revision 1.3 2003/03/12 04:48:17 bills Remove persistence calls to CAPersistence Revision 1.2 2003/03/11 20:57:30 bills first pass at save/restore state Revision 1.1 2003/03/11 00:30:43 bills add .cpp implementation of print calls Created by William Stewart on Sat Mar 08 2003. Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved $NoKeywords: $ =============================================================================*/ #include "CAConditionalMacros.h" #include "CAStreamBasicDescription.h" #include "CAMath.h" #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) #include #else #include #endif 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 = EndianU32_NtoB(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 == kAudioFormatAppleLossless) { int sourceBits = 0; switch (mFormatFlags) { case kAppleLosslessFormatFlag_16BitSourceData: sourceBits = 16; break; case kAppleLosslessFormatFlag_20BitSourceData: sourceBits = 20; break; case kAppleLosslessFormatFlag_24BitSourceData: sourceBits = 24; break; case 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.); }