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