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