1 /* 2 Copyright (C) 2016 Apple Inc. All Rights Reserved. 3 See LICENSE.txt for this sample’s licensing information 4 5 Abstract: 6 Part of Core Audio Public Utility Classes 7 */ 8 9 #ifndef __CAStreamBasicDescription_h__ 10 #define __CAStreamBasicDescription_h__ 11 12 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) 13 #include <CoreAudio/CoreAudioTypes.h> 14 #include <CoreFoundation/CoreFoundation.h> 15 #else 16 #include "CoreAudioTypes.h" 17 #include "CoreFoundation.h" 18 #endif 19 20 #include "CADebugMacros.h" 21 #include <string.h> // for memset, memcpy 22 #include <stdio.h> // for FILE * 23 24 #pragma mark This file needs to compile on more earlier versions of the OS, so please keep that in mind when editing it 25 26 #ifndef ASBD_STRICT_EQUALITY 27 #define ASBD_STRICT_EQUALITY 0 28 #endif 29 30 #if __GNUC__ && ASBD_STRICT_EQUALITY 31 // not turning on the deprecation just yet 32 #define ASBD_EQUALITY_DEPRECATED __attribute__((deprecated("This method uses a possibly surprising wildcard comparison (i.e. 0 channels == 1 channel)"))) 33 #else 34 #define ASBD_EQUALITY_DEPRECATED 35 #endif 36 37 #ifndef CA_CANONICAL_DEPRECATED 38 #define CA_CANONICAL_DEPRECATED 39 #endif 40 41 extern char *CAStringForOSType (OSType t, char *writeLocation, size_t bufsize); 42 43 // define Leopard specific symbols for backward compatibility if applicable 44 #if COREAUDIOTYPES_VERSION < 1050 45 typedef Float32 AudioSampleType; 46 enum { kAudioFormatFlagsCanonical = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked }; 47 #endif 48 #if COREAUDIOTYPES_VERSION < 1051 49 typedef Float32 AudioUnitSampleType; 50 enum { 51 kLinearPCMFormatFlagsSampleFractionShift = 7, 52 kLinearPCMFormatFlagsSampleFractionMask = (0x3F << kLinearPCMFormatFlagsSampleFractionShift), 53 }; 54 #endif 55 56 // define the IsMixable format flag for all versions of the system 57 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3) 58 enum { kIsNonMixableFlag = kAudioFormatFlagIsNonMixable }; 59 #else 60 enum { kIsNonMixableFlag = (1L << 6) }; 61 #endif 62 63 //============================================================================= 64 // CAStreamBasicDescription 65 // 66 // This is a wrapper class for the AudioStreamBasicDescription struct. 67 // It adds a number of convenience routines, but otherwise adds nothing 68 // to the footprint of the original struct. 69 //============================================================================= 70 class CAStreamBasicDescription : 71 public AudioStreamBasicDescription 72 { 73 74 // Constants 75 public: 76 static const AudioStreamBasicDescription sEmpty; 77 78 enum CommonPCMFormat { 79 kPCMFormatOther = 0, 80 kPCMFormatFloat32 = 1, 81 kPCMFormatInt16 = 2, 82 kPCMFormatFixed824 = 3, 83 kPCMFormatFloat64 = 4, 84 kPCMFormatInt32 = 5 85 }; 86 87 // options for IsEquivalent 88 enum { 89 kCompareDefault = 0, 90 kCompareUsingWildcards = 1 << 0, // treats fields with values of 0 as wildcards. 91 // too liberal if you need to represent 0 channels. 92 kCompareForHardware = 1 << 1, // formats are hardware formats (IsNonMixable flag is significant). 93 94 kCompareForHardwareUsingWildcards = kCompareForHardware + kCompareUsingWildcards // for convenience 95 }; 96 typedef UInt32 ComparisonOptions; 97 98 // Construction/Destruction 99 public: 100 CAStreamBasicDescription(); 101 102 CAStreamBasicDescription(const AudioStreamBasicDescription &desc); 103 104 CAStreamBasicDescription( double inSampleRate, UInt32 inFormatID, 105 UInt32 inBytesPerPacket, UInt32 inFramesPerPacket, 106 UInt32 inBytesPerFrame, UInt32 inChannelsPerFrame, 107 UInt32 inBitsPerChannel, UInt32 inFormatFlags); 108 CAStreamBasicDescription(double inSampleRate,UInt32 inNumChannels,CommonPCMFormat pcmf,bool inIsInterleaved)109 CAStreamBasicDescription( double inSampleRate, UInt32 inNumChannels, CommonPCMFormat pcmf, bool inIsInterleaved) { 110 unsigned wordsize; 111 112 mSampleRate = inSampleRate; 113 mFormatID = kAudioFormatLinearPCM; 114 mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; 115 mFramesPerPacket = 1; 116 mChannelsPerFrame = inNumChannels; 117 mBytesPerFrame = mBytesPerPacket = 0; 118 mReserved = 0; 119 120 switch (pcmf) { 121 default: 122 return; 123 case kPCMFormatFloat32: 124 wordsize = 4; 125 mFormatFlags |= kAudioFormatFlagIsFloat; 126 break; 127 case kPCMFormatFloat64: 128 wordsize = 8; 129 mFormatFlags |= kAudioFormatFlagIsFloat; 130 break; 131 case kPCMFormatInt16: 132 wordsize = 2; 133 mFormatFlags |= kAudioFormatFlagIsSignedInteger; 134 break; 135 case kPCMFormatInt32: 136 wordsize = 4; 137 mFormatFlags |= kAudioFormatFlagIsSignedInteger; 138 break; 139 case kPCMFormatFixed824: 140 wordsize = 4; 141 mFormatFlags |= kAudioFormatFlagIsSignedInteger | (24 << kLinearPCMFormatFlagsSampleFractionShift); 142 break; 143 } 144 mBitsPerChannel = wordsize * 8; 145 if (inIsInterleaved) 146 mBytesPerFrame = mBytesPerPacket = wordsize * inNumChannels; 147 else { 148 mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 149 mBytesPerFrame = mBytesPerPacket = wordsize; 150 } 151 } 152 153 // Assignment 154 CAStreamBasicDescription& operator=(const AudioStreamBasicDescription& v) { SetFrom(v); return *this; } 155 SetFrom(const AudioStreamBasicDescription & desc)156 void SetFrom(const AudioStreamBasicDescription &desc) 157 { 158 memcpy(this, &desc, sizeof(AudioStreamBasicDescription)); 159 } 160 FromText(const char * inTextDesc)161 bool FromText(const char *inTextDesc) { return FromText(inTextDesc, *this); } 162 static bool FromText(const char *inTextDesc, AudioStreamBasicDescription &outDesc); 163 // return true if parsing was successful 164 165 static const char *sTextParsingUsageString; 166 167 // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 168 // 169 // interrogation 170 IsPCM()171 bool IsPCM() const { return mFormatID == kAudioFormatLinearPCM; } 172 PackednessIsSignificant()173 bool PackednessIsSignificant() const 174 { 175 Assert(IsPCM(), "PackednessIsSignificant only applies for PCM"); 176 return (SampleWordSize() << 3) != mBitsPerChannel; 177 } 178 AlignmentIsSignificant()179 bool AlignmentIsSignificant() const 180 { 181 return PackednessIsSignificant() || (mBitsPerChannel & 7) != 0; 182 } 183 IsInterleaved()184 bool IsInterleaved() const 185 { 186 return !(mFormatFlags & kAudioFormatFlagIsNonInterleaved); 187 } 188 IsSignedInteger()189 bool IsSignedInteger() const 190 { 191 return IsPCM() && (mFormatFlags & kAudioFormatFlagIsSignedInteger); 192 } 193 IsFloat()194 bool IsFloat() const 195 { 196 return IsPCM() && (mFormatFlags & kAudioFormatFlagIsFloat); 197 } 198 IsNativeEndian()199 bool IsNativeEndian() const 200 { 201 return (mFormatFlags & kAudioFormatFlagIsBigEndian) == kAudioFormatFlagsNativeEndian; 202 } 203 204 // for sanity with interleaved/deinterleaved possibilities, never access mChannelsPerFrame, use these: NumberInterleavedChannels()205 UInt32 NumberInterleavedChannels() const { return IsInterleaved() ? mChannelsPerFrame : 1; } NumberChannelStreams()206 UInt32 NumberChannelStreams() const { return IsInterleaved() ? 1 : mChannelsPerFrame; } NumberChannels()207 UInt32 NumberChannels() const { return mChannelsPerFrame; } SampleWordSize()208 UInt32 SampleWordSize() const { 209 return (mBytesPerFrame > 0 && NumberInterleavedChannels()) ? mBytesPerFrame / NumberInterleavedChannels() : 0; 210 } 211 FramesToBytes(UInt32 nframes)212 UInt32 FramesToBytes(UInt32 nframes) const { return nframes * mBytesPerFrame; } BytesToFrames(UInt32 nbytes)213 UInt32 BytesToFrames(UInt32 nbytes) const { 214 Assert(mBytesPerFrame > 0, "bytesPerFrame must be > 0 in BytesToFrames"); 215 return nbytes / mBytesPerFrame; 216 } 217 SameChannelsAndInterleaving(const CAStreamBasicDescription & a)218 bool SameChannelsAndInterleaving(const CAStreamBasicDescription &a) const 219 { 220 return this->NumberChannels() == a.NumberChannels() && this->IsInterleaved() == a.IsInterleaved(); 221 } 222 223 bool IdentifyCommonPCMFormat(CommonPCMFormat &outFormat, bool *outIsInterleaved=NULL) const 224 { // return true if it's a valid PCM format. 225 226 outFormat = kPCMFormatOther; 227 // trap out patently invalid formats. 228 if (mFormatID != kAudioFormatLinearPCM || mFramesPerPacket != 1 || mBytesPerFrame != mBytesPerPacket || mBitsPerChannel/8 > mBytesPerFrame || mChannelsPerFrame == 0) 229 return false; 230 bool interleaved = (mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; 231 if (outIsInterleaved != NULL) *outIsInterleaved = interleaved; 232 unsigned wordsize = mBytesPerFrame; 233 if (interleaved) { 234 if (wordsize % mChannelsPerFrame != 0) return false; 235 wordsize /= mChannelsPerFrame; 236 } 237 238 if ((mFormatFlags & kAudioFormatFlagIsBigEndian) == kAudioFormatFlagsNativeEndian 239 && wordsize * 8 == mBitsPerChannel) { 240 // packed and native endian, good 241 if (mFormatFlags & kLinearPCMFormatFlagIsFloat) { 242 // float: reject nonsense bits 243 if (mFormatFlags & (kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagsSampleFractionMask)) 244 return false; 245 if (wordsize == 4) 246 outFormat = kPCMFormatFloat32; 247 if (wordsize == 8) 248 outFormat = kPCMFormatFloat64; 249 } else if (mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) { 250 // signed int 251 unsigned fracbits = (mFormatFlags & kLinearPCMFormatFlagsSampleFractionMask) >> kLinearPCMFormatFlagsSampleFractionShift; 252 if (wordsize == 4 && fracbits == 24) 253 outFormat = kPCMFormatFixed824; 254 else if (wordsize == 4 && fracbits == 0) 255 outFormat = kPCMFormatInt32; 256 else if (wordsize == 2 && fracbits == 0) 257 outFormat = kPCMFormatInt16; 258 } 259 } 260 return true; 261 } 262 263 bool IsCommonFloat32(bool *outIsInterleaved=NULL) const { 264 CommonPCMFormat fmt; 265 return IdentifyCommonPCMFormat(fmt, outIsInterleaved) && fmt == kPCMFormatFloat32; 266 } 267 bool IsCommonFloat64(bool *outIsInterleaved=NULL) const { 268 CommonPCMFormat fmt; 269 return IdentifyCommonPCMFormat(fmt, outIsInterleaved) && fmt == kPCMFormatFloat64; 270 } 271 bool IsCommonFixed824(bool *outIsInterleaved=NULL) const { 272 CommonPCMFormat fmt; 273 return IdentifyCommonPCMFormat(fmt, outIsInterleaved) && fmt == kPCMFormatFixed824; 274 } 275 bool IsCommonInt16(bool *outIsInterleaved=NULL) const { 276 CommonPCMFormat fmt; 277 return IdentifyCommonPCMFormat(fmt, outIsInterleaved) && fmt == kPCMFormatInt16; 278 } 279 280 // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 281 // 282 // manipulation 283 284 CA_CANONICAL_DEPRECATED SetCanonical(UInt32 nChannels,bool interleaved)285 void SetCanonical(UInt32 nChannels, bool interleaved) 286 // note: leaves sample rate untouched 287 { 288 mFormatID = kAudioFormatLinearPCM; 289 UInt32 sampleSize = SizeOf32(AudioSampleType); 290 mFormatFlags = kAudioFormatFlagsCanonical; 291 mBitsPerChannel = 8 * sampleSize; 292 mChannelsPerFrame = nChannels; 293 mFramesPerPacket = 1; 294 if (interleaved) 295 mBytesPerPacket = mBytesPerFrame = nChannels * sampleSize; 296 else { 297 mBytesPerPacket = mBytesPerFrame = sampleSize; 298 mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 299 } 300 } 301 302 CA_CANONICAL_DEPRECATED IsCanonical()303 bool IsCanonical() const 304 { 305 if (mFormatID != kAudioFormatLinearPCM) return false; 306 UInt32 reqFormatFlags; 307 UInt32 flagsMask = (kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagsSampleFractionMask); 308 bool interleaved = (mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; 309 unsigned sampleSize = SizeOf32(AudioSampleType); 310 reqFormatFlags = kAudioFormatFlagsCanonical; 311 UInt32 reqFrameSize = interleaved ? (mChannelsPerFrame * sampleSize) : sampleSize; 312 313 return ((mFormatFlags & flagsMask) == reqFormatFlags 314 && mBitsPerChannel == 8 * sampleSize 315 && mFramesPerPacket == 1 316 && mBytesPerFrame == reqFrameSize 317 && mBytesPerPacket == reqFrameSize); 318 } 319 320 CA_CANONICAL_DEPRECATED SetAUCanonical(UInt32 nChannels,bool interleaved)321 void SetAUCanonical(UInt32 nChannels, bool interleaved) 322 { 323 mFormatID = kAudioFormatLinearPCM; 324 #if CA_PREFER_FIXED_POINT 325 mFormatFlags = kAudioFormatFlagsCanonical | (kAudioUnitSampleFractionBits << kLinearPCMFormatFlagsSampleFractionShift); 326 #else 327 mFormatFlags = kAudioFormatFlagsCanonical; 328 #endif 329 mChannelsPerFrame = nChannels; 330 mFramesPerPacket = 1; 331 mBitsPerChannel = 8 * SizeOf32(AudioUnitSampleType); 332 if (interleaved) 333 mBytesPerPacket = mBytesPerFrame = nChannels * SizeOf32(AudioUnitSampleType); 334 else { 335 mBytesPerPacket = mBytesPerFrame = SizeOf32(AudioUnitSampleType); 336 mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 337 } 338 } 339 ChangeNumberChannels(UInt32 nChannels,bool interleaved)340 void ChangeNumberChannels(UInt32 nChannels, bool interleaved) 341 // alter an existing format 342 { 343 Assert(IsPCM(), "ChangeNumberChannels only works for PCM formats"); 344 UInt32 wordSize = SampleWordSize(); // get this before changing ANYTHING 345 if (wordSize == 0) 346 wordSize = (mBitsPerChannel + 7) / 8; 347 mChannelsPerFrame = nChannels; 348 mFramesPerPacket = 1; 349 if (interleaved) { 350 mBytesPerPacket = mBytesPerFrame = nChannels * wordSize; 351 mFormatFlags &= ~static_cast<UInt32>(kAudioFormatFlagIsNonInterleaved); 352 } else { 353 mBytesPerPacket = mBytesPerFrame = wordSize; 354 mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 355 } 356 } 357 358 // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 359 // 360 // other 361 362 // IsEqual: Deprecated because of widespread errors due to the default wildcarding behavior. 363 ASBD_EQUALITY_DEPRECATED 364 bool IsEqual(const AudioStreamBasicDescription &other) const; 365 bool IsEqual(const AudioStreamBasicDescription &other, bool interpretingWildcards) const; 366 367 // IsExactlyEqual: bit-for-bit. usually unnecessarily strict. 368 static bool IsExactlyEqual(const AudioStreamBasicDescription &x, const AudioStreamBasicDescription &y); 369 370 // IsEquivalent: Returns whether the two formats are functionally the same, i.e. if one could 371 // be correctly passed as the other without an AudioConverter. IsEquivalent(const AudioStreamBasicDescription & x,const AudioStreamBasicDescription & y)372 static bool IsEquivalent(const AudioStreamBasicDescription &x, const AudioStreamBasicDescription &y) { return IsEquivalent(x, y, kCompareDefault); } 373 static bool IsEquivalent(const AudioStreamBasicDescription &x, const AudioStreamBasicDescription &y, ComparisonOptions comparisonOptions); 374 375 // Member versions of IsExactlyEqual and IsEquivalent. IsExactlyEqual(const AudioStreamBasicDescription & other)376 bool IsExactlyEqual(const AudioStreamBasicDescription &other) const { return IsExactlyEqual(*this, other); } IsEquivalent(const AudioStreamBasicDescription & other)377 bool IsEquivalent(const AudioStreamBasicDescription &other) const { return IsEquivalent(*this, other); } IsEquivalent(const AudioStreamBasicDescription & other,ComparisonOptions comparisonOptions)378 bool IsEquivalent(const AudioStreamBasicDescription &other, ComparisonOptions comparisonOptions) const { return IsEquivalent(*this, other, comparisonOptions); } 379 Print()380 void Print() const { 381 Print (stdout); 382 } 383 Print(FILE * file)384 void Print(FILE* file) const { 385 PrintFormat (file, "", "AudioStreamBasicDescription:"); 386 } 387 PrintFormat(FILE * f,const char * indent,const char * name)388 void PrintFormat(FILE *f, const char *indent, const char *name) const { 389 char buf[256]; 390 fprintf(f, "%s%s %s\n", indent, name, AsString(buf, sizeof(buf))); 391 } 392 PrintFormat2(FILE * f,const char * indent,const char * name)393 void PrintFormat2(FILE *f, const char *indent, const char *name) const { // no trailing newline 394 char buf[256]; 395 fprintf(f, "%s%s %s", indent, name, AsString(buf, sizeof(buf))); 396 } 397 398 char * AsString(char *buf, size_t bufsize, bool brief=false) const; 399 Print(const AudioStreamBasicDescription & inDesc)400 static void Print (const AudioStreamBasicDescription &inDesc) 401 { 402 CAStreamBasicDescription desc(inDesc); 403 desc.Print (); 404 } 405 406 OSStatus Save(CFPropertyListRef *outData) const; 407 408 OSStatus Restore(CFPropertyListRef &inData); 409 410 // Operations IsMixable(const AudioStreamBasicDescription & inDescription)411 static bool IsMixable(const AudioStreamBasicDescription& inDescription) { return (inDescription.mFormatID == kAudioFormatLinearPCM) && ((inDescription.mFormatFlags & kIsNonMixableFlag) == 0); } 412 CA_CANONICAL_DEPRECATED 413 static void NormalizeLinearPCMFormat(AudioStreamBasicDescription& ioDescription); 414 CA_CANONICAL_DEPRECATED 415 static void NormalizeLinearPCMFormat(bool inNativeEndian, AudioStreamBasicDescription& ioDescription); 416 static void VirtualizeLinearPCMFormat(AudioStreamBasicDescription& ioDescription); 417 static void VirtualizeLinearPCMFormat(bool inNativeEndian, AudioStreamBasicDescription& ioDescription); 418 static void ResetFormat(AudioStreamBasicDescription& ioDescription); 419 static void FillOutFormat(AudioStreamBasicDescription& ioDescription, const AudioStreamBasicDescription& inTemplateDescription); 420 static void GetSimpleName(const AudioStreamBasicDescription& inDescription, char* outName, UInt32 inMaxNameLength, bool inAbbreviate, bool inIncludeSampleRate = false); 421 422 #if CoreAudio_Debug 423 static void PrintToLog(const AudioStreamBasicDescription& inDesc); 424 #endif 425 426 UInt32 GetRegularizedFormatFlags(bool forHardware) const; 427 428 private: 429 static bool EquivalentFormatFlags(const AudioStreamBasicDescription &x, const AudioStreamBasicDescription &y, bool forHardware, bool usingWildcards); 430 }; 431 432 #define CAStreamBasicDescription_EmptyInit 0.0, 0, 0, 0, 0, 0, 0, 0, 0 433 #define CAStreamBasicDescription_Empty { CAStreamBasicDescription_EmptyInit } 434 435 // operator== is deprecated because it uses the deprecated IsEqual(other, true). 436 bool operator<(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y); 437 ASBD_EQUALITY_DEPRECATED bool operator==(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y); 438 #if TARGET_OS_MAC || (TARGET_OS_WIN32 && (_MSC_VER > 600)) 439 ASBD_EQUALITY_DEPRECATED inline bool operator!=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !(x == y); } 440 ASBD_EQUALITY_DEPRECATED inline bool operator<=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return (x < y) || (x == y); } 441 ASBD_EQUALITY_DEPRECATED inline bool operator>=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !(x < y); } 442 ASBD_EQUALITY_DEPRECATED inline bool operator>(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !((x < y) || (x == y)); } 443 #endif 444 445 bool SanityCheck(const AudioStreamBasicDescription& x); 446 447 448 #endif // __CAStreamBasicDescription_h__ 449