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