OpenShot Audio Library | OpenShotAudio: juce_CoreAudioFormat.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.3.1

Loading...
Searching...
No Matches
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
31namespace juce
32{
33
34//==============================================================================
35namespace
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//==============================================================================
60const char* const CoreAudioFormat::midiDataBase64 = "midiDataBase64";
61const char* const CoreAudioFormat::tempo = "tempo";
62const char* const CoreAudioFormat::timeSig = "time signature";
63const char* const CoreAudioFormat::keySig = "key signature";
64
65//==============================================================================
66struct 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//==============================================================================
342class CoreAudioReader : public AudioFormatReader
343{
344public:
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
532 return AudioFormatReader::getChannelLayout();
533 }
534
535 bool ok = false;
536
537private:
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//==============================================================================
565CoreAudioFormat::CoreAudioFormat()
566 : AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodecs())
567{
568}
569
570CoreAudioFormat::~CoreAudioFormat() {}
571
572Array<int> CoreAudioFormat::getPossibleSampleRates() { return {}; }
573Array<int> CoreAudioFormat::getPossibleBitDepths() { return {}; }
574
575bool CoreAudioFormat::canDoStereo() { return true; }
576bool CoreAudioFormat::canDoMono() { return true; }
577
578//==============================================================================
579AudioFormatReader* 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
593AudioFormatWriter* 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
612class CoreAudioLayoutsUnitTest : public UnitTest
613{
614public:
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
709private:
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
858static CoreAudioLayoutsUnitTest coreAudioLayoutsUnitTest;
859
860#endif
861
862} // namespace juce
863
864#endif