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"
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
); }
47 #pragma pack (push, 1)
50 #define PACKED __attribute__((packed))
55 //==============================================================================
60 uint16 type
; // these are different in AIFF and WAV
61 uint16 startIdentifier
;
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());
128 //==============================================================================
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();
159 void create (MemoryBlock
& block
, const StringPairArray
& values
)
161 const int numCues
= values
.getValue ("NumCuePoints", "0").getIntValue();
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
173 Array
<int> identifiers
;
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();
182 jassert (! identifiers
.contains (identifier
));
183 identifiers
.add (identifier
);
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
);
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
);
210 if ((out
.getDataSize() & 1) != 0)
216 //==============================================================================
219 void create (MemoryBlock
& block
, const StringPairArray
& values
)
221 const int numNotes
= values
.getValue ("NumCueNotes", "0").getIntValue();
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));
239 if ((out
.getDataSize() & 1) != 0)
248 //==============================================================================
249 class AiffAudioFormatReader
: public AudioFormatReader
253 int64 dataChunkStart
;
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"))
284 const int ver
= input
->readIntBigEndian();
285 if (ver
!= 0 && ver
!= (int) 0xa2805140)
288 else if (type
== chunkName ("COMM"))
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))
306 unsigned int sampRate
= ByteOrder::bigEndianInt (sampleRateBytes
+ 2);
307 sampRate
>>= (16414 - ByteOrder::bigEndianShort (sampleRateBytes
));
308 sampleRate
= (int) sampRate
;
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;
318 const int compType
= input
->readInt();
320 if (compType
== chunkName ("NONE") || compType
== chunkName ("twos"))
322 littleEndian
= false;
324 else if (compType
== chunkName ("sowt"))
335 else if (type
== chunkName ("SSND"))
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)
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())
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
;
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)
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;
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
;
484 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader
);
487 //==============================================================================
488 class AiffAudioFormatWriter
: public AudioFormatWriter
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
),
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();
518 ~AiffAudioFormatWriter()
520 if ((bytesWritten
& 1) != 0)
521 output
->writeByte (0);
526 //==============================================================================
527 bool write (const int** data
, int numSamples
)
529 jassert (data
!= nullptr && *data
!= nullptr); // the input must contain at least one channel!
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..
558 bytesWritten
+= bytes
;
559 lengthInSamples
+= numSamples
;
566 MemoryBlock tempBlock
, markChunk
, comtChunk
, instChunk
;
567 uint32 lengthInSamples
, bytesWritten
;
568 int64 headerPosition
;
573 using namespace AiffFileHelpers
;
575 const bool couldSeekOk
= output
->setPosition (headerPosition
);
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 };
601 sampleRateBytes
[0] = 0x3f;
602 sampleRateBytes
[1] = 0xff;
603 sampleRateBytes
[2] = 0x80;
607 int mask
= 0x40000000;
608 sampleRateBytes
[0] = 0x40;
610 if (sampleRate
>= mask
)
613 sampleRateBytes
[1] = 0x1d;
617 int n
= (int) sampleRate
;
620 for (i
= 0; i
<= 32 ; ++i
)
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; }
698 bool AiffAudioFormat::canHandleFile (const File
& f
)
700 if (AudioFormat::canHandleFile (f
))
703 const OSType type
= PlatformUtilities::getTypeOfFile (f
.getFullPathName());
704 return type
== 'AIFF' || type
== 'AIFC'
705 || type
== 'aiff' || type
== 'aifc';
709 AudioFormatReader
* AiffAudioFormat::createReaderFor (InputStream
* sourceStream
, const bool deleteStreamIfOpeningFails
)
711 ScopedPointer
<AiffAudioFormatReader
> w (new AiffAudioFormatReader (sourceStream
));
713 if (w
->sampleRate
> 0)
716 if (! deleteStreamIfOpeningFails
)
722 AudioFormatWriter
* AiffAudioFormat::createWriterFor (OutputStream
* out
,
724 unsigned int numberOfChannels
,
726 const StringPairArray
& metadataValues
,
727 int /*qualityOptionIndex*/)
729 if (getPossibleBitDepths().contains (bitsPerSample
))
730 return new AiffAudioFormatWriter (out
, sampleRate
, numberOfChannels
, bitsPerSample
, metadataValues
);