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