29static const char*
const aiffFormatName =
"AIFF file";
42namespace AiffFileHelpers
47 #pragma pack (push, 1)
101 static void create (MemoryBlock& block,
const StringPairArray& values)
103 if (values.getAllKeys().contains (
"MidiUnityNote",
true))
105 block.setSize ((
sizeof (InstChunk) + 3) & ~(
size_t) 3,
true);
106 auto& inst = *
static_cast<InstChunk*
> (block.getData());
108 inst.baseNote = getValue8 (values,
"MidiUnityNote",
"60");
109 inst.detune = getValue8 (values,
"Detune",
"0");
110 inst.lowNote = getValue8 (values,
"LowNote",
"0");
111 inst.highNote = getValue8 (values,
"HighNote",
"127");
112 inst.lowVelocity = getValue8 (values,
"LowVelocity",
"1");
113 inst.highVelocity = getValue8 (values,
"HighVelocity",
"127");
114 inst.gain = (
int16) getValue16 (values,
"Gain",
"0");
116 inst.sustainLoop.type = getValue16 (values,
"Loop0Type",
"0");
117 inst.sustainLoop.startIdentifier = getValue16 (values,
"Loop0StartIdentifier",
"0");
118 inst.sustainLoop.endIdentifier = getValue16 (values,
"Loop0EndIdentifier",
"0");
119 inst.releaseLoop.type = getValue16 (values,
"Loop1Type",
"0");
120 inst.releaseLoop.startIdentifier = getValue16 (values,
"Loop1StartIdentifier",
"0");
121 inst.releaseLoop.endIdentifier = getValue16 (values,
"Loop1EndIdentifier",
"0");
149 input.
read (unknown,
sizeof (unknown));
154 const bool rootNoteSet = rootNote != 0;
166 const char* keyString =
nullptr;
170 case minor: keyString =
"minor";
break;
171 case major: keyString =
"major";
break;
172 case neither: keyString =
"neither";
break;
173 case both: keyString =
"both";
break;
177 if (keyString !=
nullptr)
183 bool shouldBeSet)
const
185 values.
emplace (name, shouldBeSet ?
"1" :
"0");
205 static bool isValidTag (
const char* d)
noexcept
212 static bool isAppleGenre (
const String& tag)
noexcept
214 static const char* appleGenres[] =
229 if (tag == appleGenres[i])
235 static String
read (InputStream& input,
const uint32 length)
238 input.skipNextBytes (4);
239 input.readIntoMemoryBlock (mb, (ssize_t) length - 4);
241 StringArray tagsArray;
243 auto*
data =
static_cast<const char*
> (mb.getData());
244 auto* dataEnd =
data + mb.getSize();
246 while (data < dataEnd)
248 bool isGenre =
false;
250 if (isValidTag (data))
252 auto tag = String (CharPointer_UTF8 (data), CharPointer_UTF8 (dataEnd));
253 isGenre = isAppleGenre (tag);
257 data += isGenre ? 118 : 50;
259 if (data < dataEnd && data[0] == 0)
261 if (data + 52 < dataEnd && isValidTag (data + 50))
data += 50;
262 else if (data + 120 < dataEnd && isValidTag (data + 118))
data += 118;
263 else if (data + 170 < dataEnd && isValidTag (data + 168))
data += 168;
267 return tagsArray.joinIntoString (
";");
274 static bool metaDataContainsZeroIdentifiers (
const StringPairArray& values)
277 const String cueString (
"Cue");
278 const String noteString (
"CueNote");
279 const String identifierString (
"Identifier");
281 for (
auto& key : values.getAllKeys())
283 if (key.startsWith (noteString))
286 if (key.startsWith (cueString) && key.contains (identifierString))
287 if (values.getValue (key,
"-1").getIntValue() == 0)
294 static void create (MemoryBlock& block,
const StringPairArray& values)
296 auto numCues = values.getValue (
"NumCuePoints",
"0").getIntValue();
300 MemoryOutputStream out (block,
false);
301 out.writeShortBigEndian ((
short) numCues);
303 auto numCueLabels = values.getValue (
"NumCueLabels",
"0").getIntValue();
304 auto idOffset = metaDataContainsZeroIdentifiers (values) ? 1 : 0;
307 Array<int> identifiers;
310 for (
int i = 0; i < numCues; ++i)
312 auto prefixCue =
"Cue" + String (i);
313 auto identifier = idOffset + values.getValue (prefixCue +
"Identifier",
"1").getIntValue();
316 jassert (! identifiers.contains (identifier));
317 identifiers.add (identifier);
320 auto offset = values.getValue (prefixCue +
"Offset",
"0").getIntValue();
321 auto label =
"CueLabel" + String (i);
323 for (
int labelIndex = 0; labelIndex < numCueLabels; ++labelIndex)
325 auto prefixLabel =
"CueLabel" + String (labelIndex);
326 auto labelIdentifier = idOffset + values.getValue (prefixLabel +
"Identifier",
"1").getIntValue();
328 if (labelIdentifier == identifier)
330 label = values.getValue (prefixLabel +
"Text", label);
335 out.writeShortBigEndian ((
short) identifier);
336 out.writeIntBigEndian (offset);
338 auto labelLength =
jmin ((
size_t) 254, label.getNumBytesAsUTF8());
339 out.writeByte (
static_cast<char> (labelLength + 1));
340 out.write (label.toUTF8(), labelLength);
343 if ((out.getDataSize() & 1) != 0)
353 static void create (MemoryBlock& block,
const StringPairArray& values)
355 auto numNotes = values.getValue (
"NumCueNotes",
"0").getIntValue();
359 MemoryOutputStream out (block,
false);
360 out.writeShortBigEndian ((
short) numNotes);
362 for (
int i = 0; i < numNotes; ++i)
364 auto prefix =
"CueNote" + String (i);
366 out.writeIntBigEndian (values.getValue (prefix +
"TimeStamp",
"0").getIntValue());
367 out.writeShortBigEndian ((
short) values.getValue (prefix +
"Identifier",
"0").getIntValue());
369 auto comment = values.getValue (prefix +
"Text", String());
370 auto commentLength =
jmin (comment.getNumBytesAsUTF8(), (
size_t) 65534);
372 out.writeShortBigEndian (
static_cast<short> (commentLength + 1));
373 out.write (comment.toUTF8(), commentLength);
376 if ((out.getDataSize() & 1) != 0)
391 using namespace AiffFileHelpers;
410 if (nextType == chunkName (
"AIFF") || nextType == chunkName (
"AIFC"))
412 bool hasGotVer =
false;
413 bool hasGotData =
false;
414 bool hasGotType =
false;
422 if (type == chunkName (
"FVER"))
427 if (ver != 0 && ver != (
int) 0xa2805140)
430 else if (type == chunkName (
"COMM"))
439 unsigned char sampleRateBytes[10];
441 const int byte0 = sampleRateBytes[0];
443 if ((byte0 & 0x80) != 0
444 || byte0 <= 0x3F || byte0 > 0x40
445 || (byte0 == 0x40 && sampleRateBytes[1] > 0x1C))
456 littleEndian =
false;
462 if (compType == chunkName (
"NONE") || compType == chunkName (
"twos"))
464 littleEndian =
false;
466 else if (compType == chunkName (
"sowt"))
470 else if (compType == chunkName (
"fl32") || compType == chunkName (
"FL32"))
472 littleEndian =
false;
482 else if (type == chunkName (
"SSND"))
490 else if (type == chunkName (
"MARK"))
495 metadataValuesMap.
emplace (
"NumCuePoints",
String (numCues));
496 metadataValuesMap.
emplace (
"NumCueLabels",
String (numCues));
498 for (
uint16 i = 0; i < numCues; ++i)
509 if ((stringLength & 1) == 0)
512 auto prefixCue =
"Cue" +
String (i);
513 metadataValuesMap.
emplace (prefixCue +
"Identifier",
String (identifier));
514 metadataValuesMap.
emplace (prefixCue +
"Offset",
String (offset));
516 auto prefixLabel =
"CueLabel" +
String (i);
517 metadataValuesMap.
emplace (prefixLabel +
"Identifier",
String (identifier));
518 metadataValuesMap.
emplace (prefixLabel +
"Text", textBlock.
toString());
521 else if (type == chunkName (
"COMT"))
524 metadataValuesMap.
emplace (
"NumCueNotes",
String (numNotes));
526 for (
uint16 i = 0; i < numNotes; ++i)
535 auto prefix =
"CueNote" +
String (i);
536 metadataValuesMap.
emplace (prefix +
"TimeStamp",
String (timestamp));
537 metadataValuesMap.
emplace (prefix +
"Identifier",
String (identifier));
541 else if (type == chunkName (
"INST"))
544 inst.
calloc (
jmax ((
size_t) length + 1,
sizeof (InstChunk)), 1);
546 inst->copyTo (metadataValuesMap);
548 else if (type == chunkName (
"basc"))
552 else if (type == chunkName (
"cate"))
555 AiffFileHelpers::CATEChunk::read (*
input, length));
557 else if ((hasGotVer && hasGotData && hasGotType)
558 || chunkEnd < input->getPosition()
569 if (metadataValuesMap.
size() > 0)
570 metadataValuesMap.
emplace (
"MetaDataSource",
"AIFF");
576 bool readSamples (
int*
const* destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
577 int64 startSampleInFile,
int numSamples)
override
587 while (numSamples > 0)
589 const int tempBufSize = 480 * 3 * 4;
590 char tempBuffer [tempBufSize];
592 const int numThisTime =
jmin (tempBufSize / bytesPerFrame, numSamples);
593 const int bytesRead =
input->
read (tempBuffer, numThisTime * bytesPerFrame);
595 if (bytesRead < numThisTime * bytesPerFrame)
598 zeromem (tempBuffer + bytesRead, (
size_t) (numThisTime * bytesPerFrame - bytesRead));
603 destSamples, startOffsetInDestBuffer, numDestChannels,
607 destSamples, startOffsetInDestBuffer, numDestChannels,
610 startOffsetInDestBuffer += numThisTime;
611 numSamples -= numThisTime;
617 template <
typename Endianness>
618 static void copySampleData (
unsigned int numBitsPerSample,
bool floatingPointData,
619 int*
const* destSamples,
int startOffsetInDestBuffer,
int numDestChannels,
620 const void* sourceData,
int numberOfChannels,
int numSamples)
noexcept
622 switch (numBitsPerSample)
624 case 8: ReadHelper<AudioData::Int32, AudioData::Int8, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
break;
625 case 16: ReadHelper<AudioData::Int32, AudioData::Int16, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
break;
626 case 24: ReadHelper<AudioData::Int32, AudioData::Int24, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
break;
627 case 32:
if (floatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
628 else ReadHelper<AudioData::Int32, AudioData::Int32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
635 int64 dataChunkStart;
647 unsigned int numChans,
unsigned int bits,
651 using namespace AiffFileHelpers;
653 if (metadataValues.
size() > 0)
660 MarkChunk::create (markChunk, metadataValues);
661 COMTChunk::create (comtChunk, metadataValues);
662 InstChunk::create (instChunk, metadataValues);
671 if ((bytesWritten & 1) != 0)
678 bool write (
const int** data,
int numSamples)
override
681 jassert (data !=
nullptr && *data !=
nullptr);
698 if (bytesWritten + bytes >= (
size_t) 0xfff00000
709 bytesWritten += bytes;
710 lengthInSamples += (
uint64) numSamples;
715 MemoryBlock tempBlock, markChunk, comtChunk, instChunk;
716 uint64 lengthInSamples = 0, bytesWritten = 0;
717 int64 headerPosition = 0;
718 bool writeFailed =
false;
722 using namespace AiffFileHelpers;
730 auto headerLen = (
int) (54 + (markChunk.
isEmpty() ? 0 : markChunk.getSize() + 8)
731 + (comtChunk.isEmpty() ? 0 : comtChunk.getSize() + 8)
732 + (instChunk.isEmpty() ? 0 : instChunk.getSize() + 8));
734 audioBytes += (audioBytes & 1);
745 uint8 sampleRateBytes[10] = {};
749 sampleRateBytes[0] = 0x3f;
750 sampleRateBytes[1] = 0xff;
751 sampleRateBytes[2] = 0x80;
755 int mask = 0x40000000;
756 sampleRateBytes[0] = 0x40;
761 sampleRateBytes[1] = 0x1d;
768 for (i = 0; i <= 32 ; ++i)
778 sampleRateBytes[1] = (
uint8) (29 - i);
779 sampleRateBytes[2] = (
uint8) ((n >> 24) & 0xff);
780 sampleRateBytes[3] = (
uint8) ((n >> 16) & 0xff);
781 sampleRateBytes[4] = (
uint8) ((n >> 8) & 0xff);
782 sampleRateBytes[5] = (
uint8) (n & 0xff);
827 littleEndian (reader.littleEndian)
831 bool readSamples (
int*
const* destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
832 int64 startSampleInFile,
int numSamples)
override
840 if (map ==
nullptr || ! mappedSection.
contains (
Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
847 AiffAudioFormatReader::copySampleData<AudioData::LittleEndian>
851 AiffAudioFormatReader::copySampleData<AudioData::BigEndian>
862 if (map ==
nullptr || ! mappedSection.
contains (sample))
866 zeromem (result, (
size_t) num *
sizeof (
float));
870 float** dest = &result;
905 if (map ==
nullptr || numSamples <= 0 || ! mappedSection.
contains (
Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
909 for (
int i = 0; i < numChannelsToRead; ++i)
917 case 8: scanMinAndMax<AudioData::UInt8> (startSampleInFile, numSamples, results, numChannelsToRead);
break;
918 case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, results, numChannelsToRead);
break;
919 case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, results, numChannelsToRead);
break;
920 case 32:
if (
usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, results, numChannelsToRead);
921 else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, results, numChannelsToRead);
930 const bool littleEndian;
932 template <
typename SampleType>
933 void scanMinAndMax (
int64 startSampleInFile,
int64 numSamples,
Range<float>* results,
int numChannelsToRead)
const noexcept
935 for (
int i = 0; i < numChannelsToRead; ++i)
936 results[i] = scanMinAndMaxForChannel<SampleType> (i, startSampleInFile, numSamples);
939 template <
typename SampleType>
940 Range<float> scanMinAndMaxForChannel (
int channel,
int64 startSampleInFile,
int64 numSamples)
const noexcept
942 return littleEndian ? scanMinAndMaxInterleaved<SampleType, AudioData::LittleEndian> (channel, startSampleInFile, numSamples)
955 return { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 };
960 return { 8, 16, 24 };
975 return type == 0x41494646 || type == 0x41494643
976 || type == 0x61696666 || type == 0x61696663 ;
984 if (w->sampleRate > 0 && w->numChannels > 0)
987 if (! deleteStreamIfOpeningFails)
1013 unsigned int numberOfChannels,
1020 (
unsigned int) bitsPerSample, metadataValues);
Holds a resizable array of primitive or copy-by-value objects.
static constexpr uint32 bigEndianInt(const void *bytes) noexcept
Turns 4 bytes into a big-endian integer.
static constexpr uint32 littleEndianInt(const void *bytes) noexcept
Turns 4 bytes into a little-endian integer.
static Type swapIfLittleEndian(Type value) noexcept
Swaps the byte order of a signed or unsigned integer if the CPU is little-endian.
static constexpr uint16 bigEndianShort(const void *bytes) noexcept
Turns 2 bytes into a big-endian integer.
static bool isLowerCase(juce_wchar character) noexcept
Checks whether a unicode character is lower-case.
static bool isLetterOrDigit(char character) noexcept
Checks whether a character is alphabetic or numeric.
static bool isUpperCase(juce_wchar character) noexcept
Checks whether a unicode character is upper-case.
Represents a local file or directory.
OSType getMacOSType() const
OSX ONLY - Finds the OSType of a file from the its resources.
std::unique_ptr< FileInputStream > createInputStream() const
Creates a stream to read from this file.
Very simple container class to hold a pointer to some data on the heap.
void calloc(SizeType newNumElements, const size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory and clears it.
A class to hold a resizable block of raw data.
bool isEmpty() const noexcept
Returns true if the memory block has zero size.
void * getData() noexcept
Returns a void pointer to the data.
String toString() const
Attempts to parse the contents of the block as a zero-terminated UTF8 string.
size_t getSize() const noexcept
Returns the block's current allocated size, in bytes.
void ensureSize(size_t minimumSize, bool initialiseNewSpaceToZero=false)
Increases the block's size only if it's smaller than a given size.
void readMaxLevels(int64 startSampleInFile, int64 numSamples, Range< float > *results, int numChannelsToRead) override
Finds the highest and lowest sample levels from a section of the audio stream.
bool readSamples(int *const *destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override
Subclasses must implement this method to perform the low-level read operation.
void getSample(int64 sample, float *result) const noexcept override
Returns the samples for all channels at a given sample position.
The base class for streams that write data to some kind of destination.
virtual bool write(const void *dataToWrite, size_t numberOfBytes)=0
Writes a block of data to the stream.
virtual int64 getPosition()=0
Returns the stream's current position.
virtual bool writeByte(char byte)
Writes a single byte to the stream.
virtual bool writeIntBigEndian(int value)
Writes a 32-bit integer to the stream in a big-endian byte order.
virtual bool setPosition(int64 newPosition)=0
Tries to move the stream's output position.
virtual bool writeShortBigEndian(short value)
Writes a 16-bit integer to the stream in a big-endian byte order.
virtual bool writeInt(int value)
Writes a 32-bit integer to the stream in a little-endian byte order.
A general-purpose range object, that simply represents any linear range with a start and end point.
constexpr bool contains(const ValueType position) const noexcept
Returns true if the given position lies inside this range.
String & getReference(int index) noexcept
Returns a reference to one of the strings in the array.
A container for holding a set of strings which are keyed by another string.
String getValue(StringRef, const String &defaultReturnValue) const
Finds the value corresponding to a key string.
const StringArray & getAllValues() const noexcept
Returns a list of all values in the array.
void addMap(const std::map< String, String > &mapToAdd)
Adds the contents of a map to this StringPairArray.
int size() const noexcept
Returns the number of strings in the array.
const StringArray & getAllKeys() const noexcept
Returns a list of all keys in the array.
int getIntValue() const noexcept
Reads the value of the string as a decimal number (up to 32 bits in size).
void zerostruct(Type &structure) noexcept
Overwrites a structure or object with zeros.
unsigned short uint16
A platform-independent 16-bit unsigned integer type.
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
signed short int16
A platform-independent 16-bit signed integer type.
signed char int8
A platform-independent 8-bit signed integer type.
unsigned long long uint64
A platform-independent 64-bit unsigned integer type.
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.
long long int64
A platform-independent 64-bit integer type.
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.