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