OpenShot Library | libopenshot-audio 0.2.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
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//==============================================================================
67{
68 static uint32 chunkName (const char* const name) noexcept { return ByteOrder::bigEndianInt (name); }
69
70 //==============================================================================
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 //==============================================================================
87 {
89 {
90 chunkType = (uint32) input.readIntBigEndian();
91 chunkSize = (int64) input.readInt64BigEndian();
92 }
93
94 uint32 chunkType;
95 int64 chunkSize;
96 };
97
98 //==============================================================================
100 {
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//==============================================================================
343{
344public:
345 CoreAudioReader (InputStream* inp) : AudioFormatReader (inp, coreAudioFormatName)
346 {
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 permisisions errors
356 &getSizeCallback,
357 nullptr, // setSize needs to be null to avoid permisisions 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,
381
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 = sizeof (float) * (size_t) numThisTime;
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 for (int i = numDestChannels; --i >= 0;)
498 {
499 auto* dest = destSamples[(i < (int) numChannels ? channelMap[i] : i)];
500
501 if (dest != nullptr)
502 {
503 if (i < (int) numChannels)
504 memcpy (dest + startOffsetInDestBuffer, bufferList->mBuffers[i].mData, numBytes);
505 else
506 zeromem (dest + startOffsetInDestBuffer, numBytes);
507 }
508 }
509
510 startOffsetInDestBuffer += numThisTime;
511 numSamples -= numThisTime;
512 lastReadPosition += numThisTime;
513 }
514
515 return true;
516 }
517
519 {
520 if (channelSet.size() == static_cast<int> (numChannels))
521 return channelSet;
522
524 }
525
526 bool ok = false;
527
528private:
529 AudioFileID audioFileID;
530 ExtAudioFileRef audioFileRef;
531 AudioChannelSet channelSet;
532 AudioStreamBasicDescription destinationAudioFormat;
533 MemoryBlock audioDataBlock;
535 int64 lastReadPosition = 0;
536 HeapBlock<int> channelMap;
537
538 static SInt64 getSizeCallback (void* inClientData)
539 {
540 return static_cast<CoreAudioReader*> (inClientData)->input->getTotalLength();
541 }
542
543 static OSStatus readCallback (void* inClientData, SInt64 inPosition, UInt32 requestCount,
544 void* buffer, UInt32* actualCount)
545 {
546 auto* reader = static_cast<CoreAudioReader*> (inClientData);
547 reader->input->setPosition (inPosition);
548 *actualCount = (UInt32) reader->input->read (buffer, (int) requestCount);
549 return noErr;
550 }
551
552 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioReader)
553};
554
555//==============================================================================
557 : AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodecs())
558{
559}
560
562
565
566bool CoreAudioFormat::canDoStereo() { return true; }
567bool CoreAudioFormat::canDoMono() { return true; }
568
569//==============================================================================
571 bool deleteStreamIfOpeningFails)
572{
573 std::unique_ptr<CoreAudioReader> r (new CoreAudioReader (sourceStream));
574
575 if (r->ok)
576 return r.release();
577
578 if (! deleteStreamIfOpeningFails)
579 r->input = nullptr;
580
581 return nullptr;
582}
583
585 double /*sampleRateToUse*/,
586 unsigned int /*numberOfChannels*/,
587 int /*bitsPerSample*/,
588 const StringPairArray& /*metadataValues*/,
589 int /*qualityOptionIndex*/)
590{
591 jassertfalse; // not yet implemented!
592 return nullptr;
593}
594
595//==============================================================================
596// Unit tests for Core Audio layout conversions
597//==============================================================================
598#if JUCE_UNIT_TESTS
599
600#define DEFINE_CHANNEL_LAYOUT_DFL_ENTRY(x) CoreAudioChannelLayoutTag { x, #x, AudioChannelSet() }
601#define DEFINE_CHANNEL_LAYOUT_TAG_ENTRY(x, y) CoreAudioChannelLayoutTag { x, #x, y }
602
603class CoreAudioLayoutsUnitTest : public UnitTest
604{
605public:
606 CoreAudioLayoutsUnitTest() : UnitTest ("Core Audio Layout <-> JUCE channel layout conversion", "Audio") {}
607
608 // some ambisonic tags which are not explicitely defined
609 enum
610 {
611 kAudioChannelLayoutTag_HOA_ACN_SN3D_0Order = (190U<<16) | 1,
612 kAudioChannelLayoutTag_HOA_ACN_SN3D_1Order = (190U<<16) | 4,
613 kAudioChannelLayoutTag_HOA_ACN_SN3D_2Order = (190U<<16) | 9,
614 kAudioChannelLayoutTag_HOA_ACN_SN3D_3Order = (190U<<16) | 16,
615 kAudioChannelLayoutTag_HOA_ACN_SN3D_4Order = (190U<<16) | 25,
616 kAudioChannelLayoutTag_HOA_ACN_SN3D_5Order = (190U<<16) | 36
617 };
618
619 void runTest() override
620 {
621 auto& knownTags = getAllKnownLayoutTags();
622
623 {
624 // Check that all known tags defined in CoreAudio SDK version 10.12.4 are known to JUCE
625 // Include all defined tags even if there are duplicates as Apple will sometimes change
626 // definitions
627 beginTest ("All CA tags handled");
628
629 for (auto tagEntry : knownTags)
630 {
631 auto labels = CoreAudioLayouts::fromCoreAudio (tagEntry.tag);
632
633 expect (! labels.isDiscreteLayout(), "Tag \"" + String (tagEntry.name) + "\" is not handled by JUCE");
634 }
635 }
636
637 {
638 beginTest ("Number of speakers");
639
640 for (auto tagEntry : knownTags)
641 {
642 auto labels = CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag);
643
644 expect (labels.size() == (tagEntry.tag & 0xffff), "Tag \"" + String (tagEntry.name) + "\" has incorrect channel count");
645 }
646 }
647
648 {
649 beginTest ("No duplicate speaker");
650
651 for (auto tagEntry : knownTags)
652 {
653 auto labels = CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag);
654 labels.sort();
655
656 for (int i = 0; i < (labels.size() - 1); ++i)
657 expect (labels.getReference (i) != labels.getReference (i + 1),
658 "Tag \"" + String (tagEntry.name) + "\" has the same speaker twice");
659 }
660 }
661
662 {
663 beginTest ("CA speaker list and juce layouts are consistent");
664
665 for (auto tagEntry : knownTags)
666 expect (AudioChannelSet::channelSetWithChannels (CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag))
667 == CoreAudioLayouts::fromCoreAudio (tagEntry.tag),
668 "Tag \"" + String (tagEntry.name) + "\" is not converted consistently by JUCE");
669 }
670
671 {
672 beginTest ("AudioChannelSet documentation is correct");
673
674 for (auto tagEntry : knownTags)
675 {
676 if (tagEntry.equivalentChannelSet.isDisabled())
677 continue;
678
679 expect (CoreAudioLayouts::fromCoreAudio (tagEntry.tag) == tagEntry.equivalentChannelSet,
680 "Documentation for tag \"" + String (tagEntry.name) + "\" is incorrect");
681 }
682 }
683
684 {
685 beginTest ("CA tag reverse conversion");
686
687 for (auto tagEntry : knownTags)
688 {
689 if (tagEntry.equivalentChannelSet.isDisabled())
690 continue;
691
692 expect (CoreAudioLayouts::toCoreAudio (tagEntry.equivalentChannelSet) == tagEntry.tag,
693 "Incorrect reverse conversion for tag \"" + String (tagEntry.name) + "\"");
694 }
695 }
696 }
697
698private:
699 struct CoreAudioChannelLayoutTag
700 {
701 AudioChannelLayoutTag tag;
702 const char* name;
703 AudioChannelSet equivalentChannelSet; /* referred to this in the AudioChannelSet documentation */
704 };
705
706 //==============================================================================
707 const Array<CoreAudioChannelLayoutTag>& getAllKnownLayoutTags() const
708 {
709 static CoreAudioChannelLayoutTag tags[] = {
710 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Mono, AudioChannelSet::mono()),
711 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Stereo, AudioChannelSet::stereo()),
712 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_StereoHeadphones),
713 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MatrixStereo),
714 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MidSide),
715 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_XY),
716 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Binaural),
717 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Ambisonic_B_Format),
718 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Quadraphonic, AudioChannelSet::quadraphonic()),
719 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Pentagonal, AudioChannelSet::pentagonal()),
720 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Hexagonal, AudioChannelSet::hexagonal()),
721 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Octagonal, AudioChannelSet::octagonal()),
722 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Cube),
723 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_1_0),
724 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_2_0),
725 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_3_0_A, AudioChannelSet::createLCR()),
726 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_3_0_B),
727 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_4_0_A, AudioChannelSet::createLCRS()),
728 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_4_0_B),
729 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_5_0_A, AudioChannelSet::create5point0()),
730 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_0_B),
731 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_0_C),
732 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_0_D),
733 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_5_1_A, AudioChannelSet::create5point1()),
734 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_1_B),
735 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_1_C),
736 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_5_1_D),
737 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_6_1_A, AudioChannelSet::create6point1()),
738 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_7_1_A, AudioChannelSet::create7point1SDDS()),
739 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MPEG_7_1_B),
740 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_MPEG_7_1_C, AudioChannelSet::create7point1()),
741 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Emagic_Default_7_1),
742 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_SMPTE_DTV),
743 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_1_0),
744 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_2_0),
745 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_ITU_2_1, AudioChannelSet::createLRS()),
746 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_2_2),
747 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_0),
748 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_1),
749 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_2),
750 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_2_1),
751 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_ITU_3_4_1),
752 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_0),
753 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_1),
754 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_2),
755 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_3),
756 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_4),
757 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_5),
758 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_6),
759 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_7),
760 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_8),
761 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_9),
762 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_10),
763 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_11),
764 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_12),
765 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_13),
766 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_14),
767 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_15),
768 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_16),
769 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_17),
770 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_18),
771 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_19),
772 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DVD_20),
773 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_4),
774 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_5),
775 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_6),
776 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_8),
777 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_5_0),
778 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_AudioUnit_6_0, AudioChannelSet::create6point0()),
779 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_AudioUnit_7_0, AudioChannelSet::create7point0()),
780 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_AudioUnit_7_0_Front, AudioChannelSet::create7point0SDDS()),
781 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_5_1),
782 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_6_1),
783 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_7_1),
784 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AudioUnit_7_1_Front),
785 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_3_0),
786 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_Quadraphonic),
787 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_4_0),
788 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_5_0),
789 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_5_1),
790 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_6_0),
791 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_6_1),
792 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_7_0),
793 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_7_1),
794 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_7_1_B),
795 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_7_1_C),
796 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AAC_Octagonal),
797 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_TMH_10_2_std),
798 // DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_TMH_10_2_full), no indication on how to handle this tag
799 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_1_0_1),
800 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_3_0),
801 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_3_1),
802 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_3_0_1),
803 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_2_1_1),
804 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_AC3_3_1_1),
805 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC_6_0_A),
806 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC_7_0_A),
807 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_6_1_A),
808 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_6_1_B),
809 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_6_1_C),
810 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_A),
811 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_B),
812 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_C),
813 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_D),
814 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_E),
815 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_F),
816 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_G),
817 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_EAC3_7_1_H),
818 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_3_1),
819 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_4_1),
820 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_DTS_6_0_A, AudioChannelSet::create6point0Music()),
821 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_0_B),
822 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_0_C),
823 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_DTS_6_1_A, AudioChannelSet::create6point1Music()),
824 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_B),
825 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_C),
826 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_7_0),
827 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_7_1),
828 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_0_A),
829 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_0_B),
830 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_1_A),
831 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_1_B),
832 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_D),
833 DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_D),
834 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_0Order, AudioChannelSet::ambisonic (0)),
835 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_1Order, AudioChannelSet::ambisonic (1)),
836 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_2Order, AudioChannelSet::ambisonic (2)),
837 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_3Order, AudioChannelSet::ambisonic (3)),
838 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_4Order, AudioChannelSet::ambisonic (4)),
839 DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_5Order, AudioChannelSet::ambisonic (5))
840 };
841 static Array<CoreAudioChannelLayoutTag> knownTags (tags, sizeof (tags) / sizeof (CoreAudioChannelLayoutTag));
842
843 return knownTags;
844 }
845};
846
847static CoreAudioLayoutsUnitTest coreAudioLayoutsUnitTest;
848
849#endif
850
851} // namespace juce
852
853#endif
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:60
Represents a set of audio channel types.
int size() const noexcept
Returns the number of channels in the set.
int getChannelIndexForType(ChannelType type) const noexcept
Returns the index for a particular channel-type.
Reads samples from an audio file stream.
InputStream * input
The input stream, for use by subclasses.
bool usesFloatingPointData
Indicates whether the data is floating-point or fixed.
StringPairArray metadataValues
A set of metadata values that the reader has pulled out of the stream.
static void clearSamplesBeyondAvailableLength(int **destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int &numSamples, int64 fileLengthInSamples)
Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie beyond the end of...
int64 lengthInSamples
The total number of samples in the audio stream.
double sampleRate
The sample-rate of the stream.
virtual AudioChannelSet getChannelLayout()
Get the channel layout of the audio stream.
unsigned int bitsPerSample
The number of bits per sample, e.g.
unsigned int numChannels
The total number of channels in the audio stream.
Writes samples to an audio file stream.
Subclasses of AudioFormat are used to read and write different audio file formats.
static JUCE_CONSTEXPR uint32 bigEndianInt(const void *bytes) noexcept
Turns 4 bytes into a big-endian integer.
bool canDoMono() override
Returns true if the format can do 1-channel audio.
CoreAudioFormat()
Creates a format object.
~CoreAudioFormat() override
Destructor.
static const char *const midiDataBase64
Metadata property name used when reading a caf file with a MIDI chunk.
bool canDoStereo() override
Returns true if the format can do 2-channel audio.
AudioFormatReader * createReaderFor(InputStream *, bool deleteStreamIfOpeningFails) override
Tries to create an object that can read from a stream containing audio data in this format.
Array< int > getPossibleSampleRates() override
Returns a set of sample rates that the format can read and write.
static const char *const tempo
Metadata property name used when reading a caf file with tempo information.
static const char *const timeSig
Metadata property name used when reading a caf file time signature information.
static const char *const keySig
Metadata property name used when reading a caf file time signature information.
AudioFormatWriter * createWriterFor(OutputStream *, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray &metadataValues, int qualityOptionIndex) override
Tries to create an object that can write to a stream with this audio format.
Array< int > getPossibleBitDepths() override
Returns a set of bit depths that the format can read and write.
AudioChannelSet getChannelLayout() override
Get the channel layout of the audio stream.
bool readSamples(int **destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override
Subclasses must implement this method to perform the low-level read operation.
Very simple container class to hold a pointer to some data on the heap.
void malloc(SizeType newNumElements, size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory.
ElementType * get() const noexcept
Returns a raw pointer to the allocated data.
The base class for streams that read data.
virtual int64 getPosition()=0
Returns the offset of the next byte that will be read from the stream.
virtual int readIntBigEndian()
Reads four bytes from the stream as a big-endian 32-bit value.
virtual bool setPosition(int64 newPosition)=0
Tries to move the current read position of the stream.
virtual int64 getTotalLength()=0
Returns the total number of bytes available for reading in this stream.
virtual double readDoubleBigEndian()
Reads eight bytes as a 64-bit floating point value.
virtual int64 readInt64BigEndian()
Reads eight bytes from the stream as a big-endian 64-bit value.
virtual short readShortBigEndian()
Reads two bytes from the stream as a little-endian 16-bit value.
virtual int read(void *destBuffer, int maxBytesToRead)=0
Reads some data from the stream into a memory buffer.
virtual String readString()
Reads a zero-terminated UTF-8 string from the stream.
A class to hold a resizable block of raw data.
void ensureSize(const size_t minimumSize, bool initialiseNewSpaceToZero=false)
Increases the block's size only if it's smaller than a given size.
void * getData() const noexcept
Returns a void pointer to the data.
The base class for streams that write data to some kind of destination.
A container for holding a set of strings which are keyed by another string.
void set(const String &key, const String &value)
Adds or amends a key/value pair.
The JUCE String class!
Definition juce_String.h:43
static String fromCFString(CFStringRef cfString)
OSX ONLY - Creates a String from an OSX CFString.
This is a base class for classes that perform a unit test.