1 /*
2      File: CAStreamBasicDescription.cpp
3  Abstract:  CAStreamBasicDescription.h
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 #include "CAStreamBasicDescription.h"
48 #include "CAMath.h"
49 
50 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
51 	#include <CoreFoundation/CFByteOrder.h>
52 #else
53 	#include <CFByteOrder.h>
54 #endif
55 
56 #pragma mark	This file needs to compile on earlier versions of the OS, so please keep that in mind when editing it
57 
CAStringForOSType(OSType t,char * writeLocation)58 char *CAStringForOSType (OSType t, char *writeLocation)
59 {
60 	char *p = writeLocation;
61     unsigned char str[4] = {0}, *q = str;
62 	*(UInt32 *)str = CFSwapInt32HostToBig(t);
63 
64 	bool hasNonPrint = false;
65 	for (int i = 0; i < 4; ++i) {
66 		if (!(isprint(*q) && *q != '\\')) {
67 			hasNonPrint = true;
68 			break;
69 		}
70         q++;
71 	}
72     q = str;
73 
74 	if (hasNonPrint)
75 		p += sprintf (p, "0x");
76 	else
77 		*p++ = '\'';
78 
79 	for (int i = 0; i < 4; ++i) {
80 		if (hasNonPrint) {
81 			p += sprintf(p, "%02X", *q++);
82 		} else {
83 			*p++ = *q++;
84 		}
85 	}
86 	if (!hasNonPrint)
87 		*p++ = '\'';
88 	*p = '\0';
89 	return writeLocation;
90 }
91 
92 
93 const AudioStreamBasicDescription	CAStreamBasicDescription::sEmpty = { 0.0, 0, 0, 0, 0, 0, 0, 0, 0 };
94 
CAStreamBasicDescription()95 CAStreamBasicDescription::CAStreamBasicDescription()
96 {
97 	memset (this, 0, sizeof(AudioStreamBasicDescription));
98 }
99 
CAStreamBasicDescription(const AudioStreamBasicDescription & desc)100 CAStreamBasicDescription::CAStreamBasicDescription(const AudioStreamBasicDescription &desc)
101 {
102 	SetFrom(desc);
103 }
104 
105 
CAStreamBasicDescription(double inSampleRate,UInt32 inFormatID,UInt32 inBytesPerPacket,UInt32 inFramesPerPacket,UInt32 inBytesPerFrame,UInt32 inChannelsPerFrame,UInt32 inBitsPerChannel,UInt32 inFormatFlags)106 CAStreamBasicDescription::CAStreamBasicDescription(double inSampleRate,		UInt32 inFormatID,
107 									UInt32 inBytesPerPacket,	UInt32 inFramesPerPacket,
108 									UInt32 inBytesPerFrame,		UInt32 inChannelsPerFrame,
109 									UInt32 inBitsPerChannel,	UInt32 inFormatFlags)
110 {
111 	mSampleRate = inSampleRate;
112 	mFormatID = inFormatID;
113 	mBytesPerPacket = inBytesPerPacket;
114 	mFramesPerPacket = inFramesPerPacket;
115 	mBytesPerFrame = inBytesPerFrame;
116 	mChannelsPerFrame = inChannelsPerFrame;
117 	mBitsPerChannel = inBitsPerChannel;
118 	mFormatFlags = inFormatFlags;
119 	mReserved = 0;
120 }
121 
AsString(char * buf,size_t _bufsize) const122 char *CAStreamBasicDescription::AsString(char *buf, size_t _bufsize) const
123 {
124 	int bufsize = (int)_bufsize;	// must be signed to protect against overflow
125 	char *theBuffer = buf;
126 	int nc;
127 	char formatID[24];
128 	CAStringForOSType (mFormatID, formatID);
129 	nc = snprintf(buf, bufsize, "%2d ch, %6.0f Hz, %s (0x%08X) ", (int)NumberChannels(), mSampleRate, formatID, (int)mFormatFlags);
130 	buf += nc; if ((bufsize -= nc) <= 0) goto exit;
131 	if (mFormatID == kAudioFormatLinearPCM) {
132 		bool isInt = !(mFormatFlags & kLinearPCMFormatFlagIsFloat);
133 		int wordSize = SampleWordSize();
134 		const char *endian = (wordSize > 1) ?
135 			((mFormatFlags & kLinearPCMFormatFlagIsBigEndian) ? " big-endian" : " little-endian" ) : "";
136 		const char *sign = isInt ?
137 			((mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) ? " signed" : " unsigned") : "";
138 		const char *floatInt = isInt ? "integer" : "float";
139 		char packed[32];
140 		if (wordSize > 0 && PackednessIsSignificant()) {
141 			if (mFormatFlags & kLinearPCMFormatFlagIsPacked)
142 				snprintf(packed, sizeof(packed), "packed in %d bytes", wordSize);
143 			else
144 				snprintf(packed, sizeof(packed), "unpacked in %d bytes", wordSize);
145 		} else
146 			packed[0] = '\0';
147 		const char *align = (wordSize > 0 && AlignmentIsSignificant()) ?
148 			((mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) ? " high-aligned" : " low-aligned") : "";
149 		const char *deinter = (mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? ", deinterleaved" : "";
150 		const char *commaSpace = (packed[0]!='\0') || (align[0]!='\0') ? ", " : "";
151 		char bitdepth[20];
152 
153 		int fracbits = (mFormatFlags & kLinearPCMFormatFlagsSampleFractionMask) >> kLinearPCMFormatFlagsSampleFractionShift;
154 		if (fracbits > 0)
155 			snprintf(bitdepth, sizeof(bitdepth), "%d.%d", (int)mBitsPerChannel - fracbits, fracbits);
156 		else
157 			snprintf(bitdepth, sizeof(bitdepth), "%d", (int)mBitsPerChannel);
158 
159 		/* nc =*/ snprintf(buf, bufsize, "%s-bit%s%s %s%s%s%s%s",
160 			bitdepth, endian, sign, floatInt,
161 			commaSpace, packed, align, deinter);
162 		// buf += nc; if ((bufsize -= nc) <= 0) goto exit;
163 	} else if (mFormatID == 'alac') {	//	kAudioFormatAppleLossless
164 		int sourceBits = 0;
165 		switch (mFormatFlags)
166 		{
167 			case 1:	//	kAppleLosslessFormatFlag_16BitSourceData
168 				sourceBits = 16;
169 				break;
170     		case 2:	//	kAppleLosslessFormatFlag_20BitSourceData
171     			sourceBits = 20;
172     			break;
173     		case 3:	//	kAppleLosslessFormatFlag_24BitSourceData
174     			sourceBits = 24;
175     			break;
176     		case 4:	//	kAppleLosslessFormatFlag_32BitSourceData
177     			sourceBits = 32;
178     			break;
179 		}
180 		if (sourceBits)
181 			nc = snprintf(buf, bufsize, "from %d-bit source, ", sourceBits);
182 		else
183 			nc = snprintf(buf, bufsize, "from UNKNOWN source bit depth, ");
184 		buf += nc; if ((bufsize -= nc) <= 0) goto exit;
185 		/* nc =*/ snprintf(buf, bufsize, "%d frames/packet", (int)mFramesPerPacket);
186 		//	buf += nc; if ((bufsize -= nc) <= 0) goto exit;
187 	}
188 	else
189 		/* nc =*/ snprintf(buf, bufsize, "%d bits/channel, %d bytes/packet, %d frames/packet, %d bytes/frame",
190 			(int)mBitsPerChannel, (int)mBytesPerPacket, (int)mFramesPerPacket, (int)mBytesPerFrame);
191 exit:
192 	return theBuffer;
193 }
194 
NormalizeLinearPCMFormat(AudioStreamBasicDescription & ioDescription)195 void	CAStreamBasicDescription::NormalizeLinearPCMFormat(AudioStreamBasicDescription& ioDescription)
196 {
197 	//  the only thing that changes is to make mixable linear PCM into the canonical linear PCM format
198 	if((ioDescription.mFormatID == kAudioFormatLinearPCM) && ((ioDescription.mFormatFlags & kIsNonMixableFlag) == 0))
199 	{
200 		//  the canonical linear PCM format
201 		ioDescription.mFormatFlags = kAudioFormatFlagsCanonical;
202 		ioDescription.mBytesPerPacket = SizeOf32(AudioSampleType) * ioDescription.mChannelsPerFrame;
203 		ioDescription.mFramesPerPacket = 1;
204 		ioDescription.mBytesPerFrame = SizeOf32(AudioSampleType) * ioDescription.mChannelsPerFrame;
205 		ioDescription.mBitsPerChannel = 8 * SizeOf32(AudioSampleType);
206 	}
207 }
208 
NormalizeLinearPCMFormat(bool inNativeEndian,AudioStreamBasicDescription & ioDescription)209 void	CAStreamBasicDescription::NormalizeLinearPCMFormat(bool inNativeEndian, AudioStreamBasicDescription& ioDescription)
210 {
211 	//  the only thing that changes is to make mixable linear PCM into the canonical linear PCM format
212 	if((ioDescription.mFormatID == kAudioFormatLinearPCM) && ((ioDescription.mFormatFlags & kIsNonMixableFlag) == 0))
213 	{
214 		//  the canonical linear PCM format
215 		ioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
216 		if(inNativeEndian)
217 		{
218 #if TARGET_RT_BIG_ENDIAN
219 			ioDescription.mFormatFlags |= kAudioFormatFlagIsBigEndian;
220 #endif
221 		}
222 		else
223 		{
224 #if TARGET_RT_LITTLE_ENDIAN
225 			ioDescription.mFormatFlags |= kAudioFormatFlagIsBigEndian;
226 #endif
227 		}
228 		ioDescription.mBytesPerPacket = SizeOf32(AudioSampleType) * ioDescription.mChannelsPerFrame;
229 		ioDescription.mFramesPerPacket = 1;
230 		ioDescription.mBytesPerFrame = SizeOf32(AudioSampleType) * ioDescription.mChannelsPerFrame;
231 		ioDescription.mBitsPerChannel = 8 * SizeOf32(AudioSampleType);
232 	}
233 }
234 
ResetFormat(AudioStreamBasicDescription & ioDescription)235 void	CAStreamBasicDescription::ResetFormat(AudioStreamBasicDescription& ioDescription)
236 {
237 	ioDescription.mSampleRate = 0;
238 	ioDescription.mFormatID = 0;
239 	ioDescription.mBytesPerPacket = 0;
240 	ioDescription.mFramesPerPacket = 0;
241 	ioDescription.mBytesPerFrame = 0;
242 	ioDescription.mChannelsPerFrame = 0;
243 	ioDescription.mBitsPerChannel = 0;
244 	ioDescription.mFormatFlags = 0;
245 }
246 
FillOutFormat(AudioStreamBasicDescription & ioDescription,const AudioStreamBasicDescription & inTemplateDescription)247 void	CAStreamBasicDescription::FillOutFormat(AudioStreamBasicDescription& ioDescription, const AudioStreamBasicDescription& inTemplateDescription)
248 {
249 	if(fiszero(ioDescription.mSampleRate))
250 	{
251 		ioDescription.mSampleRate = inTemplateDescription.mSampleRate;
252 	}
253 	if(ioDescription.mFormatID == 0)
254 	{
255 		ioDescription.mFormatID = inTemplateDescription.mFormatID;
256 	}
257 	if(ioDescription.mFormatFlags == 0)
258 	{
259 		ioDescription.mFormatFlags = inTemplateDescription.mFormatFlags;
260 	}
261 	if(ioDescription.mBytesPerPacket == 0)
262 	{
263 		ioDescription.mBytesPerPacket = inTemplateDescription.mBytesPerPacket;
264 	}
265 	if(ioDescription.mFramesPerPacket == 0)
266 	{
267 		ioDescription.mFramesPerPacket = inTemplateDescription.mFramesPerPacket;
268 	}
269 	if(ioDescription.mBytesPerFrame == 0)
270 	{
271 		ioDescription.mBytesPerFrame = inTemplateDescription.mBytesPerFrame;
272 	}
273 	if(ioDescription.mChannelsPerFrame == 0)
274 	{
275 		ioDescription.mChannelsPerFrame = inTemplateDescription.mChannelsPerFrame;
276 	}
277 	if(ioDescription.mBitsPerChannel == 0)
278 	{
279 		ioDescription.mBitsPerChannel = inTemplateDescription.mBitsPerChannel;
280 	}
281 }
282 
GetSimpleName(const AudioStreamBasicDescription & inDescription,char * outName,UInt32 inMaxNameLength,bool inAbbreviate,bool inIncludeSampleRate)283 void	CAStreamBasicDescription::GetSimpleName(const AudioStreamBasicDescription& inDescription, char* outName, UInt32 inMaxNameLength, bool inAbbreviate, bool inIncludeSampleRate)
284 {
285 	if(inIncludeSampleRate)
286 	{
287 		int theCharactersWritten = snprintf(outName, inMaxNameLength, "%.0f ", inDescription.mSampleRate);
288 		outName += theCharactersWritten;
289 		inMaxNameLength -= theCharactersWritten;
290 	}
291 
292 	switch(inDescription.mFormatID)
293 	{
294 		case kAudioFormatLinearPCM:
295 			{
296 				const char* theEndianString = NULL;
297 				if((inDescription.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0)
298 				{
299 					#if	TARGET_RT_LITTLE_ENDIAN
300 						theEndianString = "Big Endian";
301 					#endif
302 				}
303 				else
304 				{
305 					#if	TARGET_RT_BIG_ENDIAN
306 						theEndianString = "Little Endian";
307 					#endif
308 				}
309 
310 				const char* theKindString = NULL;
311 				if((inDescription.mFormatFlags & kAudioFormatFlagIsFloat) != 0)
312 				{
313 					theKindString = (inAbbreviate ? "Float" : "Floating Point");
314 				}
315 				else if((inDescription.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
316 				{
317 					theKindString = (inAbbreviate ? "SInt" : "Signed Integer");
318 				}
319 				else
320 				{
321 					theKindString = (inAbbreviate ? "UInt" : "Unsigned Integer");
322 				}
323 
324 				const char* thePackingString = NULL;
325 				if((inDescription.mFormatFlags & kAudioFormatFlagIsPacked) == 0)
326 				{
327 					if((inDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) != 0)
328 					{
329 						thePackingString = "High";
330 					}
331 					else
332 					{
333 						thePackingString = "Low";
334 					}
335 				}
336 
337 				const char* theMixabilityString = NULL;
338 				if((inDescription.mFormatFlags & kIsNonMixableFlag) == 0)
339 				{
340 					theMixabilityString = "Mixable";
341 				}
342 				else
343 				{
344 					theMixabilityString = "Unmixable";
345 				}
346 
347 				if(inAbbreviate)
348 				{
349 					if(theEndianString != NULL)
350 					{
351 						if(thePackingString != NULL)
352 						{
353 							snprintf(outName, inMaxNameLength, "%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);
354 						}
355 						else
356 						{
357 							snprintf(outName, inMaxNameLength, "%s %d Ch %s %s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, theEndianString, theKindString, (int)inDescription.mBitsPerChannel);
358 						}
359 					}
360 					else
361 					{
362 						if(thePackingString != NULL)
363 						{
364 							snprintf(outName, inMaxNameLength, "%s %d Ch %s %s%d/%s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, thePackingString, theKindString, (int)inDescription.mBitsPerChannel, theKindString, (int)((inDescription.mBytesPerFrame / inDescription.mChannelsPerFrame) * 8));
365 						}
366 						else
367 						{
368 							snprintf(outName, inMaxNameLength, "%s %d Ch %s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, theKindString, (int)inDescription.mBitsPerChannel);
369 						}
370 					}
371 				}
372 				else
373 				{
374 					if(theEndianString != NULL)
375 					{
376 						if(thePackingString != NULL)
377 						{
378 							snprintf(outName, inMaxNameLength, "%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);
379 						}
380 						else
381 						{
382 							snprintf(outName, inMaxNameLength, "%s %d Channel %d Bit %s %s", theMixabilityString, (int)inDescription.mChannelsPerFrame, (int)inDescription.mBitsPerChannel, theEndianString, theKindString);
383 						}
384 					}
385 					else
386 					{
387 						if(thePackingString != NULL)
388 						{
389 							snprintf(outName, inMaxNameLength, "%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);
390 						}
391 						else
392 						{
393 							snprintf(outName, inMaxNameLength, "%s %d Channel %d Bit %s", theMixabilityString, (int)inDescription.mChannelsPerFrame, (int)inDescription.mBitsPerChannel, theKindString);
394 						}
395 					}
396 				}
397 			}
398 			break;
399 
400 		case kAudioFormatAC3:
401 			strlcpy(outName, "AC-3", sizeof(outName));
402 			break;
403 
404 		case kAudioFormat60958AC3:
405 			strlcpy(outName, "AC-3 for SPDIF", sizeof(outName));
406 			break;
407 
408 		default:
409 			CACopy4CCToCString(outName, inDescription.mFormatID);
410 			break;
411 	};
412 }
413 
414 #if CoreAudio_Debug
415 #include "CALogMacros.h"
416 
PrintToLog(const AudioStreamBasicDescription & inDesc)417 void	CAStreamBasicDescription::PrintToLog(const AudioStreamBasicDescription& inDesc)
418 {
419 	PrintFloat		("  Sample Rate:        ", inDesc.mSampleRate);
420 	Print4CharCode	("  Format ID:          ", inDesc.mFormatID);
421 	PrintHex		("  Format Flags:       ", inDesc.mFormatFlags);
422 	PrintInt		("  Bytes per Packet:   ", inDesc.mBytesPerPacket);
423 	PrintInt		("  Frames per Packet:  ", inDesc.mFramesPerPacket);
424 	PrintInt		("  Bytes per Frame:    ", inDesc.mBytesPerFrame);
425 	PrintInt		("  Channels per Frame: ", inDesc.mChannelsPerFrame);
426 	PrintInt		("  Bits per Channel:   ", inDesc.mBitsPerChannel);
427 }
428 #endif
429 
operator <(const AudioStreamBasicDescription & x,const AudioStreamBasicDescription & y)430 bool	operator<(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
431 {
432 	bool theAnswer = false;
433 	bool isDone = false;
434 
435 	//	note that if either side is 0, that field is skipped
436 
437 	//	format ID is the first order sort
438 	if((!isDone) && ((x.mFormatID != 0) && (y.mFormatID != 0)))
439 	{
440 		if(x.mFormatID != y.mFormatID)
441 		{
442 			//	formats are sorted numerically except that linear
443 			//	PCM is always first
444 			if(x.mFormatID == kAudioFormatLinearPCM)
445 			{
446 				theAnswer = true;
447 			}
448 			else if(y.mFormatID == kAudioFormatLinearPCM)
449 			{
450 				theAnswer = false;
451 			}
452 			else
453 			{
454 				theAnswer = x.mFormatID < y.mFormatID;
455 			}
456 			isDone = true;
457 		}
458 	}
459 
460 
461 	//  mixable is always better than non-mixable for linear PCM and should be the second order sort item
462 	if((!isDone) && ((x.mFormatID == kAudioFormatLinearPCM) && (y.mFormatID == kAudioFormatLinearPCM)))
463 	{
464 		if(((x.mFormatFlags & kIsNonMixableFlag) == 0) && ((y.mFormatFlags & kIsNonMixableFlag) != 0))
465 		{
466 			theAnswer = true;
467 			isDone = true;
468 		}
469 		else if(((x.mFormatFlags & kIsNonMixableFlag) != 0) && ((y.mFormatFlags & kIsNonMixableFlag) == 0))
470 		{
471 			theAnswer = false;
472 			isDone = true;
473 		}
474 	}
475 
476 	//	floating point vs integer for linear PCM only
477 	if((!isDone) && ((x.mFormatID == kAudioFormatLinearPCM) && (y.mFormatID == kAudioFormatLinearPCM)))
478 	{
479 		if((x.mFormatFlags & kAudioFormatFlagIsFloat) != (y.mFormatFlags & kAudioFormatFlagIsFloat))
480 		{
481 			//	floating point is better than integer
482 			theAnswer = y.mFormatFlags & kAudioFormatFlagIsFloat;
483 			isDone = true;
484 		}
485 	}
486 
487 	//	bit depth
488 	if((!isDone) && ((x.mBitsPerChannel != 0) && (y.mBitsPerChannel != 0)))
489 	{
490 		if(x.mBitsPerChannel != y.mBitsPerChannel)
491 		{
492 			//	deeper bit depths are higher quality
493 			theAnswer = x.mBitsPerChannel < y.mBitsPerChannel;
494 			isDone = true;
495 		}
496 	}
497 
498 	//	sample rate
499 	if((!isDone) && fnonzero(x.mSampleRate) && fnonzero(y.mSampleRate))
500 	{
501 		if(fnotequal(x.mSampleRate, y.mSampleRate))
502 		{
503 			//	higher sample rates are higher quality
504 			theAnswer = x.mSampleRate < y.mSampleRate;
505 			isDone = true;
506 		}
507 	}
508 
509 	//	number of channels
510 	if((!isDone) && ((x.mChannelsPerFrame != 0) && (y.mChannelsPerFrame != 0)))
511 	{
512 		if(x.mChannelsPerFrame != y.mChannelsPerFrame)
513 		{
514 			//	more channels is higher quality
515 			theAnswer = x.mChannelsPerFrame < y.mChannelsPerFrame;
516 			//isDone = true;
517 		}
518 	}
519 
520 	return theAnswer;
521 }
522 
MatchFormatFlags(const AudioStreamBasicDescription & x,const AudioStreamBasicDescription & y)523 static bool MatchFormatFlags(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
524 {
525 	UInt32 xFlags = x.mFormatFlags;
526 	UInt32 yFlags = y.mFormatFlags;
527 
528 	// match wildcards
529 	if (x.mFormatID == 0 || y.mFormatID == 0 || xFlags == 0 || yFlags == 0)
530 		return true;
531 
532 	if (x.mFormatID == kAudioFormatLinearPCM)
533 	{
534 		// knock off the all clear flag
535 		xFlags = xFlags & ~kAudioFormatFlagsAreAllClear;
536 		yFlags = yFlags & ~kAudioFormatFlagsAreAllClear;
537 
538 		// if both kAudioFormatFlagIsPacked bits are set, then we don't care about the kAudioFormatFlagIsAlignedHigh bit.
539 		if (xFlags & yFlags & kAudioFormatFlagIsPacked) {
540 			xFlags = xFlags & ~kAudioFormatFlagIsAlignedHigh;
541 			yFlags = yFlags & ~kAudioFormatFlagIsAlignedHigh;
542 		}
543 
544 		// if both kAudioFormatFlagIsFloat bits are set, then we don't care about the kAudioFormatFlagIsSignedInteger bit.
545 		if (xFlags & yFlags & kAudioFormatFlagIsFloat) {
546 			xFlags = xFlags & ~kAudioFormatFlagIsSignedInteger;
547 			yFlags = yFlags & ~kAudioFormatFlagIsSignedInteger;
548 		}
549 
550 		//	if the bit depth is 8 bits or less and the format is packed, we don't care about endianness
551 		if((x.mBitsPerChannel <= 8) && ((xFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
552 		{
553 			xFlags = xFlags & ~kAudioFormatFlagIsBigEndian;
554 		}
555 		if((y.mBitsPerChannel <= 8) && ((yFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
556 		{
557 			yFlags = yFlags & ~kAudioFormatFlagIsBigEndian;
558 		}
559 
560 		//	if the number of channels is 1, we don't care about non-interleavedness
561 		if (x.mChannelsPerFrame == 1 && y.mChannelsPerFrame == 1) {
562 			xFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
563 			yFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
564 		}
565 	}
566 	return xFlags == yFlags;
567 }
568 
operator ==(const AudioStreamBasicDescription & x,const AudioStreamBasicDescription & y)569 bool	operator==(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
570 {
571 	//	the semantics for equality are:
572 	//		1) Values must match exactly -- except for PCM format flags, see above.
573 	//		2) wildcard's are ignored in the comparison
574 
575 #define MATCH(name) ((x.name) == 0 || (y.name) == 0 || (x.name) == (y.name))
576 
577 	return
578 			//	check the sample rate
579 		(fiszero(x.mSampleRate) || fiszero(y.mSampleRate) || fequal(x.mSampleRate, y.mSampleRate))
580 
581 			//	check the format ids
582 		&& MATCH(mFormatID)
583 
584 			//	check the format flags
585 		&& MatchFormatFlags(x, y)
586 
587 			//	check the bytes per packet
588 		&& MATCH(mBytesPerPacket)
589 
590 			//	check the frames per packet
591 		&& MATCH(mFramesPerPacket)
592 
593 			//	check the bytes per frame
594 		&& MATCH(mBytesPerFrame)
595 
596 			//	check the channels per frame
597 		&& MATCH(mChannelsPerFrame)
598 
599 			//	check the channels per frame
600 		&& MATCH(mBitsPerChannel) ;
601 }
602 
IsEqual(const AudioStreamBasicDescription & other,bool interpretingWildcards) const603 bool	CAStreamBasicDescription::IsEqual(const AudioStreamBasicDescription &other, bool interpretingWildcards) const
604 {
605 	if (interpretingWildcards)
606 		return *this == other;
607 	return memcmp(this, &other, offsetof(AudioStreamBasicDescription, mReserved)) == 0;
608 }
609 
SanityCheck(const AudioStreamBasicDescription & x)610 bool SanityCheck(const AudioStreamBasicDescription& x)
611 {
612 	// This function returns false if there are sufficiently insane values in any field.
613 	// It is very conservative so even some very unlikely values will pass.
614 	// This is just meant to catch the case where the data from a file is corrupted.
615 
616 	return
617 		(x.mSampleRate >= 0.)
618 		&& (x.mSampleRate < 3e6)	// SACD sample rate is 2.8224 MHz
619 		&& (x.mBytesPerPacket < 1000000)
620 		&& (x.mFramesPerPacket < 1000000)
621 		&& (x.mBytesPerFrame < 1000000)
622 		&& (x.mChannelsPerFrame <= 1024)
623 		&& (x.mBitsPerChannel <= 1024)
624 		&& (x.mFormatID != 0)
625 		&& !(x.mFormatID == kAudioFormatLinearPCM && (x.mFramesPerPacket != 1 || x.mBytesPerPacket != x.mBytesPerFrame));
626 }
627 
FromText(const char * inTextDesc,AudioStreamBasicDescription & fmt)628 bool CAStreamBasicDescription::FromText(const char *inTextDesc, AudioStreamBasicDescription &fmt)
629 {
630 	const char *p = inTextDesc;
631 
632 	memset(&fmt, 0, sizeof(fmt));
633 
634 	bool isPCM = true;	// until proven otherwise
635 	UInt32 pcmFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
636 
637 	if (p[0] == '-')	// previously we required a leading dash on PCM formats
638 		++p;
639 
640 	if (p[0] == 'B' && p[1] == 'E') {
641 		pcmFlags |= kLinearPCMFormatFlagIsBigEndian;
642 		p += 2;
643 	} else if (p[0] == 'L' && p[1] == 'E') {
644 		p += 2;
645 	} else {
646 		// default is native-endian
647 #if TARGET_RT_BIG_ENDIAN
648 		pcmFlags |= kLinearPCMFormatFlagIsBigEndian;
649 #endif
650 	}
651 	if (p[0] == 'F') {
652 		pcmFlags = (pcmFlags & ~kAudioFormatFlagIsSignedInteger) | kAudioFormatFlagIsFloat;
653 		++p;
654 	} else {
655 		if (p[0] == 'U') {
656 			pcmFlags &= ~kAudioFormatFlagIsSignedInteger;
657 			++p;
658 		}
659 		if (p[0] == 'I')
660 			++p;
661 		else {
662 			// it's not PCM; presumably some other format (NOT VALIDATED; use AudioFormat for that)
663 			isPCM = false;
664 			p = inTextDesc;	// go back to the beginning
665 			char buf[4] = { ' ',' ',' ',' ' };
666 			for (int i = 0; i < 4; ++i) {
667 				if (*p != '\\') {
668 					if ((buf[i] = *p++) == '\0') {
669 						// special-case for 'aac'
670 						if (i != 3) return false;
671 						--p;	// keep pointing at the terminating null
672 						buf[i] = ' ';
673 						break;
674 					}
675 				} else {
676 					// "\xNN" is a hex byte
677 					if (*++p != 'x') return false;
678 					int x;
679 					if (sscanf(++p, "%02X", &x) != 1) return false;
680 					buf[i] = x;
681 					p += 2;
682 				}
683 			}
684 
685 			if (strchr("-@/#", buf[3])) {
686 				// further special-casing for 'aac'
687 				buf[3] = ' ';
688 				--p;
689 			}
690 
691 			fmt.mFormatID = CFSwapInt32BigToHost(*(UInt32 *)buf);
692 		}
693 	}
694 
695 	if (isPCM) {
696 		fmt.mFormatID = kAudioFormatLinearPCM;
697 		fmt.mFormatFlags = pcmFlags;
698 		fmt.mFramesPerPacket = 1;
699 		fmt.mChannelsPerFrame = 1;
700 		int bitdepth = 0, fracbits = 0;
701 		while (isdigit(*p))
702 			bitdepth = 10 * bitdepth + *p++ - '0';
703 		if (*p == '.') {
704 			++p;
705 			if (!isdigit(*p)) {
706 				fprintf(stderr, "Expected fractional bits following '.'\n");
707 				goto Bail;
708 			}
709 			while (isdigit(*p))
710 				fracbits = 10 * fracbits + *p++ - '0';
711 			bitdepth += fracbits;
712 			fmt.mFormatFlags |= (fracbits << kLinearPCMFormatFlagsSampleFractionShift);
713 		}
714 		fmt.mBitsPerChannel = bitdepth;
715 		fmt.mBytesPerPacket = fmt.mBytesPerFrame = (bitdepth + 7) / 8;
716 		if (bitdepth & 7) {
717 			// assume unpacked. (packed odd bit depths are describable but not supported in AudioConverter.)
718 			fmt.mFormatFlags &= ~kLinearPCMFormatFlagIsPacked;
719 			// alignment matters; default to high-aligned. use ':L_' for low.
720 			fmt.mFormatFlags |= kLinearPCMFormatFlagIsAlignedHigh;
721 		}
722 	}
723 	if (*p == '@') {
724 		++p;
725 		while (isdigit(*p))
726 			fmt.mSampleRate = 10 * fmt.mSampleRate + (*p++ - '0');
727 	}
728 	if (*p == '/') {
729 		UInt32 flags = 0;
730 		while (true) {
731 			char c = *++p;
732 			if (c >= '0' && c <= '9')
733 				flags = (flags << 4) | (c - '0');
734 			else if (c >= 'A' && c <= 'F')
735 				flags = (flags << 4) | (c - 'A' + 10);
736 			else if (c >= 'a' && c <= 'f')
737 				flags = (flags << 4) | (c - 'a' + 10);
738 			else break;
739 		}
740 		fmt.mFormatFlags = flags;
741 	}
742 	if (*p == '#') {
743 		++p;
744 		while (isdigit(*p))
745 			fmt.mFramesPerPacket = 10 * fmt.mFramesPerPacket + (*p++ - '0');
746 	}
747 	if (*p == ':') {
748 		++p;
749 		fmt.mFormatFlags &= ~kLinearPCMFormatFlagIsPacked;
750 		if (*p == 'L')
751 			fmt.mFormatFlags &= ~kLinearPCMFormatFlagIsAlignedHigh;
752 		else if (*p == 'H')
753 			fmt.mFormatFlags |= kLinearPCMFormatFlagIsAlignedHigh;
754 		else
755 			goto Bail;
756 		++p;
757 		int bytesPerFrame = 0;
758 		while (isdigit(*p))
759 			bytesPerFrame = 10 * bytesPerFrame + (*p++ - '0');
760 		fmt.mBytesPerFrame = fmt.mBytesPerPacket = bytesPerFrame;
761 	}
762 	if (*p == ',') {
763 		++p;
764 		int ch = 0;
765 		while (isdigit(*p))
766 			ch = 10 * ch + (*p++ - '0');
767 		fmt.mChannelsPerFrame = ch;
768 		if (*p == 'D') {
769 			++p;
770 			if (fmt.mFormatID != kAudioFormatLinearPCM) {
771 				fprintf(stderr, "non-interleaved flag invalid for non-PCM formats\n");
772 				goto Bail;
773 			}
774 			fmt.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
775 		} else {
776 			if (*p == 'I') ++p;	// default
777 			if (fmt.mFormatID == kAudioFormatLinearPCM)
778 				fmt.mBytesPerPacket = fmt.mBytesPerFrame *= ch;
779 		}
780 	}
781 	if (*p != '\0') {
782 		fprintf(stderr, "extra characters at end of format string: %s\n", p);
783 		goto Bail;
784 	}
785 	return true;
786 
787 Bail:
788 	fprintf(stderr, "Invalid format string: %s\n", inTextDesc);
789 	fprintf(stderr, "Syntax of format strings is: \n");
790 	return false;
791 }
792 
793 const char *CAStreamBasicDescription::sTextParsingUsageString =
794 	"format[@sample_rate_hz][/format_flags][#frames_per_packet][:LHbytesPerFrame][,channelsDI].\n"
795 	"Format for PCM is [-][BE|LE]{F|I|UI}{bitdepth}; else a 4-char format code (e.g. aac, alac).\n";
796