Massive UI work
[juce-lv2.git] / juce / source / src / audio / audio_file_formats / juce_AiffAudioFormat.cpp
blobb8ccdbecb475f3fbe6bae2c60833abca9609a2b9
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../core/juce_StandardHeader.h"
28 BEGIN_JUCE_NAMESPACE
30 #include "juce_AiffAudioFormat.h"
31 #include "../../io/streams/juce_BufferedInputStream.h"
32 #include "../../io/streams/juce_MemoryOutputStream.h"
33 #include "../../core/juce_PlatformUtilities.h"
34 #include "../../text/juce_LocalisedStrings.h"
37 //==============================================================================
38 static const char* const aiffFormatName = "AIFF file";
39 static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 };
41 //==============================================================================
42 namespace AiffFileHelpers
44 inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); }
46 #if JUCE_MSVC
47 #pragma pack (push, 1)
48 #define PACKED
49 #elif JUCE_GCC
50 #define PACKED __attribute__((packed))
51 #else
52 #define PACKED
53 #endif
55 //==============================================================================
56 struct InstChunk
58 struct Loop
60 uint16 type; // these are different in AIFF and WAV
61 uint16 startIdentifier;
62 uint16 endIdentifier;
63 } PACKED;
65 int8 baseNote;
66 int8 detune;
67 int8 lowNote;
68 int8 highNote;
69 int8 lowVelocity;
70 int8 highVelocity;
71 int16 gain;
72 Loop sustainLoop;
73 Loop releaseLoop;
75 void copyTo (StringPairArray& values) const
77 values.set ("MidiUnityNote", String (baseNote));
78 values.set ("Detune", String (detune));
80 values.set ("LowNote", String (lowNote));
81 values.set ("HighNote", String (highNote));
82 values.set ("LowVelocity", String (lowVelocity));
83 values.set ("HighVelocity", String (highVelocity));
85 values.set ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain)));
87 values.set ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more
88 values.set ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type)));
89 values.set ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier)));
90 values.set ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier)));
91 values.set ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type)));
92 values.set ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier)));
93 values.set ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier)));
96 static void create (MemoryBlock& block, const StringPairArray& values)
98 if (values.getAllKeys().contains ("MidiUnityNote", true))
100 block.setSize ((sizeof (InstChunk) + 3) & ~3, true);
101 InstChunk* const inst = static_cast <InstChunk*> (block.getData());
103 inst->baseNote = (int8) values.getValue ("MidiUnityNote", "60").getIntValue();
104 inst->detune = (int8) values.getValue ("Detune", "0").getIntValue();
105 inst->lowNote = (int8) values.getValue ("LowNote", "0").getIntValue();
106 inst->highNote = (int8) values.getValue ("HighNote", "127").getIntValue();
107 inst->lowVelocity = (int8) values.getValue ("LowVelocity", "1").getIntValue();
108 inst->highVelocity = (int8) values.getValue ("HighVelocity", "127").getIntValue();
109 inst->gain = (int16) ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Gain", "0").getIntValue());
111 inst->sustainLoop.type = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0Type", "0").getIntValue());
112 inst->sustainLoop.startIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0StartIdentifier", "0").getIntValue());
113 inst->sustainLoop.endIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0EndIdentifier", "0").getIntValue());
114 inst->releaseLoop.type = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1Type", "0").getIntValue());
115 inst->releaseLoop.startIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1StartIdentifier", "0").getIntValue());
116 inst->releaseLoop.endIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1EndIdentifier", "0").getIntValue());
120 } PACKED;
122 #if JUCE_MSVC
123 #pragma pack (pop)
124 #endif
126 #undef PACKED
128 //==============================================================================
129 namespace MarkChunk
131 bool metaDataContainsZeroIdentifiers (const StringPairArray& values)
133 // (zero cue identifiers are valid for WAV but not for AIFF)
134 const String cueString ("Cue");
135 const String noteString ("CueNote");
136 const String identifierString ("Identifier");
138 const StringArray& keys = values.getAllKeys();
140 for (int i = 0; i < keys.size(); ++i)
142 const String key (keys[i]);
144 if (key.startsWith (noteString))
145 continue; // zero identifier IS valid in a COMT chunk
147 if (key.startsWith (cueString) && key.contains (identifierString))
149 const int value = values.getValue (key, "-1").getIntValue();
151 if (value == 0)
152 return true;
156 return false;
159 void create (MemoryBlock& block, const StringPairArray& values)
161 const int numCues = values.getValue ("NumCuePoints", "0").getIntValue();
163 if (numCues > 0)
165 MemoryOutputStream out (block, false);
167 out.writeShortBigEndian ((short) numCues);
169 const int numCueLabels = values.getValue ("NumCueLabels", "0").getIntValue();
170 const int idOffset = metaDataContainsZeroIdentifiers (values) ? 1 : 0; // can't have zero IDs in AIFF
172 #if JUCE_DEBUG
173 Array<int> identifiers;
174 #endif
176 for (int i = 0; i < numCues; ++i)
178 const String prefixCue ("Cue" + String (i));
179 const int identifier = idOffset + values.getValue (prefixCue + "Identifier", "1").getIntValue();
181 #if JUCE_DEBUG
182 jassert (! identifiers.contains (identifier));
183 identifiers.add (identifier);
184 #endif
186 const int offset = values.getValue (prefixCue + "Offset", "0").getIntValue();
187 String label ("CueLabel" + String (i));
189 for (int labelIndex = 0; labelIndex < numCueLabels; ++labelIndex)
191 const String prefixLabel ("CueLabel" + String (labelIndex));
192 const int labelIdentifier = idOffset + values.getValue (prefixLabel + "Identifier", "1").getIntValue();
194 if (labelIdentifier == identifier)
196 label = values.getValue (prefixLabel + "Text", label);
197 break;
201 out.writeShortBigEndian ((short) identifier);
202 out.writeIntBigEndian (offset);
204 const int labelLength = jmin (254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring
205 out.writeByte ((char) labelLength + 1);
206 out.write (label.toUTF8(), labelLength);
207 out.writeByte (0);
210 if ((out.getDataSize() & 1) != 0)
211 out.writeByte (0);
216 //==============================================================================
217 namespace COMTChunk
219 void create (MemoryBlock& block, const StringPairArray& values)
221 const int numNotes = values.getValue ("NumCueNotes", "0").getIntValue();
223 if (numNotes > 0)
225 MemoryOutputStream out (block, false);
226 out.writeShortBigEndian ((short) numNotes);
228 for (int i = 0; i < numNotes; ++i)
230 const String prefix ("CueNote" + String (i));
232 out.writeIntBigEndian (values.getValue (prefix + "TimeStamp", "0").getIntValue());
233 out.writeShortBigEndian ((short) values.getValue (prefix + "Identifier", "0").getIntValue());
235 const String comment (values.getValue (prefix + "Text", String::empty));
236 out.write (comment.toUTF8(), jmin (comment.getNumBytesAsUTF8(), 65534));
237 out.writeByte (0);
239 if ((out.getDataSize() & 1) != 0)
240 out.writeByte (0);
248 //==============================================================================
249 class AiffAudioFormatReader : public AudioFormatReader
251 public:
252 int bytesPerFrame;
253 int64 dataChunkStart;
254 bool littleEndian;
256 //==============================================================================
257 AiffAudioFormatReader (InputStream* in)
258 : AudioFormatReader (in, TRANS (aiffFormatName))
260 using namespace AiffFileHelpers;
262 if (input->readInt() == chunkName ("FORM"))
264 const int len = input->readIntBigEndian();
265 const int64 end = input->getPosition() + len;
267 const int nextType = input->readInt();
268 if (nextType == chunkName ("AIFF") || nextType == chunkName ("AIFC"))
270 bool hasGotVer = false;
271 bool hasGotData = false;
272 bool hasGotType = false;
274 while (input->getPosition() < end)
276 const int type = input->readInt();
277 const uint32 length = (uint32) input->readIntBigEndian();
278 const int64 chunkEnd = input->getPosition() + length;
280 if (type == chunkName ("FVER"))
282 hasGotVer = true;
284 const int ver = input->readIntBigEndian();
285 if (ver != 0 && ver != (int) 0xa2805140)
286 break;
288 else if (type == chunkName ("COMM"))
290 hasGotType = true;
292 numChannels = (unsigned int) input->readShortBigEndian();
293 lengthInSamples = input->readIntBigEndian();
294 bitsPerSample = input->readShortBigEndian();
295 bytesPerFrame = (numChannels * bitsPerSample) >> 3;
297 unsigned char sampleRateBytes[10];
298 input->read (sampleRateBytes, 10);
299 const int byte0 = sampleRateBytes[0];
301 if ((byte0 & 0x80) != 0
302 || byte0 <= 0x3F || byte0 > 0x40
303 || (byte0 == 0x40 && sampleRateBytes[1] > 0x1C))
304 break;
306 unsigned int sampRate = ByteOrder::bigEndianInt (sampleRateBytes + 2);
307 sampRate >>= (16414 - ByteOrder::bigEndianShort (sampleRateBytes));
308 sampleRate = (int) sampRate;
310 if (length <= 18)
312 // some types don't have a chunk large enough to include a compression
313 // type, so assume it's just big-endian pcm
314 littleEndian = false;
316 else
318 const int compType = input->readInt();
320 if (compType == chunkName ("NONE") || compType == chunkName ("twos"))
322 littleEndian = false;
324 else if (compType == chunkName ("sowt"))
326 littleEndian = true;
328 else
330 sampleRate = 0;
331 break;
335 else if (type == chunkName ("SSND"))
337 hasGotData = true;
339 const int offset = input->readIntBigEndian();
340 dataChunkStart = input->getPosition() + 4 + offset;
341 lengthInSamples = (bytesPerFrame > 0) ? jmin (lengthInSamples, (int64) (length / bytesPerFrame)) : 0;
343 else if (type == chunkName ("MARK"))
345 const uint16 numCues = (uint16) input->readShortBigEndian();
347 // these two are always the same for AIFF-read files
348 metadataValues.set ("NumCuePoints", String (numCues));
349 metadataValues.set ("NumCueLabels", String (numCues));
351 for (uint16 i = 0; i < numCues; ++i)
353 uint16 identifier = (uint16) input->readShortBigEndian();
354 uint32 offset = (uint32) input->readIntBigEndian();
355 uint8 stringLength = (uint8) input->readByte();
356 MemoryBlock textBlock;
357 input->readIntoMemoryBlock (textBlock, stringLength);
359 // if the stringLength is even then read one more byte as the
360 // string needs to be an even number of bytes INCLUDING the
361 // leading length character in the pascal string
362 if ((stringLength & 1) == 0)
363 input->readByte();
365 const String prefixCue ("Cue" + String (i));
366 metadataValues.set (prefixCue + "Identifier", String (identifier));
367 metadataValues.set (prefixCue + "Offset", String (offset));
369 const String prefixLabel ("CueLabel" + String (i));
370 metadataValues.set (prefixLabel + "Identifier", String (identifier));
371 metadataValues.set (prefixLabel + "Text", textBlock.toString());
374 else if (type == chunkName ("COMT"))
376 const uint16 numNotes = (uint16) input->readShortBigEndian();
377 metadataValues.set ("NumCueNotes", String (numNotes));
379 for (uint16 i = 0; i < numNotes; ++i)
381 uint32 timestamp = (uint32) input->readIntBigEndian();
382 uint16 identifier = (uint16) input->readShortBigEndian(); // may be zero in this case
383 uint16 stringLength = (uint16) input->readShortBigEndian();
385 MemoryBlock textBlock;
386 input->readIntoMemoryBlock (textBlock, stringLength + (stringLength & 1));
388 const String prefix ("CueNote" + String (i));
389 metadataValues.set (prefix + "TimeStamp", String (timestamp));
390 metadataValues.set (prefix + "Identifier", String (identifier));
391 metadataValues.set (prefix + "Text", textBlock.toString());
394 else if (type == chunkName ("INST"))
396 HeapBlock <InstChunk> inst;
397 inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
398 input->read (inst, length);
399 inst->copyTo (metadataValues);
401 else if ((hasGotVer && hasGotData && hasGotType)
402 || chunkEnd < input->getPosition()
403 || input->isExhausted())
405 break;
408 input->setPosition (chunkEnd);
413 if (metadataValues.size() > 0)
414 metadataValues.set ("MetaDataSource", "AIFF");
417 //==============================================================================
418 bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
419 int64 startSampleInFile, int numSamples)
421 const int64 samplesAvailable = lengthInSamples - startSampleInFile;
423 if (samplesAvailable < numSamples)
425 for (int i = numDestChannels; --i >= 0;)
426 if (destSamples[i] != nullptr)
427 zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * numSamples);
429 numSamples = (int) samplesAvailable;
432 if (numSamples <= 0)
433 return true;
435 input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame);
437 while (numSamples > 0)
439 const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
440 char tempBuffer [tempBufSize];
442 const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples);
443 const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);
445 if (bytesRead < numThisTime * bytesPerFrame)
447 jassert (bytesRead >= 0);
448 zeromem (tempBuffer + bytesRead, numThisTime * bytesPerFrame - bytesRead);
451 jassert (! usesFloatingPointData); // (would need to add support for this if it's possible)
453 if (littleEndian)
455 switch (bitsPerSample)
457 case 8: ReadHelper<AudioData::Int32, AudioData::Int8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break;
458 case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break;
459 case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break;
460 case 32: ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break;
461 default: jassertfalse; break;
464 else
466 switch (bitsPerSample)
468 case 8: ReadHelper<AudioData::Int32, AudioData::Int8, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break;
469 case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break;
470 case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break;
471 case 32: ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break;
472 default: jassertfalse; break;
476 startOffsetInDestBuffer += numThisTime;
477 numSamples -= numThisTime;
480 return true;
483 private:
484 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader);
487 //==============================================================================
488 class AiffAudioFormatWriter : public AudioFormatWriter
490 public:
491 //==============================================================================
492 AiffAudioFormatWriter (OutputStream* out, double sampleRate_,
493 unsigned int numChans, int bits,
494 const StringPairArray& metadataValues)
495 : AudioFormatWriter (out, TRANS (aiffFormatName), sampleRate_, numChans, bits),
496 lengthInSamples (0),
497 bytesWritten (0),
498 writeFailed (false)
500 using namespace AiffFileHelpers;
502 if (metadataValues.size() > 0)
504 // The meta data should have been santised for the AIFF format.
505 // If it was originally sourced from a WAV file the MetaDataSource
506 // key should be removed (or set to "AIFF") once this has been done
507 jassert (metadataValues.getValue ("MetaDataSource", "None") != "WAV");
509 MarkChunk::create (markChunk, metadataValues);
510 COMTChunk::create (comtChunk, metadataValues);
511 InstChunk::create (instChunk, metadataValues);
514 headerPosition = out->getPosition();
515 writeHeader();
518 ~AiffAudioFormatWriter()
520 if ((bytesWritten & 1) != 0)
521 output->writeByte (0);
523 writeHeader();
526 //==============================================================================
527 bool write (const int** data, int numSamples)
529 jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel!
531 if (writeFailed)
532 return false;
534 const int bytes = numChannels * numSamples * bitsPerSample / 8;
535 tempBlock.ensureSize (bytes, false);
537 switch (bitsPerSample)
539 case 8: WriteHelper<AudioData::Int8, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break;
540 case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break;
541 case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break;
542 case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break;
543 default: jassertfalse; break;
546 if (bytesWritten + bytes >= (uint32) 0xfff00000
547 || ! output->write (tempBlock.getData(), bytes))
549 // failed to write to disk, so let's try writing the header.
550 // If it's just run out of disk space, then if it does manage
551 // to write the header, we'll still have a useable file..
552 writeHeader();
553 writeFailed = true;
554 return false;
556 else
558 bytesWritten += bytes;
559 lengthInSamples += numSamples;
561 return true;
565 private:
566 MemoryBlock tempBlock, markChunk, comtChunk, instChunk;
567 uint32 lengthInSamples, bytesWritten;
568 int64 headerPosition;
569 bool writeFailed;
571 void writeHeader()
573 using namespace AiffFileHelpers;
575 const bool couldSeekOk = output->setPosition (headerPosition);
576 (void) couldSeekOk;
578 // if this fails, you've given it an output stream that can't seek! It needs
579 // to be able to seek back to write the header
580 jassert (couldSeekOk);
582 const int headerLen = (int) (54 + (markChunk.getSize() > 0 ? markChunk.getSize() + 8 : 0)
583 + (comtChunk.getSize() > 0 ? comtChunk.getSize() + 8 : 0)
584 + (instChunk.getSize() > 0 ? instChunk.getSize() + 8 : 0));
585 int audioBytes = lengthInSamples * ((bitsPerSample * numChannels) / 8);
586 audioBytes += (audioBytes & 1);
588 output->writeInt (chunkName ("FORM"));
589 output->writeIntBigEndian (headerLen + audioBytes - 8);
590 output->writeInt (chunkName ("AIFF"));
591 output->writeInt (chunkName ("COMM"));
592 output->writeIntBigEndian (18);
593 output->writeShortBigEndian ((short) numChannels);
594 output->writeIntBigEndian (lengthInSamples);
595 output->writeShortBigEndian ((short) bitsPerSample);
597 uint8 sampleRateBytes[10] = { 0 };
599 if (sampleRate <= 1)
601 sampleRateBytes[0] = 0x3f;
602 sampleRateBytes[1] = 0xff;
603 sampleRateBytes[2] = 0x80;
605 else
607 int mask = 0x40000000;
608 sampleRateBytes[0] = 0x40;
610 if (sampleRate >= mask)
612 jassertfalse;
613 sampleRateBytes[1] = 0x1d;
615 else
617 int n = (int) sampleRate;
619 int i;
620 for (i = 0; i <= 32 ; ++i)
622 if ((n & mask) != 0)
623 break;
625 mask >>= 1;
628 n = n << (i + 1);
630 sampleRateBytes[1] = (uint8) (29 - i);
631 sampleRateBytes[2] = (uint8) ((n >> 24) & 0xff);
632 sampleRateBytes[3] = (uint8) ((n >> 16) & 0xff);
633 sampleRateBytes[4] = (uint8) ((n >> 8) & 0xff);
634 sampleRateBytes[5] = (uint8) (n & 0xff);
638 output->write (sampleRateBytes, 10);
640 if (markChunk.getSize() > 0)
642 output->writeInt (chunkName ("MARK"));
643 output->writeIntBigEndian ((int) markChunk.getSize());
644 output->write (markChunk.getData(), (int) markChunk.getSize());
647 if (comtChunk.getSize() > 0)
649 output->writeInt (chunkName ("COMT"));
650 output->writeIntBigEndian ((int) comtChunk.getSize());
651 output->write (comtChunk.getData(), (int) comtChunk.getSize());
654 if (instChunk.getSize() > 0)
656 output->writeInt (chunkName ("INST"));
657 output->writeIntBigEndian ((int) instChunk.getSize());
658 output->write (instChunk.getData(), (int) instChunk.getSize());
661 output->writeInt (chunkName ("SSND"));
662 output->writeIntBigEndian (audioBytes + 8);
663 output->writeInt (0);
664 output->writeInt (0);
666 jassert (output->getPosition() == headerLen);
669 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter);
672 //==============================================================================
673 AiffAudioFormat::AiffAudioFormat()
674 : AudioFormat (TRANS (aiffFormatName), StringArray (aiffExtensions))
678 AiffAudioFormat::~AiffAudioFormat()
682 const Array <int> AiffAudioFormat::getPossibleSampleRates()
684 const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 };
685 return Array <int> (rates);
688 const Array <int> AiffAudioFormat::getPossibleBitDepths()
690 const int depths[] = { 8, 16, 24, 0 };
691 return Array <int> (depths);
694 bool AiffAudioFormat::canDoStereo() { return true; }
695 bool AiffAudioFormat::canDoMono() { return true; }
697 #if JUCE_MAC
698 bool AiffAudioFormat::canHandleFile (const File& f)
700 if (AudioFormat::canHandleFile (f))
701 return true;
703 const OSType type = PlatformUtilities::getTypeOfFile (f.getFullPathName());
704 return type == 'AIFF' || type == 'AIFC'
705 || type == 'aiff' || type == 'aifc';
707 #endif
709 AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails)
711 ScopedPointer <AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream));
713 if (w->sampleRate > 0)
714 return w.release();
716 if (! deleteStreamIfOpeningFails)
717 w->input = nullptr;
719 return nullptr;
722 AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
723 double sampleRate,
724 unsigned int numberOfChannels,
725 int bitsPerSample,
726 const StringPairArray& metadataValues,
727 int /*qualityOptionIndex*/)
729 if (getPossibleBitDepths().contains (bitsPerSample))
730 return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, bitsPerSample, metadataValues);
732 return nullptr;
735 END_JUCE_NAMESPACE