1 /* 2 File: CAStreamBasicDescription.h 3 Abstract: Part of CoreAudio Utility Classes 4 Version: 1.01 5 6 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 Inc. ("Apple") in consideration of your agreement to the following 8 terms, and your use, installation, modification or redistribution of 9 this Apple software constitutes acceptance of these terms. If you do 10 not agree with these terms, please do not use, install, modify or 11 redistribute this Apple software. 12 13 In consideration of your agreement to abide by the following terms, and 14 subject to these terms, Apple grants you a personal, non-exclusive 15 license, under Apple's copyrights in this original Apple software (the 16 "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 Software, with or without modifications, in source and/or binary forms; 18 provided that if you redistribute the Apple Software in its entirety and 19 without modifications, you must retain this notice and the following 20 text and disclaimers in all such redistributions of the Apple Software. 21 Neither the name, trademarks, service marks or logos of Apple Inc. may 22 be used to endorse or promote products derived from the Apple Software 23 without specific prior written permission from Apple. Except as 24 expressly stated in this notice, no other rights or licenses, express or 25 implied, are granted by Apple herein, including but not limited to any 26 patent rights that may be infringed by your derivative works or by other 27 works in which the Apple Software may be incorporated. 28 29 The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 35 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 POSSIBILITY OF SUCH DAMAGE. 43 44 Copyright (C) 2012 Apple Inc. All Rights Reserved. 45 46 */ 47 #ifndef __CAStreamBasicDescription_h__ 48 #define __CAStreamBasicDescription_h__ 49 50 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) 51 #include <CoreAudio/CoreAudioTypes.h> 52 #include <CoreFoundation/CoreFoundation.h> 53 #else 54 #include "CoreAudioTypes.h" 55 #include "CoreFoundation.h" 56 #endif 57 58 #include "CADebugMacros.h" 59 #include <string.h> // for memset, memcpy 60 #include <stdio.h> // for FILE * 61 62 #pragma mark This file needs to compile on more earlier versions of the OS, so please keep that in mind when editing it 63 64 extern char *CAStringForOSType (OSType t, char *writeLocation); 65 66 // define Leopard specific symbols for backward compatibility if applicable 67 #if COREAUDIOTYPES_VERSION < 1050 68 typedef Float32 AudioSampleType; 69 enum { kAudioFormatFlagsCanonical = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked }; 70 #endif 71 #if COREAUDIOTYPES_VERSION < 1051 72 typedef Float32 AudioUnitSampleType; 73 enum { 74 kLinearPCMFormatFlagsSampleFractionShift = 7, 75 kLinearPCMFormatFlagsSampleFractionMask = (0x3F << kLinearPCMFormatFlagsSampleFractionShift), 76 }; 77 #endif 78 79 // define the IsMixable format flag for all versions of the system 80 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3) 81 enum { kIsNonMixableFlag = kAudioFormatFlagIsNonMixable }; 82 #else 83 enum { kIsNonMixableFlag = (1L << 6) }; 84 #endif 85 86 //============================================================================= 87 // CAStreamBasicDescription 88 // 89 // This is a wrapper class for the AudioStreamBasicDescription struct. 90 // It adds a number of convenience routines, but otherwise adds nothing 91 // to the footprint of the original struct. 92 //============================================================================= 93 class CAStreamBasicDescription : 94 public AudioStreamBasicDescription 95 { 96 97 // Constants 98 public: 99 static const AudioStreamBasicDescription sEmpty; 100 101 enum CommonPCMFormat { 102 kPCMFormatOther = 0, 103 kPCMFormatFloat32 = 1, 104 kPCMFormatInt16 = 2, 105 kPCMFormatFixed824 = 3 106 }; 107 108 // Construction/Destruction 109 public: 110 CAStreamBasicDescription(); 111 112 CAStreamBasicDescription(const AudioStreamBasicDescription &desc); 113 114 CAStreamBasicDescription( double inSampleRate, UInt32 inFormatID, 115 UInt32 inBytesPerPacket, UInt32 inFramesPerPacket, 116 UInt32 inBytesPerFrame, UInt32 inChannelsPerFrame, 117 UInt32 inBitsPerChannel, UInt32 inFormatFlags); 118 CAStreamBasicDescription(double inSampleRate,UInt32 inNumChannels,CommonPCMFormat pcmf,bool inIsInterleaved)119 CAStreamBasicDescription( double inSampleRate, UInt32 inNumChannels, CommonPCMFormat pcmf, bool inIsInterleaved) { 120 unsigned wordsize; 121 122 mSampleRate = inSampleRate; 123 mFormatID = kAudioFormatLinearPCM; 124 mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; 125 mFramesPerPacket = 1; 126 mChannelsPerFrame = inNumChannels; 127 mBytesPerFrame = mBytesPerPacket = 0; 128 mReserved = 0; 129 130 switch (pcmf) { 131 default: 132 return; 133 case kPCMFormatFloat32: 134 wordsize = 4; 135 mFormatFlags |= kAudioFormatFlagIsFloat; 136 break; 137 case kPCMFormatInt16: 138 wordsize = 2; 139 mFormatFlags |= kAudioFormatFlagIsSignedInteger; 140 break; 141 case kPCMFormatFixed824: 142 wordsize = 4; 143 mFormatFlags |= kAudioFormatFlagIsSignedInteger | (24 << kLinearPCMFormatFlagsSampleFractionShift); 144 break; 145 } 146 mBitsPerChannel = wordsize * 8; 147 if (inIsInterleaved) 148 mBytesPerFrame = mBytesPerPacket = wordsize * inNumChannels; 149 else { 150 mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 151 mBytesPerFrame = mBytesPerPacket = wordsize; 152 } 153 } 154 155 // Assignment 156 CAStreamBasicDescription& operator=(const AudioStreamBasicDescription& v) { SetFrom(v); return *this; } 157 SetFrom(const AudioStreamBasicDescription & desc)158 void SetFrom(const AudioStreamBasicDescription &desc) 159 { 160 memcpy(this, &desc, sizeof(AudioStreamBasicDescription)); 161 } 162 FromText(const char * inTextDesc)163 bool FromText(const char *inTextDesc) { return FromText(inTextDesc, *this); } 164 static bool FromText(const char *inTextDesc, AudioStreamBasicDescription &outDesc); 165 // return true if parsing was successful 166 167 static const char *sTextParsingUsageString; 168 169 // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 170 // 171 // interrogation 172 IsPCM()173 bool IsPCM() const { return mFormatID == kAudioFormatLinearPCM; } 174 PackednessIsSignificant()175 bool PackednessIsSignificant() const 176 { 177 Assert(IsPCM(), "PackednessIsSignificant only applies for PCM"); 178 return (SampleWordSize() << 3) != mBitsPerChannel; 179 } 180 AlignmentIsSignificant()181 bool AlignmentIsSignificant() const 182 { 183 return PackednessIsSignificant() || (mBitsPerChannel & 7) != 0; 184 } 185 IsInterleaved()186 bool IsInterleaved() const 187 { 188 return !IsPCM() || !(mFormatFlags & kAudioFormatFlagIsNonInterleaved); 189 } 190 IsSignedInteger()191 bool IsSignedInteger() const 192 { 193 return IsPCM() && (mFormatFlags & kAudioFormatFlagIsSignedInteger); 194 } 195 IsFloat()196 bool IsFloat() const 197 { 198 return IsPCM() && (mFormatFlags & kAudioFormatFlagIsFloat); 199 } 200 IsNativeEndian()201 bool IsNativeEndian() const 202 { 203 return (mFormatFlags & kAudioFormatFlagIsBigEndian) == kAudioFormatFlagsNativeEndian; 204 } 205 206 // for sanity with interleaved/deinterleaved possibilities, never access mChannelsPerFrame, use these: NumberInterleavedChannels()207 UInt32 NumberInterleavedChannels() const { return IsInterleaved() ? mChannelsPerFrame : 1; } NumberChannelStreams()208 UInt32 NumberChannelStreams() const { return IsInterleaved() ? 1 : mChannelsPerFrame; } NumberChannels()209 UInt32 NumberChannels() const { return mChannelsPerFrame; } SampleWordSize()210 UInt32 SampleWordSize() const { 211 return (mBytesPerFrame > 0 && NumberInterleavedChannels()) ? mBytesPerFrame / NumberInterleavedChannels() : 0; 212 } 213 FramesToBytes(UInt32 nframes)214 UInt32 FramesToBytes(UInt32 nframes) const { return nframes * mBytesPerFrame; } BytesToFrames(UInt32 nbytes)215 UInt32 BytesToFrames(UInt32 nbytes) const { 216 Assert(mBytesPerFrame > 0, "bytesPerFrame must be > 0 in BytesToFrames"); 217 return nbytes / mBytesPerFrame; 218 } 219 SameChannelsAndInterleaving(const CAStreamBasicDescription & a)220 bool SameChannelsAndInterleaving(const CAStreamBasicDescription &a) const 221 { 222 return this->NumberChannels() == a.NumberChannels() && this->IsInterleaved() == a.IsInterleaved(); 223 } 224 225 bool IdentifyCommonPCMFormat(CommonPCMFormat &outFormat, bool *outIsInterleaved=NULL) const 226 { // return true if it's a valid PCM format. 227 228 outFormat = kPCMFormatOther; 229 // trap out patently invalid formats. 230 if (mFormatID != kAudioFormatLinearPCM || mFramesPerPacket != 1 || mBytesPerFrame != mBytesPerPacket || mBitsPerChannel/8 > mBytesPerFrame || mChannelsPerFrame == 0) 231 return false; 232 bool interleaved = (mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; 233 if (outIsInterleaved != NULL) *outIsInterleaved = interleaved; 234 unsigned wordsize = mBytesPerFrame; 235 if (interleaved) { 236 if (wordsize % mChannelsPerFrame != 0) return false; 237 wordsize /= mChannelsPerFrame; 238 } 239 240 if ((mFormatFlags & kAudioFormatFlagIsBigEndian) == kAudioFormatFlagsNativeEndian 241 && wordsize * 8 == mBitsPerChannel) { 242 // packed and native endian, good 243 if (mFormatFlags & kLinearPCMFormatFlagIsFloat) { 244 // float: reject nonsense bits 245 if (mFormatFlags & (kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagsSampleFractionMask)) 246 return false; 247 if (wordsize == 4) 248 outFormat = kPCMFormatFloat32; 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 == 2 && fracbits == 0) 255 outFormat = kPCMFormatInt16; 256 } 257 } 258 return true; 259 } 260 261 bool IsCommonFloat32(bool *outIsInterleaved=NULL) const { 262 CommonPCMFormat fmt; 263 return IdentifyCommonPCMFormat(fmt, outIsInterleaved) && fmt == kPCMFormatFloat32; 264 } 265 bool IsCommonFixed824(bool *outIsInterleaved=NULL) const { 266 CommonPCMFormat fmt; 267 return IdentifyCommonPCMFormat(fmt, outIsInterleaved) && fmt == kPCMFormatFixed824; 268 } 269 bool IsCommonInt16(bool *outIsInterleaved=NULL) const { 270 CommonPCMFormat fmt; 271 return IdentifyCommonPCMFormat(fmt, outIsInterleaved) && fmt == kPCMFormatInt16; 272 } 273 274 // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 275 // 276 // manipulation 277 SetCanonical(UInt32 nChannels,bool interleaved)278 void SetCanonical(UInt32 nChannels, bool interleaved) 279 // note: leaves sample rate untouched 280 { 281 mFormatID = kAudioFormatLinearPCM; 282 int sampleSize = SizeOf32(AudioSampleType); 283 mFormatFlags = kAudioFormatFlagsCanonical; 284 mBitsPerChannel = 8 * sampleSize; 285 mChannelsPerFrame = nChannels; 286 mFramesPerPacket = 1; 287 if (interleaved) 288 mBytesPerPacket = mBytesPerFrame = nChannels * sampleSize; 289 else { 290 mBytesPerPacket = mBytesPerFrame = sampleSize; 291 mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 292 } 293 } 294 IsCanonical()295 bool IsCanonical() const 296 { 297 if (mFormatID != kAudioFormatLinearPCM) return false; 298 UInt32 reqFormatFlags; 299 UInt32 flagsMask = (kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagsSampleFractionMask); 300 bool interleaved = (mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; 301 unsigned sampleSize = SizeOf32(AudioSampleType); 302 reqFormatFlags = kAudioFormatFlagsCanonical; 303 UInt32 reqFrameSize = interleaved ? (mChannelsPerFrame * sampleSize) : sampleSize; 304 305 return ((mFormatFlags & flagsMask) == reqFormatFlags 306 && mBitsPerChannel == 8 * sampleSize 307 && mFramesPerPacket == 1 308 && mBytesPerFrame == reqFrameSize 309 && mBytesPerPacket == reqFrameSize); 310 } 311 SetAUCanonical(UInt32 nChannels,bool interleaved)312 void SetAUCanonical(UInt32 nChannels, bool interleaved) 313 { 314 mFormatID = kAudioFormatLinearPCM; 315 #if CA_PREFER_FIXED_POINT 316 mFormatFlags = kAudioFormatFlagsCanonical | (kAudioUnitSampleFractionBits << kLinearPCMFormatFlagsSampleFractionShift); 317 #else 318 mFormatFlags = kAudioFormatFlagsCanonical; 319 #endif 320 mChannelsPerFrame = nChannels; 321 mFramesPerPacket = 1; 322 mBitsPerChannel = 8 * SizeOf32(AudioUnitSampleType); 323 if (interleaved) 324 mBytesPerPacket = mBytesPerFrame = nChannels * SizeOf32(AudioUnitSampleType); 325 else { 326 mBytesPerPacket = mBytesPerFrame = SizeOf32(AudioUnitSampleType); 327 mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 328 } 329 } 330 ChangeNumberChannels(UInt32 nChannels,bool interleaved)331 void ChangeNumberChannels(UInt32 nChannels, bool interleaved) 332 // alter an existing format 333 { 334 Assert(IsPCM(), "ChangeNumberChannels only works for PCM formats"); 335 UInt32 wordSize = SampleWordSize(); // get this before changing ANYTHING 336 if (wordSize == 0) 337 wordSize = (mBitsPerChannel + 7) / 8; 338 mChannelsPerFrame = nChannels; 339 mFramesPerPacket = 1; 340 if (interleaved) { 341 mBytesPerPacket = mBytesPerFrame = nChannels * wordSize; 342 mFormatFlags &= ~kAudioFormatFlagIsNonInterleaved; 343 } else { 344 mBytesPerPacket = mBytesPerFrame = wordSize; 345 mFormatFlags |= kAudioFormatFlagIsNonInterleaved; 346 } 347 } 348 349 // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 350 // 351 // other 352 353 bool IsEqual(const AudioStreamBasicDescription &other, bool interpretingWildcards=true) const; 354 Print()355 void Print() const { 356 Print (stdout); 357 } 358 Print(FILE * file)359 void Print(FILE* file) const { 360 PrintFormat (file, "", "AudioStreamBasicDescription:"); 361 } 362 PrintFormat(FILE * f,const char * indent,const char * name)363 void PrintFormat(FILE *f, const char *indent, const char *name) const { 364 char buf[256]; 365 fprintf(f, "%s%s %s\n", indent, name, AsString(buf, sizeof(buf))); 366 } 367 PrintFormat2(FILE * f,const char * indent,const char * name)368 void PrintFormat2(FILE *f, const char *indent, const char *name) const { // no trailing newline 369 char buf[256]; 370 fprintf(f, "%s%s %s", indent, name, AsString(buf, sizeof(buf))); 371 } 372 373 char * AsString(char *buf, size_t bufsize) const; 374 Print(const AudioStreamBasicDescription & inDesc)375 static void Print (const AudioStreamBasicDescription &inDesc) 376 { 377 CAStreamBasicDescription desc(inDesc); 378 desc.Print (); 379 } 380 381 OSStatus Save(CFPropertyListRef *outData) const; 382 383 OSStatus Restore(CFPropertyListRef &inData); 384 385 // Operations IsMixable(const AudioStreamBasicDescription & inDescription)386 static bool IsMixable(const AudioStreamBasicDescription& inDescription) { return (inDescription.mFormatID == kAudioFormatLinearPCM) && ((inDescription.mFormatFlags & kIsNonMixableFlag) == 0); } 387 static void NormalizeLinearPCMFormat(AudioStreamBasicDescription& ioDescription); 388 static void NormalizeLinearPCMFormat(bool inNativeEndian, AudioStreamBasicDescription& ioDescription); 389 static void ResetFormat(AudioStreamBasicDescription& ioDescription); 390 static void FillOutFormat(AudioStreamBasicDescription& ioDescription, const AudioStreamBasicDescription& inTemplateDescription); 391 static void GetSimpleName(const AudioStreamBasicDescription& inDescription, char* outName, UInt32 inMaxNameLength, bool inAbbreviate, bool inIncludeSampleRate = false); 392 #if CoreAudio_Debug 393 static void PrintToLog(const AudioStreamBasicDescription& inDesc); 394 #endif 395 }; 396 397 bool operator<(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y); 398 bool operator==(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y); 399 #if TARGET_OS_MAC || (TARGET_OS_WIN32 && (_MSC_VER > 600)) 400 inline bool operator!=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !(x == y); } 401 inline bool operator<=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return (x < y) || (x == y); } 402 inline bool operator>=(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !(x < y); } 403 inline bool operator>(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y) { return !((x < y) || (x == y)); } 404 #endif 405 406 bool SanityCheck(const AudioStreamBasicDescription& x); 407 408 409 #endif // __CAStreamBasicDescription_h__ 410