The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1024 lines
43KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. static const char* const aiffFormatName = "AIFF file";
  21. //==============================================================================
  22. const char* const AiffAudioFormat::appleOneShot = "apple one shot";
  23. const char* const AiffAudioFormat::appleRootSet = "apple root set";
  24. const char* const AiffAudioFormat::appleRootNote = "apple root note";
  25. const char* const AiffAudioFormat::appleBeats = "apple beats";
  26. const char* const AiffAudioFormat::appleDenominator = "apple denominator";
  27. const char* const AiffAudioFormat::appleNumerator = "apple numerator";
  28. const char* const AiffAudioFormat::appleTag = "apple tag";
  29. const char* const AiffAudioFormat::appleKey = "apple key";
  30. //==============================================================================
  31. namespace AiffFileHelpers
  32. {
  33. inline int chunkName (const char* name) noexcept { return (int) ByteOrder::littleEndianInt (name); }
  34. #if JUCE_MSVC
  35. #pragma pack (push, 1)
  36. #endif
  37. //==============================================================================
  38. struct InstChunk
  39. {
  40. struct Loop
  41. {
  42. uint16 type; // these are different in AIFF and WAV
  43. uint16 startIdentifier;
  44. uint16 endIdentifier;
  45. } JUCE_PACKED;
  46. int8 baseNote;
  47. int8 detune;
  48. int8 lowNote;
  49. int8 highNote;
  50. int8 lowVelocity;
  51. int8 highVelocity;
  52. int16 gain;
  53. Loop sustainLoop;
  54. Loop releaseLoop;
  55. void copyTo (std::map<String, String>& values) const
  56. {
  57. values.emplace ("MidiUnityNote", String (baseNote));
  58. values.emplace ("Detune", String (detune));
  59. values.emplace ("LowNote", String (lowNote));
  60. values.emplace ("HighNote", String (highNote));
  61. values.emplace ("LowVelocity", String (lowVelocity));
  62. values.emplace ("HighVelocity", String (highVelocity));
  63. values.emplace ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain)));
  64. values.emplace ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more
  65. values.emplace ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type)));
  66. values.emplace ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier)));
  67. values.emplace ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier)));
  68. values.emplace ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type)));
  69. values.emplace ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier)));
  70. values.emplace ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier)));
  71. }
  72. static uint16 getValue16 (const StringPairArray& values, const char* name, const char* def)
  73. {
  74. return ByteOrder::swapIfLittleEndian ((uint16) values.getValue (name, def).getIntValue());
  75. }
  76. static int8 getValue8 (const StringPairArray& values, const char* name, const char* def)
  77. {
  78. return (int8) values.getValue (name, def).getIntValue();
  79. }
  80. static void create (MemoryBlock& block, const StringPairArray& values)
  81. {
  82. if (values.getAllKeys().contains ("MidiUnityNote", true))
  83. {
  84. block.setSize ((sizeof (InstChunk) + 3) & ~(size_t) 3, true);
  85. auto& inst = *static_cast<InstChunk*> (block.getData());
  86. inst.baseNote = getValue8 (values, "MidiUnityNote", "60");
  87. inst.detune = getValue8 (values, "Detune", "0");
  88. inst.lowNote = getValue8 (values, "LowNote", "0");
  89. inst.highNote = getValue8 (values, "HighNote", "127");
  90. inst.lowVelocity = getValue8 (values, "LowVelocity", "1");
  91. inst.highVelocity = getValue8 (values, "HighVelocity", "127");
  92. inst.gain = (int16) getValue16 (values, "Gain", "0");
  93. inst.sustainLoop.type = getValue16 (values, "Loop0Type", "0");
  94. inst.sustainLoop.startIdentifier = getValue16 (values, "Loop0StartIdentifier", "0");
  95. inst.sustainLoop.endIdentifier = getValue16 (values, "Loop0EndIdentifier", "0");
  96. inst.releaseLoop.type = getValue16 (values, "Loop1Type", "0");
  97. inst.releaseLoop.startIdentifier = getValue16 (values, "Loop1StartIdentifier", "0");
  98. inst.releaseLoop.endIdentifier = getValue16 (values, "Loop1EndIdentifier", "0");
  99. }
  100. }
  101. } JUCE_PACKED;
  102. //==============================================================================
  103. struct BASCChunk
  104. {
  105. enum Key
  106. {
  107. minor = 1,
  108. major = 2,
  109. neither = 3,
  110. both = 4
  111. };
  112. BASCChunk (InputStream& input)
  113. {
  114. zerostruct (*this);
  115. flags = (uint32) input.readIntBigEndian();
  116. numBeats = (uint32) input.readIntBigEndian();
  117. rootNote = (uint16) input.readShortBigEndian();
  118. key = (uint16) input.readShortBigEndian();
  119. timeSigNum = (uint16) input.readShortBigEndian();
  120. timeSigDen = (uint16) input.readShortBigEndian();
  121. oneShot = (uint16) input.readShortBigEndian();
  122. input.read (unknown, sizeof (unknown));
  123. }
  124. void addToMetadata (std::map<String, String>& metadata) const
  125. {
  126. const bool rootNoteSet = rootNote != 0;
  127. setBoolFlag (metadata, AiffAudioFormat::appleOneShot, oneShot == 2);
  128. setBoolFlag (metadata, AiffAudioFormat::appleRootSet, rootNoteSet);
  129. if (rootNoteSet)
  130. metadata.emplace (AiffAudioFormat::appleRootNote, String (rootNote));
  131. metadata.emplace (AiffAudioFormat::appleBeats, String (numBeats));
  132. metadata.emplace (AiffAudioFormat::appleDenominator, String (timeSigDen));
  133. metadata.emplace (AiffAudioFormat::appleNumerator, String (timeSigNum));
  134. const char* keyString = nullptr;
  135. switch (key)
  136. {
  137. case minor: keyString = "minor"; break;
  138. case major: keyString = "major"; break;
  139. case neither: keyString = "neither"; break;
  140. case both: keyString = "both"; break;
  141. default: break;
  142. }
  143. if (keyString != nullptr)
  144. metadata.emplace (AiffAudioFormat::appleKey, keyString);
  145. }
  146. void setBoolFlag (std::map<String, String>& values,
  147. const char* name,
  148. bool shouldBeSet) const
  149. {
  150. values.emplace (name, shouldBeSet ? "1" : "0");
  151. }
  152. uint32 flags;
  153. uint32 numBeats;
  154. uint16 rootNote;
  155. uint16 key;
  156. uint16 timeSigNum;
  157. uint16 timeSigDen;
  158. uint16 oneShot;
  159. uint8 unknown[66];
  160. } JUCE_PACKED;
  161. #if JUCE_MSVC
  162. #pragma pack (pop)
  163. #endif
  164. //==============================================================================
  165. namespace CATEChunk
  166. {
  167. static bool isValidTag (const char* d) noexcept
  168. {
  169. return CharacterFunctions::isLetterOrDigit (d[0]) && CharacterFunctions::isUpperCase (static_cast<juce_wchar> (d[0]))
  170. && CharacterFunctions::isLetterOrDigit (d[1]) && CharacterFunctions::isLowerCase (static_cast<juce_wchar> (d[1]))
  171. && CharacterFunctions::isLetterOrDigit (d[2]) && CharacterFunctions::isLowerCase (static_cast<juce_wchar> (d[2]));
  172. }
  173. static bool isAppleGenre (const String& tag) noexcept
  174. {
  175. static const char* appleGenres[] =
  176. {
  177. "Rock/Blues",
  178. "Electronic/Dance",
  179. "Jazz",
  180. "Urban",
  181. "World/Ethnic",
  182. "Cinematic/New Age",
  183. "Orchestral",
  184. "Country/Folk",
  185. "Experimental",
  186. "Other Genre"
  187. };
  188. for (int i = 0; i < numElementsInArray (appleGenres); ++i)
  189. if (tag == appleGenres[i])
  190. return true;
  191. return false;
  192. }
  193. static String read (InputStream& input, const uint32 length)
  194. {
  195. MemoryBlock mb;
  196. input.skipNextBytes (4);
  197. input.readIntoMemoryBlock (mb, (ssize_t) length - 4);
  198. StringArray tagsArray;
  199. auto* data = static_cast<const char*> (mb.getData());
  200. auto* dataEnd = data + mb.getSize();
  201. while (data < dataEnd)
  202. {
  203. bool isGenre = false;
  204. if (isValidTag (data))
  205. {
  206. auto tag = String (CharPointer_UTF8 (data), CharPointer_UTF8 (dataEnd));
  207. isGenre = isAppleGenre (tag);
  208. tagsArray.add (tag);
  209. }
  210. data += isGenre ? 118 : 50;
  211. if (data < dataEnd && data[0] == 0)
  212. {
  213. if (data + 52 < dataEnd && isValidTag (data + 50)) data += 50;
  214. else if (data + 120 < dataEnd && isValidTag (data + 118)) data += 118;
  215. else if (data + 170 < dataEnd && isValidTag (data + 168)) data += 168;
  216. }
  217. }
  218. return tagsArray.joinIntoString (";");
  219. }
  220. }
  221. //==============================================================================
  222. namespace MarkChunk
  223. {
  224. static bool metaDataContainsZeroIdentifiers (const StringPairArray& values)
  225. {
  226. // (zero cue identifiers are valid for WAV but not for AIFF)
  227. const String cueString ("Cue");
  228. const String noteString ("CueNote");
  229. const String identifierString ("Identifier");
  230. for (auto& key : values.getAllKeys())
  231. {
  232. if (key.startsWith (noteString))
  233. continue; // zero identifier IS valid in a COMT chunk
  234. if (key.startsWith (cueString) && key.contains (identifierString))
  235. if (values.getValue (key, "-1").getIntValue() == 0)
  236. return true;
  237. }
  238. return false;
  239. }
  240. static void create (MemoryBlock& block, const StringPairArray& values)
  241. {
  242. auto numCues = values.getValue ("NumCuePoints", "0").getIntValue();
  243. if (numCues > 0)
  244. {
  245. MemoryOutputStream out (block, false);
  246. out.writeShortBigEndian ((short) numCues);
  247. auto numCueLabels = values.getValue ("NumCueLabels", "0").getIntValue();
  248. auto idOffset = metaDataContainsZeroIdentifiers (values) ? 1 : 0; // can't have zero IDs in AIFF
  249. #if JUCE_DEBUG
  250. Array<int> identifiers;
  251. #endif
  252. for (int i = 0; i < numCues; ++i)
  253. {
  254. auto prefixCue = "Cue" + String (i);
  255. auto identifier = idOffset + values.getValue (prefixCue + "Identifier", "1").getIntValue();
  256. #if JUCE_DEBUG
  257. jassert (! identifiers.contains (identifier));
  258. identifiers.add (identifier);
  259. #endif
  260. auto offset = values.getValue (prefixCue + "Offset", "0").getIntValue();
  261. auto label = "CueLabel" + String (i);
  262. for (int labelIndex = 0; labelIndex < numCueLabels; ++labelIndex)
  263. {
  264. auto prefixLabel = "CueLabel" + String (labelIndex);
  265. auto labelIdentifier = idOffset + values.getValue (prefixLabel + "Identifier", "1").getIntValue();
  266. if (labelIdentifier == identifier)
  267. {
  268. label = values.getValue (prefixLabel + "Text", label);
  269. break;
  270. }
  271. }
  272. out.writeShortBigEndian ((short) identifier);
  273. out.writeIntBigEndian (offset);
  274. auto labelLength = jmin ((size_t) 254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring
  275. out.writeByte (static_cast<char> (labelLength + 1));
  276. out.write (label.toUTF8(), labelLength);
  277. out.writeByte (0);
  278. if ((out.getDataSize() & 1) != 0)
  279. out.writeByte (0);
  280. }
  281. }
  282. }
  283. }
  284. //==============================================================================
  285. namespace COMTChunk
  286. {
  287. static void create (MemoryBlock& block, const StringPairArray& values)
  288. {
  289. auto numNotes = values.getValue ("NumCueNotes", "0").getIntValue();
  290. if (numNotes > 0)
  291. {
  292. MemoryOutputStream out (block, false);
  293. out.writeShortBigEndian ((short) numNotes);
  294. for (int i = 0; i < numNotes; ++i)
  295. {
  296. auto prefix = "CueNote" + String (i);
  297. out.writeIntBigEndian (values.getValue (prefix + "TimeStamp", "0").getIntValue());
  298. out.writeShortBigEndian ((short) values.getValue (prefix + "Identifier", "0").getIntValue());
  299. auto comment = values.getValue (prefix + "Text", String());
  300. auto commentLength = jmin (comment.getNumBytesAsUTF8(), (size_t) 65534);
  301. out.writeShortBigEndian (static_cast<short> (commentLength + 1));
  302. out.write (comment.toUTF8(), commentLength);
  303. out.writeByte (0);
  304. if ((out.getDataSize() & 1) != 0)
  305. out.writeByte (0);
  306. }
  307. }
  308. }
  309. }
  310. }
  311. //==============================================================================
  312. class AiffAudioFormatReader : public AudioFormatReader
  313. {
  314. public:
  315. AiffAudioFormatReader (InputStream* in)
  316. : AudioFormatReader (in, aiffFormatName)
  317. {
  318. using namespace AiffFileHelpers;
  319. std::map<String, String> metadataValuesMap;
  320. for (int i = 0; i != metadataValues.size(); ++i)
  321. {
  322. metadataValuesMap.emplace (metadataValues.getAllKeys().getReference (i),
  323. metadataValues.getAllValues().getReference (i));
  324. }
  325. // If this fails, there were duplicate keys in the metadata
  326. jassert ((size_t) metadataValuesMap.size() == (size_t) metadataValues.size());
  327. if (input->readInt() == chunkName ("FORM"))
  328. {
  329. auto len = input->readIntBigEndian();
  330. auto end = input->getPosition() + len;
  331. auto nextType = input->readInt();
  332. if (nextType == chunkName ("AIFF") || nextType == chunkName ("AIFC"))
  333. {
  334. bool hasGotVer = false;
  335. bool hasGotData = false;
  336. bool hasGotType = false;
  337. while (input->getPosition() < end)
  338. {
  339. auto type = input->readInt();
  340. auto length = (uint32) input->readIntBigEndian();
  341. auto chunkEnd = input->getPosition() + length;
  342. if (type == chunkName ("FVER"))
  343. {
  344. hasGotVer = true;
  345. auto ver = input->readIntBigEndian();
  346. if (ver != 0 && ver != (int) 0xa2805140)
  347. break;
  348. }
  349. else if (type == chunkName ("COMM"))
  350. {
  351. hasGotType = true;
  352. numChannels = (unsigned int) input->readShortBigEndian();
  353. lengthInSamples = input->readIntBigEndian();
  354. bitsPerSample = (unsigned int) input->readShortBigEndian();
  355. bytesPerFrame = (int) ((numChannels * bitsPerSample) >> 3);
  356. unsigned char sampleRateBytes[10];
  357. input->read (sampleRateBytes, 10);
  358. const int byte0 = sampleRateBytes[0];
  359. if ((byte0 & 0x80) != 0
  360. || byte0 <= 0x3F || byte0 > 0x40
  361. || (byte0 == 0x40 && sampleRateBytes[1] > 0x1C))
  362. break;
  363. auto sampRate = ByteOrder::bigEndianInt (sampleRateBytes + 2);
  364. sampRate >>= (16414 - ByteOrder::bigEndianShort (sampleRateBytes));
  365. sampleRate = (int) sampRate;
  366. if (length <= 18)
  367. {
  368. // some types don't have a chunk large enough to include a compression
  369. // type, so assume it's just big-endian pcm
  370. littleEndian = false;
  371. }
  372. else
  373. {
  374. auto compType = input->readInt();
  375. if (compType == chunkName ("NONE") || compType == chunkName ("twos"))
  376. {
  377. littleEndian = false;
  378. }
  379. else if (compType == chunkName ("sowt"))
  380. {
  381. littleEndian = true;
  382. }
  383. else if (compType == chunkName ("fl32") || compType == chunkName ("FL32"))
  384. {
  385. littleEndian = false;
  386. usesFloatingPointData = true;
  387. }
  388. else
  389. {
  390. sampleRate = 0;
  391. break;
  392. }
  393. }
  394. }
  395. else if (type == chunkName ("SSND"))
  396. {
  397. hasGotData = true;
  398. auto offset = input->readIntBigEndian();
  399. dataChunkStart = input->getPosition() + 4 + offset;
  400. lengthInSamples = (bytesPerFrame > 0) ? jmin (lengthInSamples, ((int64) length) / (int64) bytesPerFrame) : 0;
  401. }
  402. else if (type == chunkName ("MARK"))
  403. {
  404. auto numCues = (uint16) input->readShortBigEndian();
  405. // these two are always the same for AIFF-read files
  406. metadataValuesMap.emplace ("NumCuePoints", String (numCues));
  407. metadataValuesMap.emplace ("NumCueLabels", String (numCues));
  408. for (uint16 i = 0; i < numCues; ++i)
  409. {
  410. auto identifier = (uint16) input->readShortBigEndian();
  411. auto offset = (uint32) input->readIntBigEndian();
  412. auto stringLength = (uint8) input->readByte();
  413. MemoryBlock textBlock;
  414. input->readIntoMemoryBlock (textBlock, stringLength);
  415. // if the stringLength is even then read one more byte as the
  416. // string needs to be an even number of bytes INCLUDING the
  417. // leading length character in the pascal string
  418. if ((stringLength & 1) == 0)
  419. input->readByte();
  420. auto prefixCue = "Cue" + String (i);
  421. metadataValuesMap.emplace (prefixCue + "Identifier", String (identifier));
  422. metadataValuesMap.emplace (prefixCue + "Offset", String (offset));
  423. auto prefixLabel = "CueLabel" + String (i);
  424. metadataValuesMap.emplace (prefixLabel + "Identifier", String (identifier));
  425. metadataValuesMap.emplace (prefixLabel + "Text", textBlock.toString());
  426. }
  427. }
  428. else if (type == chunkName ("COMT"))
  429. {
  430. auto numNotes = (uint16) input->readShortBigEndian();
  431. metadataValuesMap.emplace ("NumCueNotes", String (numNotes));
  432. for (uint16 i = 0; i < numNotes; ++i)
  433. {
  434. auto timestamp = (uint32) input->readIntBigEndian();
  435. auto identifier = (uint16) input->readShortBigEndian(); // may be zero in this case
  436. auto stringLength = (uint16) input->readShortBigEndian();
  437. MemoryBlock textBlock;
  438. input->readIntoMemoryBlock (textBlock, stringLength + (stringLength & 1));
  439. auto prefix = "CueNote" + String (i);
  440. metadataValuesMap.emplace (prefix + "TimeStamp", String (timestamp));
  441. metadataValuesMap.emplace (prefix + "Identifier", String (identifier));
  442. metadataValuesMap.emplace (prefix + "Text", textBlock.toString());
  443. }
  444. }
  445. else if (type == chunkName ("INST"))
  446. {
  447. HeapBlock<InstChunk> inst;
  448. inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
  449. input->read (inst, (int) length);
  450. inst->copyTo (metadataValuesMap);
  451. }
  452. else if (type == chunkName ("basc"))
  453. {
  454. AiffFileHelpers::BASCChunk (*input).addToMetadata (metadataValuesMap);
  455. }
  456. else if (type == chunkName ("cate"))
  457. {
  458. metadataValuesMap.emplace (AiffAudioFormat::appleTag,
  459. AiffFileHelpers::CATEChunk::read (*input, length));
  460. }
  461. else if ((hasGotVer && hasGotData && hasGotType)
  462. || chunkEnd < input->getPosition()
  463. || input->isExhausted())
  464. {
  465. break;
  466. }
  467. input->setPosition (chunkEnd + (chunkEnd & 1)); // (chunks should be aligned to an even byte address)
  468. }
  469. }
  470. }
  471. if (metadataValuesMap.size() > 0)
  472. metadataValuesMap.emplace ("MetaDataSource", "AIFF");
  473. metadataValues.addMap (metadataValuesMap);
  474. }
  475. //==============================================================================
  476. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  477. int64 startSampleInFile, int numSamples) override
  478. {
  479. clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
  480. startSampleInFile, numSamples, lengthInSamples);
  481. if (numSamples <= 0)
  482. return true;
  483. input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame);
  484. while (numSamples > 0)
  485. {
  486. const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
  487. char tempBuffer [tempBufSize];
  488. const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples);
  489. const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);
  490. if (bytesRead < numThisTime * bytesPerFrame)
  491. {
  492. jassert (bytesRead >= 0);
  493. zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead));
  494. }
  495. if (littleEndian)
  496. copySampleData<AudioData::LittleEndian> (bitsPerSample, usesFloatingPointData,
  497. destSamples, startOffsetInDestBuffer, numDestChannels,
  498. tempBuffer, (int) numChannels, numThisTime);
  499. else
  500. copySampleData<AudioData::BigEndian> (bitsPerSample, usesFloatingPointData,
  501. destSamples, startOffsetInDestBuffer, numDestChannels,
  502. tempBuffer, (int) numChannels, numThisTime);
  503. startOffsetInDestBuffer += numThisTime;
  504. numSamples -= numThisTime;
  505. }
  506. return true;
  507. }
  508. template <typename Endianness>
  509. static void copySampleData (unsigned int numBitsPerSample, bool floatingPointData,
  510. int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels,
  511. const void* sourceData, int numberOfChannels, int numSamples) noexcept
  512. {
  513. switch (numBitsPerSample)
  514. {
  515. case 8: ReadHelper<AudioData::Int32, AudioData::Int8, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break;
  516. case 16: ReadHelper<AudioData::Int32, AudioData::Int16, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break;
  517. case 24: ReadHelper<AudioData::Int32, AudioData::Int24, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break;
  518. case 32: if (floatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
  519. else ReadHelper<AudioData::Int32, AudioData::Int32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
  520. break;
  521. default: jassertfalse; break;
  522. }
  523. }
  524. int bytesPerFrame;
  525. int64 dataChunkStart;
  526. bool littleEndian;
  527. private:
  528. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader)
  529. };
  530. //==============================================================================
  531. class AiffAudioFormatWriter : public AudioFormatWriter
  532. {
  533. public:
  534. AiffAudioFormatWriter (OutputStream* out, double rate,
  535. unsigned int numChans, unsigned int bits,
  536. const StringPairArray& metadataValues)
  537. : AudioFormatWriter (out, aiffFormatName, rate, numChans, bits)
  538. {
  539. using namespace AiffFileHelpers;
  540. if (metadataValues.size() > 0)
  541. {
  542. // The meta data should have been sanitised for the AIFF format.
  543. // If it was originally sourced from a WAV file the MetaDataSource
  544. // key should be removed (or set to "AIFF") once this has been done
  545. jassert (metadataValues.getValue ("MetaDataSource", "None") != "WAV");
  546. MarkChunk::create (markChunk, metadataValues);
  547. COMTChunk::create (comtChunk, metadataValues);
  548. InstChunk::create (instChunk, metadataValues);
  549. }
  550. headerPosition = out->getPosition();
  551. writeHeader();
  552. }
  553. ~AiffAudioFormatWriter() override
  554. {
  555. if ((bytesWritten & 1) != 0)
  556. output->writeByte (0);
  557. writeHeader();
  558. }
  559. //==============================================================================
  560. bool write (const int** data, int numSamples) override
  561. {
  562. jassert (numSamples >= 0);
  563. jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel!
  564. if (writeFailed)
  565. return false;
  566. auto bytes = numChannels * (size_t) numSamples * bitsPerSample / 8;
  567. tempBlock.ensureSize (bytes, false);
  568. switch (bitsPerSample)
  569. {
  570. case 8: WriteHelper<AudioData::Int8, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  571. case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  572. case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  573. case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  574. default: jassertfalse; break;
  575. }
  576. if (bytesWritten + bytes >= (size_t) 0xfff00000
  577. || ! output->write (tempBlock.getData(), bytes))
  578. {
  579. // failed to write to disk, so let's try writing the header.
  580. // If it's just run out of disk space, then if it does manage
  581. // to write the header, we'll still have a useable file..
  582. writeHeader();
  583. writeFailed = true;
  584. return false;
  585. }
  586. bytesWritten += bytes;
  587. lengthInSamples += (uint64) numSamples;
  588. return true;
  589. }
  590. private:
  591. MemoryBlock tempBlock, markChunk, comtChunk, instChunk;
  592. uint64 lengthInSamples = 0, bytesWritten = 0;
  593. int64 headerPosition = 0;
  594. bool writeFailed = false;
  595. void writeHeader()
  596. {
  597. using namespace AiffFileHelpers;
  598. const bool couldSeekOk = output->setPosition (headerPosition);
  599. ignoreUnused (couldSeekOk);
  600. // if this fails, you've given it an output stream that can't seek! It needs
  601. // to be able to seek back to write the header
  602. jassert (couldSeekOk);
  603. auto headerLen = (int) (54 + (markChunk.isEmpty() ? 0 : markChunk.getSize() + 8)
  604. + (comtChunk.isEmpty() ? 0 : comtChunk.getSize() + 8)
  605. + (instChunk.isEmpty() ? 0 : instChunk.getSize() + 8));
  606. auto audioBytes = (int) (lengthInSamples * ((bitsPerSample * numChannels) / 8));
  607. audioBytes += (audioBytes & 1);
  608. output->writeInt (chunkName ("FORM"));
  609. output->writeIntBigEndian (headerLen + audioBytes - 8);
  610. output->writeInt (chunkName ("AIFF"));
  611. output->writeInt (chunkName ("COMM"));
  612. output->writeIntBigEndian (18);
  613. output->writeShortBigEndian ((short) numChannels);
  614. output->writeIntBigEndian ((int) lengthInSamples);
  615. output->writeShortBigEndian ((short) bitsPerSample);
  616. uint8 sampleRateBytes[10] = {};
  617. if (sampleRate <= 1)
  618. {
  619. sampleRateBytes[0] = 0x3f;
  620. sampleRateBytes[1] = 0xff;
  621. sampleRateBytes[2] = 0x80;
  622. }
  623. else
  624. {
  625. int mask = 0x40000000;
  626. sampleRateBytes[0] = 0x40;
  627. if (sampleRate >= mask)
  628. {
  629. jassertfalse;
  630. sampleRateBytes[1] = 0x1d;
  631. }
  632. else
  633. {
  634. int n = (int) sampleRate;
  635. int i;
  636. for (i = 0; i <= 32 ; ++i)
  637. {
  638. if ((n & mask) != 0)
  639. break;
  640. mask >>= 1;
  641. }
  642. n = n << (i + 1);
  643. sampleRateBytes[1] = (uint8) (29 - i);
  644. sampleRateBytes[2] = (uint8) ((n >> 24) & 0xff);
  645. sampleRateBytes[3] = (uint8) ((n >> 16) & 0xff);
  646. sampleRateBytes[4] = (uint8) ((n >> 8) & 0xff);
  647. sampleRateBytes[5] = (uint8) (n & 0xff);
  648. }
  649. }
  650. output->write (sampleRateBytes, 10);
  651. if (! markChunk.isEmpty())
  652. {
  653. output->writeInt (chunkName ("MARK"));
  654. output->writeIntBigEndian ((int) markChunk.getSize());
  655. *output << markChunk;
  656. }
  657. if (! comtChunk.isEmpty())
  658. {
  659. output->writeInt (chunkName ("COMT"));
  660. output->writeIntBigEndian ((int) comtChunk.getSize());
  661. *output << comtChunk;
  662. }
  663. if (! instChunk.isEmpty())
  664. {
  665. output->writeInt (chunkName ("INST"));
  666. output->writeIntBigEndian ((int) instChunk.getSize());
  667. *output << instChunk;
  668. }
  669. output->writeInt (chunkName ("SSND"));
  670. output->writeIntBigEndian (audioBytes + 8);
  671. output->writeInt (0);
  672. output->writeInt (0);
  673. jassert (output->getPosition() == headerLen);
  674. }
  675. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter)
  676. };
  677. //==============================================================================
  678. class MemoryMappedAiffReader : public MemoryMappedAudioFormatReader
  679. {
  680. public:
  681. MemoryMappedAiffReader (const File& f, const AiffAudioFormatReader& reader)
  682. : MemoryMappedAudioFormatReader (f, reader, reader.dataChunkStart,
  683. reader.bytesPerFrame * reader.lengthInSamples, reader.bytesPerFrame),
  684. littleEndian (reader.littleEndian)
  685. {
  686. }
  687. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  688. int64 startSampleInFile, int numSamples) override
  689. {
  690. clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
  691. startSampleInFile, numSamples, lengthInSamples);
  692. if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
  693. {
  694. jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
  695. return false;
  696. }
  697. if (littleEndian)
  698. AiffAudioFormatReader::copySampleData<AudioData::LittleEndian>
  699. (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer,
  700. numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
  701. else
  702. AiffAudioFormatReader::copySampleData<AudioData::BigEndian>
  703. (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer,
  704. numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
  705. return true;
  706. }
  707. void getSample (int64 sample, float* result) const noexcept override
  708. {
  709. auto num = (int) numChannels;
  710. if (map == nullptr || ! mappedSection.contains (sample))
  711. {
  712. jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
  713. zeromem (result, (size_t) num * sizeof (float));
  714. return;
  715. }
  716. float** dest = &result;
  717. const void* source = sampleToPointer (sample);
  718. if (littleEndian)
  719. {
  720. switch (bitsPerSample)
  721. {
  722. case 8: ReadHelper<AudioData::Float32, AudioData::UInt8, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
  723. case 16: ReadHelper<AudioData::Float32, AudioData::Int16, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
  724. case 24: ReadHelper<AudioData::Float32, AudioData::Int24, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
  725. case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num);
  726. else ReadHelper<AudioData::Float32, AudioData::Int32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num);
  727. break;
  728. default: jassertfalse; break;
  729. }
  730. }
  731. else
  732. {
  733. switch (bitsPerSample)
  734. {
  735. case 8: ReadHelper<AudioData::Float32, AudioData::UInt8, AudioData::BigEndian>::read (dest, 0, 1, source, 1, num); break;
  736. case 16: ReadHelper<AudioData::Float32, AudioData::Int16, AudioData::BigEndian>::read (dest, 0, 1, source, 1, num); break;
  737. case 24: ReadHelper<AudioData::Float32, AudioData::Int24, AudioData::BigEndian>::read (dest, 0, 1, source, 1, num); break;
  738. case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::BigEndian>::read (dest, 0, 1, source, 1, num);
  739. else ReadHelper<AudioData::Float32, AudioData::Int32, AudioData::BigEndian>::read (dest, 0, 1, source, 1, num);
  740. break;
  741. default: jassertfalse; break;
  742. }
  743. }
  744. }
  745. void readMaxLevels (int64 startSampleInFile, int64 numSamples, Range<float>* results, int numChannelsToRead) override
  746. {
  747. numSamples = jmin (numSamples, lengthInSamples - startSampleInFile);
  748. if (map == nullptr || numSamples <= 0 || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
  749. {
  750. jassert (numSamples <= 0); // you must make sure that the window contains all the samples you're going to attempt to read.
  751. for (int i = 0; i < numChannelsToRead; ++i)
  752. results[i] = Range<float>();
  753. return;
  754. }
  755. switch (bitsPerSample)
  756. {
  757. case 8: scanMinAndMax<AudioData::UInt8> (startSampleInFile, numSamples, results, numChannelsToRead); break;
  758. case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, results, numChannelsToRead); break;
  759. case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, results, numChannelsToRead); break;
  760. case 32: if (usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, results, numChannelsToRead);
  761. else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, results, numChannelsToRead);
  762. break;
  763. default: jassertfalse; break;
  764. }
  765. }
  766. using AudioFormatReader::readMaxLevels;
  767. private:
  768. const bool littleEndian;
  769. template <typename SampleType>
  770. void scanMinAndMax (int64 startSampleInFile, int64 numSamples, Range<float>* results, int numChannelsToRead) const noexcept
  771. {
  772. for (int i = 0; i < numChannelsToRead; ++i)
  773. results[i] = scanMinAndMaxForChannel<SampleType> (i, startSampleInFile, numSamples);
  774. }
  775. template <typename SampleType>
  776. Range<float> scanMinAndMaxForChannel (int channel, int64 startSampleInFile, int64 numSamples) const noexcept
  777. {
  778. return littleEndian ? scanMinAndMaxInterleaved<SampleType, AudioData::LittleEndian> (channel, startSampleInFile, numSamples)
  779. : scanMinAndMaxInterleaved<SampleType, AudioData::BigEndian> (channel, startSampleInFile, numSamples);
  780. }
  781. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAiffReader)
  782. };
  783. //==============================================================================
  784. AiffAudioFormat::AiffAudioFormat() : AudioFormat (aiffFormatName, ".aiff .aif") {}
  785. AiffAudioFormat::~AiffAudioFormat() {}
  786. Array<int> AiffAudioFormat::getPossibleSampleRates()
  787. {
  788. return { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 };
  789. }
  790. Array<int> AiffAudioFormat::getPossibleBitDepths()
  791. {
  792. return { 8, 16, 24 };
  793. }
  794. bool AiffAudioFormat::canDoStereo() { return true; }
  795. bool AiffAudioFormat::canDoMono() { return true; }
  796. #if JUCE_MAC
  797. bool AiffAudioFormat::canHandleFile (const File& f)
  798. {
  799. if (AudioFormat::canHandleFile (f))
  800. return true;
  801. auto type = f.getMacOSType();
  802. // (NB: written as hex to avoid four-char-constant warnings)
  803. return type == 0x41494646 /* AIFF */ || type == 0x41494643 /* AIFC */
  804. || type == 0x61696666 /* aiff */ || type == 0x61696663 /* aifc */;
  805. }
  806. #endif
  807. AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails)
  808. {
  809. std::unique_ptr<AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream));
  810. if (w->sampleRate > 0 && w->numChannels > 0)
  811. return w.release();
  812. if (! deleteStreamIfOpeningFails)
  813. w->input = nullptr;
  814. return nullptr;
  815. }
  816. MemoryMappedAudioFormatReader* AiffAudioFormat::createMemoryMappedReader (const File& file)
  817. {
  818. return createMemoryMappedReader (file.createInputStream().release());
  819. }
  820. MemoryMappedAudioFormatReader* AiffAudioFormat::createMemoryMappedReader (FileInputStream* fin)
  821. {
  822. if (fin != nullptr)
  823. {
  824. AiffAudioFormatReader reader (fin);
  825. if (reader.lengthInSamples > 0)
  826. return new MemoryMappedAiffReader (fin->getFile(), reader);
  827. }
  828. return nullptr;
  829. }
  830. AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
  831. double sampleRate,
  832. unsigned int numberOfChannels,
  833. int bitsPerSample,
  834. const StringPairArray& metadataValues,
  835. int /*qualityOptionIndex*/)
  836. {
  837. if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
  838. return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels,
  839. (unsigned int) bitsPerSample, metadataValues);
  840. return nullptr;
  841. }
  842. } // namespace juce