OpenShot Audio Library | OpenShotAudio  0.3.0
juce_CoreAudioFormat.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  By using JUCE, you agree to the terms of both the JUCE 5 End-User License
11  Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
12  27th April 2017).
13 
14  End User License Agreement: www.juce.com/juce-5-licence
15  Privacy Policy: www.juce.com/juce-5-privacy-policy
16 
17  Or: You may also use this code under the terms of the GPL v3 (see
18  www.gnu.org/licenses).
19 
20  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22  DISCLAIMED.
23 
24  ==============================================================================
25 */
26 
27 #if JUCE_MAC || JUCE_IOS
28 
29 #include "../../juce_audio_basics/native/juce_mac_CoreAudioLayouts.h"
30 
31 namespace juce
32 {
33 
34 //==============================================================================
35 namespace
36 {
37  const char* const coreAudioFormatName = "CoreAudio supported file";
38 
39  StringArray findFileExtensionsForCoreAudioCodecs()
40  {
41  StringArray extensionsArray;
42  CFArrayRef extensions = nullptr;
43  UInt32 sizeOfArray = sizeof (extensions);
44 
45  if (AudioFileGetGlobalInfo (kAudioFileGlobalInfo_AllExtensions, 0, nullptr, &sizeOfArray, &extensions) == noErr)
46  {
47  auto numValues = CFArrayGetCount (extensions);
48 
49  for (CFIndex i = 0; i < numValues; ++i)
50  extensionsArray.add ("." + String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (extensions, i)));
51 
52  CFRelease (extensions);
53  }
54 
55  return extensionsArray;
56  }
57 }
58 
59 //==============================================================================
60 const char* const CoreAudioFormat::midiDataBase64 = "midiDataBase64";
61 const char* const CoreAudioFormat::tempo = "tempo";
62 const char* const CoreAudioFormat::timeSig = "time signature";
63 const char* const CoreAudioFormat::keySig = "key signature";
64 
65 //==============================================================================
66 struct CoreAudioFormatMetatdata
67 {
68  static uint32 chunkName (const char* const name) noexcept { return ByteOrder::bigEndianInt (name); }
69 
70  //==============================================================================
71  struct FileHeader
72  {
73  FileHeader (InputStream& input)
74  {
75  fileType = (uint32) input.readIntBigEndian();
76  fileVersion = (uint16) input.readShortBigEndian();
77  fileFlags = (uint16) input.readShortBigEndian();
78  }
79 
80  uint32 fileType;
81  uint16 fileVersion;
82  uint16 fileFlags;
83  };
84 
85  //==============================================================================
86  struct ChunkHeader
87  {
88  ChunkHeader (InputStream& input)
89  {
90  chunkType = (uint32) input.readIntBigEndian();
91  chunkSize = (int64) input.readInt64BigEndian();
92  }
93 
94  uint32 chunkType;
95  int64 chunkSize;
96  };
97 
98  //==============================================================================
99  struct AudioDescriptionChunk
100  {
101  AudioDescriptionChunk (InputStream& input)
102  {
103  sampleRate = input.readDoubleBigEndian();
104  formatID = (uint32) input.readIntBigEndian();
105  formatFlags = (uint32) input.readIntBigEndian();
106  bytesPerPacket = (uint32) input.readIntBigEndian();
107  framesPerPacket = (uint32) input.readIntBigEndian();
108  channelsPerFrame = (uint32) input.readIntBigEndian();
109  bitsPerChannel = (uint32) input.readIntBigEndian();
110  }
111 
112  double sampleRate;
113  uint32 formatID;
114  uint32 formatFlags;
115  uint32 bytesPerPacket;
116  uint32 framesPerPacket;
117  uint32 channelsPerFrame;
118  uint32 bitsPerChannel;
119  };
120 
121  //==============================================================================
122  static StringPairArray parseUserDefinedChunk (InputStream& input, int64 size)
123  {
124  StringPairArray infoStrings;
125  auto originalPosition = input.getPosition();
126 
127  uint8 uuid[16];
128  input.read (uuid, sizeof (uuid));
129 
130  if (memcmp (uuid, "\x29\x81\x92\x73\xB5\xBF\x4A\xEF\xB7\x8D\x62\xD1\xEF\x90\xBB\x2C", 16) == 0)
131  {
132  auto numEntries = (uint32) input.readIntBigEndian();
133 
134  for (uint32 i = 0; i < numEntries && input.getPosition() < originalPosition + size; ++i)
135  {
136  String keyName = input.readString();
137  infoStrings.set (keyName, input.readString());
138  }
139  }
140 
141  input.setPosition (originalPosition + size);
142  return infoStrings;
143  }
144 
145  //==============================================================================
146  static StringPairArray parseMidiChunk (InputStream& input, int64 size)
147  {
148  auto originalPosition = input.getPosition();
149 
150  MemoryBlock midiBlock;
151  input.readIntoMemoryBlock (midiBlock, (ssize_t) size);
152  MemoryInputStream midiInputStream (midiBlock, false);
153 
154  StringPairArray midiMetadata;
155  MidiFile midiFile;
156 
157  if (midiFile.readFrom (midiInputStream))
158  {
159  midiMetadata.set (CoreAudioFormat::midiDataBase64, midiBlock.toBase64Encoding());
160 
161  findTempoEvents (midiFile, midiMetadata);
162  findTimeSigEvents (midiFile, midiMetadata);
163  findKeySigEvents (midiFile, midiMetadata);
164  }
165 
166  input.setPosition (originalPosition + size);
167  return midiMetadata;
168  }
169 
170  static void findTempoEvents (MidiFile& midiFile, StringPairArray& midiMetadata)
171  {
172  MidiMessageSequence tempoEvents;
173  midiFile.findAllTempoEvents (tempoEvents);
174 
175  auto numTempoEvents = tempoEvents.getNumEvents();
176  MemoryOutputStream tempoSequence;
177 
178  for (int i = 0; i < numTempoEvents; ++i)
179  {
180  auto tempo = getTempoFromTempoMetaEvent (tempoEvents.getEventPointer (i));
181 
182  if (tempo > 0.0)
183  {
184  if (i == 0)
185  midiMetadata.set (CoreAudioFormat::tempo, String (tempo));
186 
187  if (numTempoEvents > 1)
188  tempoSequence << String (tempo) << ',' << tempoEvents.getEventTime (i) << ';';
189  }
190  }
191 
192  if (tempoSequence.getDataSize() > 0)
193  midiMetadata.set ("tempo sequence", tempoSequence.toUTF8());
194  }
195 
196  static double getTempoFromTempoMetaEvent (MidiMessageSequence::MidiEventHolder* holder)
197  {
198  if (holder != nullptr)
199  {
200  auto& midiMessage = holder->message;
201 
202  if (midiMessage.isTempoMetaEvent())
203  {
204  auto tempoSecondsPerQuarterNote = midiMessage.getTempoSecondsPerQuarterNote();
205 
206  if (tempoSecondsPerQuarterNote > 0.0)
207  return 60.0 / tempoSecondsPerQuarterNote;
208  }
209  }
210 
211  return 0.0;
212  }
213 
214  static void findTimeSigEvents (MidiFile& midiFile, StringPairArray& midiMetadata)
215  {
216  MidiMessageSequence timeSigEvents;
217  midiFile.findAllTimeSigEvents (timeSigEvents);
218  auto numTimeSigEvents = timeSigEvents.getNumEvents();
219 
220  MemoryOutputStream timeSigSequence;
221 
222  for (int i = 0; i < numTimeSigEvents; ++i)
223  {
224  int numerator, denominator;
225  timeSigEvents.getEventPointer(i)->message.getTimeSignatureInfo (numerator, denominator);
226 
227  String timeSigString;
228  timeSigString << numerator << '/' << denominator;
229 
230  if (i == 0)
231  midiMetadata.set (CoreAudioFormat::timeSig, timeSigString);
232 
233  if (numTimeSigEvents > 1)
234  timeSigSequence << timeSigString << ',' << timeSigEvents.getEventTime (i) << ';';
235  }
236 
237  if (timeSigSequence.getDataSize() > 0)
238  midiMetadata.set ("time signature sequence", timeSigSequence.toUTF8());
239  }
240 
241  static void findKeySigEvents (MidiFile& midiFile, StringPairArray& midiMetadata)
242  {
243  MidiMessageSequence keySigEvents;
244  midiFile.findAllKeySigEvents (keySigEvents);
245  auto numKeySigEvents = keySigEvents.getNumEvents();
246 
247  MemoryOutputStream keySigSequence;
248 
249  for (int i = 0; i < numKeySigEvents; ++i)
250  {
251  auto& message (keySigEvents.getEventPointer (i)->message);
252  auto key = jlimit (0, 14, message.getKeySignatureNumberOfSharpsOrFlats() + 7);
253  bool isMajor = message.isKeySignatureMajorKey();
254 
255  static const char* majorKeys[] = { "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#" };
256  static const char* minorKeys[] = { "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "D#", "A#" };
257 
258  String keySigString (isMajor ? majorKeys[key]
259  : minorKeys[key]);
260 
261  if (! isMajor)
262  keySigString << 'm';
263 
264  if (i == 0)
265  midiMetadata.set (CoreAudioFormat::keySig, keySigString);
266 
267  if (numKeySigEvents > 1)
268  keySigSequence << keySigString << ',' << keySigEvents.getEventTime (i) << ';';
269  }
270 
271  if (keySigSequence.getDataSize() > 0)
272  midiMetadata.set ("key signature sequence", keySigSequence.toUTF8());
273  }
274 
275  //==============================================================================
276  static StringPairArray parseInformationChunk (InputStream& input)
277  {
278  StringPairArray infoStrings;
279  auto numEntries = (uint32) input.readIntBigEndian();
280 
281  for (uint32 i = 0; i < numEntries; ++i)
282  infoStrings.set (input.readString(), input.readString());
283 
284  return infoStrings;
285  }
286 
287  //==============================================================================
288  static bool read (InputStream& input, StringPairArray& metadataValues)
289  {
290  auto originalPos = input.getPosition();
291 
292  const FileHeader cafFileHeader (input);
293  const bool isCafFile = cafFileHeader.fileType == chunkName ("caff");
294 
295  if (isCafFile)
296  {
297  while (! input.isExhausted())
298  {
299  const ChunkHeader chunkHeader (input);
300 
301  if (chunkHeader.chunkType == chunkName ("desc"))
302  {
303  AudioDescriptionChunk audioDescriptionChunk (input);
304  }
305  else if (chunkHeader.chunkType == chunkName ("uuid"))
306  {
307  metadataValues.addArray (parseUserDefinedChunk (input, chunkHeader.chunkSize));
308  }
309  else if (chunkHeader.chunkType == chunkName ("data"))
310  {
311  // -1 signifies an unknown data size so the data has to be at the
312  // end of the file so we must have finished the header
313 
314  if (chunkHeader.chunkSize == -1)
315  break;
316 
317  input.setPosition (input.getPosition() + chunkHeader.chunkSize);
318  }
319  else if (chunkHeader.chunkType == chunkName ("midi"))
320  {
321  metadataValues.addArray (parseMidiChunk (input, chunkHeader.chunkSize));
322  }
323  else if (chunkHeader.chunkType == chunkName ("info"))
324  {
325  metadataValues.addArray (parseInformationChunk (input));
326  }
327  else
328  {
329  // we aren't decoding this chunk yet so just skip over it
330  input.setPosition (input.getPosition() + chunkHeader.chunkSize);
331  }
332  }
333  }
334 
335  input.setPosition (originalPos);
336 
337  return isCafFile;
338  }
339 };
340 
341 //==============================================================================
342 class CoreAudioReader : public AudioFormatReader
343 {
344 public:
345  CoreAudioReader (InputStream* inp) : AudioFormatReader (inp, coreAudioFormatName)
346  {
347  usesFloatingPointData = true;
348  bitsPerSample = 32;
349 
350  if (input != nullptr)
351  CoreAudioFormatMetatdata::read (*input, metadataValues);
352 
353  auto status = AudioFileOpenWithCallbacks (this,
354  &readCallback,
355  nullptr, // write needs to be null to avoid permissions errors
356  &getSizeCallback,
357  nullptr, // setSize needs to be null to avoid permissions errors
358  0, // AudioFileTypeID inFileTypeHint
359  &audioFileID);
360  if (status == noErr)
361  {
362  status = ExtAudioFileWrapAudioFileID (audioFileID, false, &audioFileRef);
363 
364  if (status == noErr)
365  {
366  AudioStreamBasicDescription sourceAudioFormat;
367  UInt32 audioStreamBasicDescriptionSize = sizeof (AudioStreamBasicDescription);
368  ExtAudioFileGetProperty (audioFileRef,
369  kExtAudioFileProperty_FileDataFormat,
370  &audioStreamBasicDescriptionSize,
371  &sourceAudioFormat);
372 
373  numChannels = sourceAudioFormat.mChannelsPerFrame;
374  sampleRate = sourceAudioFormat.mSampleRate;
375 
376  UInt32 sizeOfLengthProperty = sizeof (int64);
377  ExtAudioFileGetProperty (audioFileRef,
378  kExtAudioFileProperty_FileLengthFrames,
379  &sizeOfLengthProperty,
380  &lengthInSamples);
381 
382  HeapBlock<AudioChannelLayout> caLayout;
383  bool hasLayout = false;
384  UInt32 sizeOfLayout = 0, isWritable = 0;
385 
386  status = AudioFileGetPropertyInfo (audioFileID, kAudioFilePropertyChannelLayout, &sizeOfLayout, &isWritable);
387 
388  if (status == noErr && sizeOfLayout >= (sizeof (AudioChannelLayout) - sizeof (AudioChannelDescription)))
389  {
390  caLayout.malloc (1, static_cast<size_t> (sizeOfLayout));
391 
392  status = AudioFileGetProperty (audioFileID, kAudioFilePropertyChannelLayout,
393  &sizeOfLayout, caLayout.get());
394 
395  if (status == noErr)
396  {
397  auto fileLayout = CoreAudioLayouts::fromCoreAudio (*caLayout.get());
398 
399  if (fileLayout.size() == static_cast<int> (numChannels))
400  {
401  hasLayout = true;
402  channelSet = fileLayout;
403  }
404  }
405  }
406 
407  destinationAudioFormat.mSampleRate = sampleRate;
408  destinationAudioFormat.mFormatID = kAudioFormatLinearPCM;
409  destinationAudioFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian;
410  destinationAudioFormat.mBitsPerChannel = sizeof (float) * 8;
411  destinationAudioFormat.mChannelsPerFrame = numChannels;
412  destinationAudioFormat.mBytesPerFrame = sizeof (float);
413  destinationAudioFormat.mFramesPerPacket = 1;
414  destinationAudioFormat.mBytesPerPacket = destinationAudioFormat.mFramesPerPacket * destinationAudioFormat.mBytesPerFrame;
415 
416  status = ExtAudioFileSetProperty (audioFileRef,
417  kExtAudioFileProperty_ClientDataFormat,
418  sizeof (AudioStreamBasicDescription),
419  &destinationAudioFormat);
420  if (status == noErr)
421  {
422  bufferList.malloc (1, sizeof (AudioBufferList) + numChannels * sizeof (::AudioBuffer));
423  bufferList->mNumberBuffers = numChannels;
424  channelMap.malloc (numChannels);
425 
426  if (hasLayout && caLayout != nullptr)
427  {
428  auto caOrder = CoreAudioLayouts::getCoreAudioLayoutChannels (*caLayout);
429 
430  for (int i = 0; i < static_cast<int> (numChannels); ++i)
431  {
432  auto idx = channelSet.getChannelIndexForType (caOrder.getReference (i));
433  jassert (isPositiveAndBelow (idx, static_cast<int> (numChannels)));
434 
435  channelMap[i] = idx;
436  }
437  }
438  else
439  {
440  for (int i = 0; i < static_cast<int> (numChannels); ++i)
441  channelMap[i] = i;
442  }
443 
444  ok = true;
445  }
446  }
447  }
448  }
449 
450  ~CoreAudioReader() override
451  {
452  ExtAudioFileDispose (audioFileRef);
453  AudioFileClose (audioFileID);
454  }
455 
456  //==============================================================================
457  bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
458  int64 startSampleInFile, int numSamples) override
459  {
460  clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
461  startSampleInFile, numSamples, lengthInSamples);
462 
463  if (numSamples <= 0)
464  return true;
465 
466  if (lastReadPosition != startSampleInFile)
467  {
468  OSStatus status = ExtAudioFileSeek (audioFileRef, startSampleInFile);
469  if (status != noErr)
470  return false;
471 
472  lastReadPosition = startSampleInFile;
473  }
474 
475  while (numSamples > 0)
476  {
477  auto numThisTime = jmin (8192, numSamples);
478  auto numBytes = (size_t) numThisTime * sizeof (float);
479 
480  audioDataBlock.ensureSize (numBytes * numChannels, false);
481  auto* data = static_cast<float*> (audioDataBlock.getData());
482 
483  for (int j = (int) numChannels; --j >= 0;)
484  {
485  bufferList->mBuffers[j].mNumberChannels = 1;
486  bufferList->mBuffers[j].mDataByteSize = (UInt32) numBytes;
487  bufferList->mBuffers[j].mData = data;
488  data += numThisTime;
489  }
490 
491  auto numFramesToRead = (UInt32) numThisTime;
492  auto status = ExtAudioFileRead (audioFileRef, &numFramesToRead, bufferList);
493 
494  if (status != noErr)
495  return false;
496 
497  if (numFramesToRead == 0)
498  break;
499 
500  if ((int) numFramesToRead < numThisTime)
501  {
502  numThisTime = (int) numFramesToRead;
503  numBytes = (size_t) numThisTime * sizeof (float);
504  }
505 
506  for (int i = numDestChannels; --i >= 0;)
507  {
508  auto* dest = destSamples[(i < (int) numChannels ? channelMap[i] : i)];
509 
510  if (dest != nullptr)
511  {
512  if (i < (int) numChannels)
513  memcpy (dest + startOffsetInDestBuffer, bufferList->mBuffers[i].mData, numBytes);
514  else
515  zeromem (dest + startOffsetInDestBuffer, numBytes);
516  }
517  }
518 
519  startOffsetInDestBuffer += numThisTime;
520  numSamples -= numThisTime;
521  lastReadPosition += numThisTime;
522  }
523 
524  return true;
525  }
526 
527  AudioChannelSet getChannelLayout() override
528  {
529  if (channelSet.size() == static_cast<int> (numChannels))
530  return channelSet;
531 
533  }
534 
535  bool ok = false;
536 
537 private:
538  AudioFileID audioFileID;
539  ExtAudioFileRef audioFileRef;
540  AudioChannelSet channelSet;
541  AudioStreamBasicDescription destinationAudioFormat;
542  MemoryBlock audioDataBlock;
543  HeapBlock<AudioBufferList> bufferList;
544  int64 lastReadPosition = 0;
545  HeapBlock<int> channelMap;
546 
547  static SInt64 getSizeCallback (void* inClientData)
548  {
549  return static_cast<CoreAudioReader*> (inClientData)->input->getTotalLength();
550  }
551 
552  static OSStatus readCallback (void* inClientData, SInt64 inPosition, UInt32 requestCount,
553  void* buffer, UInt32* actualCount)
554  {
555  auto* reader = static_cast<CoreAudioReader*> (inClientData);
556  reader->input->setPosition (inPosition);
557  *actualCount = (UInt32) reader->input->read (buffer, (int) requestCount);
558  return noErr;
559  }
560 
561  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioReader)
562 };
563 
564 //==============================================================================
565 CoreAudioFormat::CoreAudioFormat()
566  : AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodecs())
567 {
568 }
569 
570 CoreAudioFormat::~CoreAudioFormat() {}
571 
572 Array<int> CoreAudioFormat::getPossibleSampleRates() { return {}; }
573 Array<int> CoreAudioFormat::getPossibleBitDepths() { return {}; }
574 
575 bool CoreAudioFormat::canDoStereo() { return true; }
576 bool CoreAudioFormat::canDoMono() { return true; }
577 
578 //==============================================================================
579 AudioFormatReader* CoreAudioFormat::createReaderFor (InputStream* sourceStream,
580  bool deleteStreamIfOpeningFails)
581 {
582  std::unique_ptr<CoreAudioReader> r (new CoreAudioReader (sourceStream));
583 
584  if (r->ok)
585  return r.release();
586 
587  if (! deleteStreamIfOpeningFails)
588  r->input = nullptr;
589 
590  return nullptr;
591 }
592 
593 AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*,
594  double /*sampleRateToUse*/,
595  unsigned int /*numberOfChannels*/,
596  int /*bitsPerSample*/,
597  const StringPairArray& /*metadataValues*/,
598  int /*qualityOptionIndex*/)
599 {
600  jassertfalse; // not yet implemented!
601  return nullptr;
602 }
603 
604 
605 //==============================================================================
606 //==============================================================================
607 #if JUCE_UNIT_TESTS
608 
609 #define DEFINE_CHANNEL_LAYOUT_DFL_ENTRY(x) CoreAudioChannelLayoutTag { x, #x, AudioChannelSet() }
610 #define DEFINE_CHANNEL_LAYOUT_TAG_ENTRY(x, y) CoreAudioChannelLayoutTag { x, #x, y }
611 
612 class CoreAudioLayoutsUnitTest : public UnitTest
613 {
614 public:
615  CoreAudioLayoutsUnitTest()
616  : UnitTest ("Core Audio Layout <-> JUCE channel layout conversion", UnitTestCategories::audio)
617  {}
618 
619  // some ambisonic tags which are not explicitly defined
620  enum
621  {
622  kAudioChannelLayoutTag_HOA_ACN_SN3D_0Order = (190U<<16) | 1,
623  kAudioChannelLayoutTag_HOA_ACN_SN3D_1Order = (190U<<16) | 4,
624  kAudioChannelLayoutTag_HOA_ACN_SN3D_2Order = (190U<<16) | 9,
625  kAudioChannelLayoutTag_HOA_ACN_SN3D_3Order = (190U<<16) | 16,
626  kAudioChannelLayoutTag_HOA_ACN_SN3D_4Order = (190U<<16) | 25,
627  kAudioChannelLayoutTag_HOA_ACN_SN3D_5Order = (190U<<16) | 36
628  };
629 
630  void runTest() override
631  {
632  auto& knownTags = getAllKnownLayoutTags();
633 
634  {
635  // Check that all known tags defined in CoreAudio SDK version 10.12.4 are known to JUCE
636  // Include all defined tags even if there are duplicates as Apple will sometimes change
637  // definitions
638  beginTest ("All CA tags handled");
639 
640  for (auto tagEntry : knownTags)
641  {
642  auto labels = CoreAudioLayouts::fromCoreAudio (tagEntry.tag);
643 
644  expect (! labels.isDiscreteLayout(), "Tag \"" + String (tagEntry.name) + "\" is not handled by JUCE");
645  }
646  }
647 
648  {
649  beginTest ("Number of speakers");
650 
651  for (auto tagEntry : knownTags)
652  {
653  auto labels = CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag);
654 
655  expect (labels.size() == (tagEntry.tag & 0xffff), "Tag \"" + String (tagEntry.name) + "\" has incorrect channel count");
656  }
657  }
658 
659  {
660  beginTest ("No duplicate speaker");
661 
662  for (auto tagEntry : knownTags)
663  {
664  auto labels = CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag);
665  labels.sort();
666 
667  for (int i = 0; i < (labels.size() - 1); ++i)
668  expect (labels.getReference (i) != labels.getReference (i + 1),
669  "Tag \"" + String (tagEntry.name) + "\" has the same speaker twice");
670  }
671  }
672 
673  {
674  beginTest ("CA speaker list and juce layouts are consistent");
675 
676  for (auto tagEntry : knownTags)
677  expect (AudioChannelSet::channelSetWithChannels (CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag))
678  == CoreAudioLayouts::fromCoreAudio (tagEntry.tag),
679  "Tag \"" + String (tagEntry.name) + "\" is not converted consistently by JUCE");
680  }
681 
682  {
683  beginTest ("AudioChannelSet documentation is correct");
684 
685  for (auto tagEntry : knownTags)
686  {
687  if (tagEntry.equivalentChannelSet.isDisabled())
688  continue;
689 
690  expect (CoreAudioLayouts::fromCoreAudio (tagEntry.tag) == tagEntry.equivalentChannelSet,
691  "Documentation for tag \"" + String (tagEntry.name) + "\" is incorrect");
692  }
693  }
694 
695  {
696  beginTest ("CA tag reverse conversion");
697 
698  for (auto tagEntry : knownTags)
699  {
700  if (tagEntry.equivalentChannelSet.isDisabled())
701  continue;
702 
703  expect (CoreAudioLayouts::toCoreAudio (tagEntry.equivalentChannelSet) == tagEntry.tag,
704  "Incorrect reverse conversion for tag \"" + String (tagEntry.name) + "\"");
705  }
706  }
707  }
708 
709 private:
710  struct CoreAudioChannelLayoutTag
711  {
712  AudioChannelLayoutTag tag;
713  const char* name;
714  AudioChannelSet equivalentChannelSet; /* referred to this in the AudioChannelSet documentation */
715  };
716 
717  //==============================================================================
718  const Array<CoreAudioChannelLayoutTag>& getAllKnownLayoutTags() const
719  {
720  static CoreAudioChannelLayoutTag tags[] = {
721  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Mono, AudioChannelSet::mono()),
722  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Stereo, AudioChannelSet::stereo()),
723  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_StereoHeadphones),
724  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MatrixStereo),
725  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MidSide),
726  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_XY),
727  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Binaural),
728  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Ambisonic_B_Format),
729  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Quadraphonic, AudioChannelSet::quadraphonic()),
730  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Pentagonal, AudioChannelSet::pentagonal()),
731  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Hexagonal, AudioChannelSet::hexagonal()),
732  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Octagonal, AudioChannelSet::octagonal()),
733  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Cube),
734  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_1_0),
735  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_2_0),
736  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_3_0_A, AudioChannelSet::createLCR()),
737  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_3_0_B),
738  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_4_0_A, AudioChannelSet::createLCRS()),
739  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_4_0_B),
740  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_5_0_A, AudioChannelSet::create5point0()),
741  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_0_B),
742  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_0_C),
743  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_0_D),
744  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_5_1_A, AudioChannelSet::create5point1()),
745  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_1_B),
746  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_1_C),
747  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_1_D),
748  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_6_1_A, AudioChannelSet::create6point1()),
749  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_7_1_A, AudioChannelSet::create7point1SDDS()),
750  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_7_1_B),
751  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_7_1_C, AudioChannelSet::create7point1()),
752  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Emagic_Default_7_1),
753  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_SMPTE_DTV),
754  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_1_0),
755  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_2_0),
756  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_ITU_2_1, AudioChannelSet::createLRS()),
757  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_2_2),
758  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_0),
759  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_1),
760  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_2),
761  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_2_1),
762  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_4_1),
763  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_0),
764  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_1),
765  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_2),
766  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_3),
767  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_4),
768  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_5),
769  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_6),
770  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_7),
771  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_8),
772  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_9),
773  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_10),
774  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_11),
775  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_12),
776  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_13),
777  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_14),
778  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_15),
779  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_16),
780  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_17),
781  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_18),
782  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_19),
783  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_20),
784  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_4),
785  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_5),
786  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_6),
787  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_8),
788  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_5_0),
789  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_AudioUnit_6_0, AudioChannelSet::create6point0()),
790  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_AudioUnit_7_0, AudioChannelSet::create7point0()),
791  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_AudioUnit_7_0_Front, AudioChannelSet::create7point0SDDS()),
792  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_5_1),
793  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_6_1),
794  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_7_1),
795  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_7_1_Front),
796  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_3_0),
797  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_Quadraphonic),
798  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_4_0),
799  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_5_0),
800  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_5_1),
801  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_6_0),
802  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_6_1),
803  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_7_0),
804  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_7_1),
805  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_7_1_B),
806  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_7_1_C),
807  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_Octagonal),
808  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_TMH_10_2_std),
809  // DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_TMH_10_2_full), no indication on how to handle this tag
810  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_1_0_1),
811  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_3_0),
812  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_3_1),
813  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_3_0_1),
814  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_2_1_1),
815  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_3_1_1),
816  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC_6_0_A),
817  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC_7_0_A),
818  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_6_1_A),
819  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_6_1_B),
820  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_6_1_C),
821  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_A),
822  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_B),
823  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_C),
824  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_D),
825  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_E),
826  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_F),
827  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_G),
828  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_H),
829  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_3_1),
830  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_4_1),
831  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_DTS_6_0_A, AudioChannelSet::create6point0Music()),
832  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_0_B),
833  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_0_C),
834  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_DTS_6_1_A, AudioChannelSet::create6point1Music()),
835  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_B),
836  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_C),
837  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_7_0),
838  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_7_1),
839  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_0_A),
840  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_0_B),
841  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_1_A),
842  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_1_B),
843  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_D),
844  DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_D),
845  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_0Order, AudioChannelSet::ambisonic (0)),
846  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_1Order, AudioChannelSet::ambisonic (1)),
847  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_2Order, AudioChannelSet::ambisonic (2)),
848  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_3Order, AudioChannelSet::ambisonic (3)),
849  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_4Order, AudioChannelSet::ambisonic (4)),
850  DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_5Order, AudioChannelSet::ambisonic (5))
851  };
852  static Array<CoreAudioChannelLayoutTag> knownTags (tags, sizeof (tags) / sizeof (CoreAudioChannelLayoutTag));
853 
854  return knownTags;
855  }
856 };
857 
858 static CoreAudioLayoutsUnitTest coreAudioLayoutsUnitTest;
859 
860 #endif
861 
862 } // namespace juce
863 
864 #endif
static AudioChannelSet JUCE_CALLTYPE create6point1()
static AudioChannelSet JUCE_CALLTYPE create6point1Music()
static AudioChannelSet JUCE_CALLTYPE pentagonal()
static AudioChannelSet JUCE_CALLTYPE create6point0Music()
static AudioChannelSet JUCE_CALLTYPE create7point1SDDS()
static JUCE_CONSTEXPR uint32 bigEndianInt(const void *bytes) noexcept
static AudioChannelSet JUCE_CALLTYPE channelSetWithChannels(const Array< ChannelType > &)
static AudioChannelSet JUCE_CALLTYPE createLCRS()
static AudioChannelSet JUCE_CALLTYPE ambisonic(int order=1)
static AudioChannelSet JUCE_CALLTYPE mono()
static AudioChannelSet JUCE_CALLTYPE create7point0SDDS()
static AudioChannelSet JUCE_CALLTYPE create5point1()
static AudioChannelSet JUCE_CALLTYPE create5point0()
static AudioChannelSet JUCE_CALLTYPE create7point1()
static AudioChannelSet JUCE_CALLTYPE createLRS()
static AudioChannelSet JUCE_CALLTYPE createLCR()
static AudioChannelSet JUCE_CALLTYPE create6point0()
static AudioChannelSet JUCE_CALLTYPE stereo()
static AudioChannelSet JUCE_CALLTYPE octagonal()
static AudioChannelSet JUCE_CALLTYPE create7point0()
static AudioChannelSet JUCE_CALLTYPE hexagonal()
static AudioChannelSet JUCE_CALLTYPE quadraphonic()
virtual AudioChannelSet getChannelLayout()