f65bdd99ac37c423cd6d09e7c40c3b08daeddef1
[ardour.git] / libs / appleutility / CAStreamBasicDescription.cpp
1 /*      Copyright:      � Copyright 2005 Apple Computer, Inc. All rights reserved.
2
3         Disclaimer:     IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
4                         ("Apple") in consideration of your agreement to the following terms, and your
5                         use, installation, modification or redistribution of this Apple software
6                         constitutes acceptance of these terms.  If you do not agree with these terms,
7                         please do not use, install, modify or redistribute this Apple software.
8
9                         In consideration of your agreement to abide by the following terms, and subject
10                         to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
11                         copyrights in this original Apple software (the "Apple Software"), to use,
12                         reproduce, modify and redistribute the Apple Software, with or without
13                         modifications, in source and/or binary forms; provided that if you redistribute
14                         the Apple Software in its entirety and without modifications, you must retain
15                         this notice and the following text and disclaimers in all such redistributions of
16                         the Apple Software.  Neither the name, trademarks, service marks or logos of
17                         Apple Computer, Inc. may be used to endorse or promote products derived from the
18                         Apple Software without specific prior written permission from Apple.  Except as
19                         expressly stated in this notice, no other rights or licenses, express or implied,
20                         are granted by Apple herein, including but not limited to any patent rights that
21                         may be infringed by your derivative works or by other works in which the Apple
22                         Software may be incorporated.
23
24                         The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
25                         WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
26                         WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27                         PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
28                         COMBINATION WITH YOUR PRODUCTS.
29
30                         IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
31                         CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
32                         GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33                         ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
34                         OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
35                         (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
36                         ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38 /*=============================================================================
39         CAStreamBasicDescription.cpp
40  
41 =============================================================================*/
42
43 #include "CAConditionalMacros.h"
44
45 #include "CAStreamBasicDescription.h"
46 #include "CAMath.h"
47
48 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
49         #include <CoreFoundation/CFByteOrder.h>
50 #else
51         #include <CFByteOrder.h>
52 #endif
53
54 #pragma mark    This file needs to compile on more earlier versions of the OS, so please keep that in mind when editing it
55
56 const AudioStreamBasicDescription       CAStreamBasicDescription::sEmpty = { 0.0, 0, 0, 0, 0, 0, 0, 0, 0 };
57
58 CAStreamBasicDescription::CAStreamBasicDescription(double inSampleRate,         UInt32 inFormatID,
59                                                                         UInt32 inBytesPerPacket,        UInt32 inFramesPerPacket,
60                                                                         UInt32 inBytesPerFrame,         UInt32 inChannelsPerFrame,
61                                                                         UInt32 inBitsPerChannel,        UInt32 inFormatFlags)
62 {
63         mSampleRate = inSampleRate;
64         mFormatID = inFormatID;
65         mBytesPerPacket = inBytesPerPacket;
66         mFramesPerPacket = inFramesPerPacket;
67         mBytesPerFrame = inBytesPerFrame;
68         mChannelsPerFrame = inChannelsPerFrame;
69         mBitsPerChannel = inBitsPerChannel;
70         mFormatFlags = inFormatFlags;
71 }
72
73 void CAStreamBasicDescription::PrintFormat(FILE *f, const char *indent, const char *name) const
74 {
75         fprintf(f, "%s%s ", indent, name);
76         char formatID[5];
77         *(UInt32 *)formatID = CFSwapInt32HostToBig(mFormatID);
78         formatID[4] = '\0';
79         fprintf(f, "%2ld ch, %6.0f Hz, '%-4.4s' (0x%08lX) ",            
80                                 NumberChannels(), mSampleRate, formatID,
81                                 mFormatFlags);
82         if (mFormatID == kAudioFormatLinearPCM) {
83                 bool isInt = !(mFormatFlags & kLinearPCMFormatFlagIsFloat);
84                 int wordSize = SampleWordSize();
85                 const char *endian = (wordSize > 1) ? 
86                         ((mFormatFlags & kLinearPCMFormatFlagIsBigEndian) ? " big-endian" : " little-endian" ) : "";
87                 const char *sign = isInt ? 
88                         ((mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) ? " signed" : " unsigned") : "";
89                 const char *floatInt = isInt ? "integer" : "float";
90                 char packed[32];
91                 if (wordSize > 0 && PackednessIsSignificant()) {
92                         if (mFormatFlags & kLinearPCMFormatFlagIsPacked)
93                                 sprintf(packed, "packed in %d bytes", wordSize);
94                         else
95                                 sprintf(packed, "unpacked in %d bytes", wordSize);
96                 } else
97                         packed[0] = '\0';
98                 const char *align = (wordSize > 0 && AlignmentIsSignificant()) ?
99                         ((mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) ? " high-aligned" : " low-aligned") : "";
100                 const char *deinter = (mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? ", deinterleaved" : "";
101                 const char *commaSpace = (packed[0]!='\0') || (align[0]!='\0') ? ", " : "";
102                 
103                 fprintf(f, "%ld-bit%s%s %s%s%s%s%s\n",
104                         mBitsPerChannel, endian, sign, floatInt, 
105                         commaSpace, packed, align, deinter);
106         } else if (mFormatID == 'alac') {       //      kAudioFormatAppleLossless
107                 int sourceBits = 0;
108                 switch (mFormatFlags)
109                 {
110                         case 1: //      kAppleLosslessFormatFlag_16BitSourceData
111                                 sourceBits = 16;
112                                 break;
113                 case 2: //      kAppleLosslessFormatFlag_20BitSourceData
114                         sourceBits = 20;
115                         break;
116                 case 3: //      kAppleLosslessFormatFlag_24BitSourceData
117                         sourceBits = 24;
118                         break;
119                 case 4: //      kAppleLosslessFormatFlag_32BitSourceData
120                         sourceBits = 32;
121                         break;
122                 }
123                 if (sourceBits)
124                         fprintf(f, "from %d-bit source, ", sourceBits);
125                 else
126                         fprintf(f, "from UNKNOWN source bit depth, ");
127                         
128                 fprintf(f, "%ld frames/packet\n", mFramesPerPacket);
129         }
130         else
131                 fprintf(f, "%ld bits/channel, %ld bytes/packet, %ld frames/packet, %ld bytes/frame\n", 
132                         mBitsPerChannel, mBytesPerPacket, mFramesPerPacket, mBytesPerFrame);
133 }
134
135 void    CAStreamBasicDescription::NormalizeLinearPCMFormat(AudioStreamBasicDescription& ioDescription)
136 {
137         //  the only thing that changes is to make mixable linear PCM into the canonical linear PCM format
138         if((ioDescription.mFormatID == kAudioFormatLinearPCM) && ((ioDescription.mFormatFlags & kIsNonMixableFlag) == 0))
139         {
140                 //  the canonical linear PCM format is 32 bit native endian floats
141                 ioDescription.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
142                 ioDescription.mBytesPerPacket = sizeof(Float32) * ioDescription.mChannelsPerFrame;
143                 ioDescription.mFramesPerPacket = 1;
144                 ioDescription.mBytesPerFrame = sizeof(Float32) * ioDescription.mChannelsPerFrame;
145                 ioDescription.mBitsPerChannel = 8 * sizeof(Float32);
146         }
147 }
148
149 void    CAStreamBasicDescription::ResetFormat(AudioStreamBasicDescription& ioDescription)
150 {
151         ioDescription.mSampleRate = 0;
152         ioDescription.mFormatID = 0;
153         ioDescription.mBytesPerPacket = 0;
154         ioDescription.mFramesPerPacket = 0;
155         ioDescription.mBytesPerFrame = 0;
156         ioDescription.mChannelsPerFrame = 0;
157         ioDescription.mBitsPerChannel = 0;
158         ioDescription.mFormatFlags = 0;
159 }
160
161 void    CAStreamBasicDescription::FillOutFormat(AudioStreamBasicDescription& ioDescription, const AudioStreamBasicDescription& inTemplateDescription)
162 {
163         if(fiszero(ioDescription.mSampleRate))
164         {
165                 ioDescription.mSampleRate = inTemplateDescription.mSampleRate;
166         }
167         if(ioDescription.mFormatID == 0)
168         {
169                 ioDescription.mFormatID = inTemplateDescription.mFormatID;
170         }
171         if(ioDescription.mFormatFlags == 0)
172         {
173                 ioDescription.mFormatFlags = inTemplateDescription.mFormatFlags;
174         }
175         if(ioDescription.mBytesPerPacket == 0)
176         {
177                 ioDescription.mBytesPerPacket = inTemplateDescription.mBytesPerPacket;
178         }
179         if(ioDescription.mFramesPerPacket == 0)
180         {
181                 ioDescription.mFramesPerPacket = inTemplateDescription.mFramesPerPacket;
182         }
183         if(ioDescription.mBytesPerFrame == 0)
184         {
185                 ioDescription.mBytesPerFrame = inTemplateDescription.mBytesPerFrame;
186         }
187         if(ioDescription.mChannelsPerFrame == 0)
188         {
189                 ioDescription.mChannelsPerFrame = inTemplateDescription.mChannelsPerFrame;
190         }
191         if(ioDescription.mBitsPerChannel == 0)
192         {
193                 ioDescription.mBitsPerChannel = inTemplateDescription.mBitsPerChannel;
194         }
195 }
196
197 void    CAStreamBasicDescription::GetSimpleName(const AudioStreamBasicDescription& inDescription, char* outName, bool inAbbreviate)
198 {
199         switch(inDescription.mFormatID)
200         {
201                 case kAudioFormatLinearPCM:
202                         {
203                                 const char* theEndianString = NULL;
204                                 if((inDescription.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0)
205                                 {
206                                         #if     TARGET_RT_LITTLE_ENDIAN
207                                                 theEndianString = "Big Endian";
208                                         #endif
209                                 }
210                                 else
211                                 {
212                                         #if     TARGET_RT_BIG_ENDIAN
213                                                 theEndianString = "Little Endian";
214                                         #endif
215                                 }
216                                 
217                                 const char* theKindString = NULL;
218                                 if((inDescription.mFormatFlags & kAudioFormatFlagIsFloat) != 0)
219                                 {
220                                         theKindString = (inAbbreviate ? "Float" : "Floating Point");
221                                 }
222                                 else if((inDescription.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
223                                 {
224                                         theKindString = (inAbbreviate ? "SInt" : "Signed Integer");
225                                 }
226                                 else
227                                 {
228                                         theKindString = (inAbbreviate ? "UInt" : "Unsigned Integer");
229                                 }
230                                 
231                                 const char* thePackingString = NULL;
232                                 if((inDescription.mFormatFlags & kAudioFormatFlagIsPacked) == 0)
233                                 {
234                                         if((inDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) != 0)
235                                         {
236                                                 thePackingString = "High";
237                                         }
238                                         else
239                                         {
240                                                 thePackingString = "Low";
241                                         }
242                                 }
243                                 
244                                 const char* theMixabilityString = NULL;
245                                 if((inDescription.mFormatFlags & kIsNonMixableFlag) == 0)
246                                 {
247                                         theMixabilityString = "Mixable";
248                                 }
249                                 else
250                                 {
251                                         theMixabilityString = "Unmixable";
252                                 }
253                                 
254                                 if(inAbbreviate)
255                                 {
256                                         if(theEndianString != NULL)
257                                         {
258                                                 if(thePackingString != NULL)
259                                                 {
260                                                         sprintf(outName, "%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);
261                                                 }
262                                                 else
263                                                 {
264                                                         sprintf(outName, "%s %d Ch %s %s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, theEndianString, theKindString, (int)inDescription.mBitsPerChannel);
265                                                 }
266                                         }
267                                         else
268                                         {
269                                                 if(thePackingString != NULL)
270                                                 {
271                                                         sprintf(outName, "%s %d Ch %s %s%d/%s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, thePackingString, theKindString, (int)inDescription.mBitsPerChannel, theKindString, (int)((inDescription.mBytesPerFrame / inDescription.mChannelsPerFrame) * 8));
272                                                 }
273                                                 else
274                                                 {
275                                                         sprintf(outName, "%s %d Ch %s%d", theMixabilityString, (int)inDescription.mChannelsPerFrame, theKindString, (int)inDescription.mBitsPerChannel);
276                                                 }
277                                         }
278                                 }
279                                 else
280                                 {
281                                         if(theEndianString != NULL)
282                                         {
283                                                 if(thePackingString != NULL)
284                                                 {
285                                                         sprintf(outName, "%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);
286                                                 }
287                                                 else
288                                                 {
289                                                         sprintf(outName, "%s %d Channel %d Bit %s %s", theMixabilityString, (int)inDescription.mChannelsPerFrame, (int)inDescription.mBitsPerChannel, theEndianString, theKindString);
290                                                 }
291                                         }
292                                         else
293                                         {
294                                                 if(thePackingString != NULL)
295                                                 {
296                                                         sprintf(outName, "%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);
297                                                 }
298                                                 else
299                                                 {
300                                                         sprintf(outName, "%s %d Channel %d Bit %s", theMixabilityString, (int)inDescription.mChannelsPerFrame, (int)inDescription.mBitsPerChannel, theKindString);
301                                                 }
302                                         }
303                                 }
304                         }
305                         break;
306                 
307                 case kAudioFormatAC3:
308                         strcpy(outName, "AC-3");
309                         break;
310                 
311                 case kAudioFormat60958AC3:
312                         strcpy(outName, "AC-3 for SPDIF");
313                         break;
314                 
315                 default:
316                         {
317                                 char* the4CCString = (char*)&inDescription.mFormatID;
318                                 outName[0] = the4CCString[0];
319                                 outName[1] = the4CCString[1];
320                                 outName[2] = the4CCString[2];
321                                 outName[3] = the4CCString[3];
322                                 outName[4] = 0;
323                         }
324                         break;
325         };
326 }
327
328 #if CoreAudio_Debug
329 #include "CALogMacros.h"
330
331 void    CAStreamBasicDescription::PrintToLog(const AudioStreamBasicDescription& inDesc)
332 {
333         PrintFloat              ("  Sample Rate:        ", inDesc.mSampleRate);
334         Print4CharCode  ("  Format ID:          ", inDesc.mFormatID);
335         PrintHex                ("  Format Flags:       ", inDesc.mFormatFlags);
336         PrintInt                ("  Bytes per Packet:   ", inDesc.mBytesPerPacket);
337         PrintInt                ("  Frames per Packet:  ", inDesc.mFramesPerPacket);
338         PrintInt                ("  Bytes per Frame:    ", inDesc.mBytesPerFrame);
339         PrintInt                ("  Channels per Frame: ", inDesc.mChannelsPerFrame);
340         PrintInt                ("  Bits per Channel:   ", inDesc.mBitsPerChannel);
341 }
342 #endif
343
344 bool    operator<(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
345 {
346         bool theAnswer = false;
347         bool isDone = false;
348         
349         //      note that if either side is 0, that field is skipped
350         
351         //      format ID is the first order sort
352         if((!isDone) && ((x.mFormatID != 0) && (y.mFormatID != 0)))
353         {
354                 if(x.mFormatID != y.mFormatID)
355                 {
356                         //      formats are sorted numerically except that linear
357                         //      PCM is always first
358                         if(x.mFormatID == kAudioFormatLinearPCM)
359                         {
360                                 theAnswer = true;
361                         }
362                         else if(y.mFormatID == kAudioFormatLinearPCM)
363                         {
364                                 theAnswer = false;
365                         }
366                         else
367                         {
368                                 theAnswer = x.mFormatID < y.mFormatID;
369                         }
370                         isDone = true;
371                 }
372         }
373         
374         
375         //  mixable is always better than non-mixable for linear PCM and should be the second order sort item
376         if((!isDone) && ((x.mFormatID == kAudioFormatLinearPCM) && (y.mFormatID == kAudioFormatLinearPCM)))
377         {
378                 if(((x.mFormatFlags & kIsNonMixableFlag) == 0) && ((y.mFormatFlags & kIsNonMixableFlag) != 0))
379                 {
380                         theAnswer = true;
381                         isDone = true;
382                 }
383                 else if(((x.mFormatFlags & kIsNonMixableFlag) != 0) && ((y.mFormatFlags & kIsNonMixableFlag) == 0))
384                 {
385                         theAnswer = false;
386                         isDone = true;
387                 }
388         }
389         
390         //      floating point vs integer for linear PCM only
391         if((!isDone) && ((x.mFormatID == kAudioFormatLinearPCM) && (y.mFormatID == kAudioFormatLinearPCM)))
392         {
393                 if((x.mFormatFlags & kAudioFormatFlagIsFloat) != (y.mFormatFlags & kAudioFormatFlagIsFloat))
394                 {
395                         //      floating point is better than integer
396                         theAnswer = y.mFormatFlags & kAudioFormatFlagIsFloat;
397                         isDone = true;
398                 }
399         }
400         
401         //      bit depth
402         if((!isDone) && ((x.mBitsPerChannel != 0) && (y.mBitsPerChannel != 0)))
403         {
404                 if(x.mBitsPerChannel != y.mBitsPerChannel)
405                 {
406                         //      deeper bit depths are higher quality
407                         theAnswer = x.mBitsPerChannel < y.mBitsPerChannel;
408                         isDone = true;
409                 }
410         }
411         
412         //      sample rate
413         if((!isDone) && fnonzero(x.mSampleRate) && fnonzero(y.mSampleRate))
414         {
415                 if(fnotequal(x.mSampleRate, y.mSampleRate))
416                 {
417                         //      higher sample rates are higher quality
418                         theAnswer = x.mSampleRate < y.mSampleRate;
419                         isDone = true;
420                 }
421         }
422         
423         //      number of channels
424         if((!isDone) && ((x.mChannelsPerFrame != 0) && (y.mChannelsPerFrame != 0)))
425         {
426                 if(x.mChannelsPerFrame != y.mChannelsPerFrame)
427                 {
428                         //      more channels is higher quality
429                         theAnswer = x.mChannelsPerFrame < y.mChannelsPerFrame;
430                         isDone = true;
431                 }
432         }
433         
434         return theAnswer;
435 }
436
437 static bool MatchFormatFlags(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
438 {
439         UInt32 xFlags = x.mFormatFlags;
440         UInt32 yFlags = y.mFormatFlags;
441         
442         // match wildcards
443         if (x.mFormatID == 0 || y.mFormatID == 0 || xFlags == 0 || yFlags == 0) 
444                 return true;
445         
446         if (x.mFormatID == kAudioFormatLinearPCM)
447         {                               
448                 // knock off the all clear flag
449                 xFlags = xFlags & ~kAudioFormatFlagsAreAllClear;
450                 yFlags = yFlags & ~kAudioFormatFlagsAreAllClear;
451         
452                 // if both kAudioFormatFlagIsPacked bits are set, then we don't care about the kAudioFormatFlagIsAlignedHigh bit.
453                 if (xFlags & yFlags & kAudioFormatFlagIsPacked) {
454                         xFlags = xFlags & ~kAudioFormatFlagIsAlignedHigh;
455                         yFlags = yFlags & ~kAudioFormatFlagIsAlignedHigh;
456                 }
457                 
458                 // if both kAudioFormatFlagIsFloat bits are set, then we don't care about the kAudioFormatFlagIsSignedInteger bit.
459                 if (xFlags & yFlags & kAudioFormatFlagIsFloat) {
460                         xFlags = xFlags & ~kAudioFormatFlagIsSignedInteger;
461                         yFlags = yFlags & ~kAudioFormatFlagIsSignedInteger;
462                 }
463                 
464                 //      if the bit depth is 8 bits or less and the format is packed, we don't care about endianness
465                 if((x.mBitsPerChannel <= 8) && ((xFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
466                 {
467                         xFlags = xFlags & ~kAudioFormatFlagIsBigEndian;
468                 }
469                 if((y.mBitsPerChannel <= 8) && ((yFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
470                 {
471                         yFlags = yFlags & ~kAudioFormatFlagIsBigEndian;
472                 }
473                 
474                 //      if the number of channels is 0 or 1, we don't care about non-interleavedness
475                 if (x.mChannelsPerFrame <= 1 && y.mChannelsPerFrame <= 1) {
476                         xFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
477                         yFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
478                 }
479         }
480         return xFlags == yFlags;
481 }
482
483 bool    operator==(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
484 {
485         //      the semantics for equality are:
486         //              1) Values must match exactly
487         //              2) wildcard's are ignored in the comparison
488         
489 #define MATCH(name) ((x.name) == 0 || (y.name) == 0 || (x.name) == (y.name))
490         
491         return 
492                         //      check the sample rate
493                 (fiszero(x.mSampleRate) || fiszero(y.mSampleRate) || fequal(x.mSampleRate, y.mSampleRate))
494                 
495                         //      check the format ids
496                 && MATCH(mFormatID)
497                 
498                         //      check the format flags
499                 && MatchFormatFlags(x, y)  
500                         
501                         //      check the bytes per packet
502                 && MATCH(mBytesPerPacket) 
503                 
504                         //      check the frames per packet
505                 && MATCH(mFramesPerPacket) 
506                 
507                         //      check the bytes per frame
508                 && MATCH(mBytesPerFrame) 
509                 
510                         //      check the channels per frame
511                 && MATCH(mChannelsPerFrame) 
512                 
513                         //      check the channels per frame
514                 && MATCH(mBitsPerChannel) ;
515 }
516
517 bool SanityCheck(const AudioStreamBasicDescription& x)
518 {
519         return (x.mSampleRate >= 0.);
520 }