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.

720 lines
30KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. //==============================================================================
  19. static const char* const aiffFormatName = "AIFF file";
  20. static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 };
  21. //==============================================================================
  22. namespace AiffFileHelpers
  23. {
  24. inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); }
  25. #if JUCE_MSVC
  26. #pragma pack (push, 1)
  27. #define PACKED
  28. #elif JUCE_GCC
  29. #define PACKED __attribute__((packed))
  30. #else
  31. #define PACKED
  32. #endif
  33. //==============================================================================
  34. struct InstChunk
  35. {
  36. struct Loop
  37. {
  38. uint16 type; // these are different in AIFF and WAV
  39. uint16 startIdentifier;
  40. uint16 endIdentifier;
  41. } PACKED;
  42. int8 baseNote;
  43. int8 detune;
  44. int8 lowNote;
  45. int8 highNote;
  46. int8 lowVelocity;
  47. int8 highVelocity;
  48. int16 gain;
  49. Loop sustainLoop;
  50. Loop releaseLoop;
  51. void copyTo (StringPairArray& values) const
  52. {
  53. values.set ("MidiUnityNote", String (baseNote));
  54. values.set ("Detune", String (detune));
  55. values.set ("LowNote", String (lowNote));
  56. values.set ("HighNote", String (highNote));
  57. values.set ("LowVelocity", String (lowVelocity));
  58. values.set ("HighVelocity", String (highVelocity));
  59. values.set ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain)));
  60. values.set ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more
  61. values.set ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type)));
  62. values.set ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier)));
  63. values.set ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier)));
  64. values.set ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type)));
  65. values.set ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier)));
  66. values.set ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier)));
  67. }
  68. static void create (MemoryBlock& block, const StringPairArray& values)
  69. {
  70. if (values.getAllKeys().contains ("MidiUnityNote", true))
  71. {
  72. block.setSize ((sizeof (InstChunk) + 3) & ~3, true);
  73. InstChunk* const inst = static_cast <InstChunk*> (block.getData());
  74. inst->baseNote = (int8) values.getValue ("MidiUnityNote", "60").getIntValue();
  75. inst->detune = (int8) values.getValue ("Detune", "0").getIntValue();
  76. inst->lowNote = (int8) values.getValue ("LowNote", "0").getIntValue();
  77. inst->highNote = (int8) values.getValue ("HighNote", "127").getIntValue();
  78. inst->lowVelocity = (int8) values.getValue ("LowVelocity", "1").getIntValue();
  79. inst->highVelocity = (int8) values.getValue ("HighVelocity", "127").getIntValue();
  80. inst->gain = (int16) ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Gain", "0").getIntValue());
  81. inst->sustainLoop.type = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0Type", "0").getIntValue());
  82. inst->sustainLoop.startIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0StartIdentifier", "0").getIntValue());
  83. inst->sustainLoop.endIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0EndIdentifier", "0").getIntValue());
  84. inst->releaseLoop.type = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1Type", "0").getIntValue());
  85. inst->releaseLoop.startIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1StartIdentifier", "0").getIntValue());
  86. inst->releaseLoop.endIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1EndIdentifier", "0").getIntValue());
  87. }
  88. }
  89. } PACKED;
  90. #if JUCE_MSVC
  91. #pragma pack (pop)
  92. #endif
  93. #undef PACKED
  94. //==============================================================================
  95. namespace MarkChunk
  96. {
  97. bool metaDataContainsZeroIdentifiers (const StringPairArray& values)
  98. {
  99. // (zero cue identifiers are valid for WAV but not for AIFF)
  100. const String cueString ("Cue");
  101. const String noteString ("CueNote");
  102. const String identifierString ("Identifier");
  103. const StringArray& keys = values.getAllKeys();
  104. for (int i = 0; i < keys.size(); ++i)
  105. {
  106. const String key (keys[i]);
  107. if (key.startsWith (noteString))
  108. continue; // zero identifier IS valid in a COMT chunk
  109. if (key.startsWith (cueString) && key.contains (identifierString))
  110. {
  111. const int value = values.getValue (key, "-1").getIntValue();
  112. if (value == 0)
  113. return true;
  114. }
  115. }
  116. return false;
  117. }
  118. void create (MemoryBlock& block, const StringPairArray& values)
  119. {
  120. const int numCues = values.getValue ("NumCuePoints", "0").getIntValue();
  121. if (numCues > 0)
  122. {
  123. MemoryOutputStream out (block, false);
  124. out.writeShortBigEndian ((short) numCues);
  125. const int numCueLabels = values.getValue ("NumCueLabels", "0").getIntValue();
  126. const int idOffset = metaDataContainsZeroIdentifiers (values) ? 1 : 0; // can't have zero IDs in AIFF
  127. #if JUCE_DEBUG
  128. Array<int> identifiers;
  129. #endif
  130. for (int i = 0; i < numCues; ++i)
  131. {
  132. const String prefixCue ("Cue" + String (i));
  133. const int identifier = idOffset + values.getValue (prefixCue + "Identifier", "1").getIntValue();
  134. #if JUCE_DEBUG
  135. jassert (! identifiers.contains (identifier));
  136. identifiers.add (identifier);
  137. #endif
  138. const int offset = values.getValue (prefixCue + "Offset", "0").getIntValue();
  139. String label ("CueLabel" + String (i));
  140. for (int labelIndex = 0; labelIndex < numCueLabels; ++labelIndex)
  141. {
  142. const String prefixLabel ("CueLabel" + String (labelIndex));
  143. const int labelIdentifier = idOffset + values.getValue (prefixLabel + "Identifier", "1").getIntValue();
  144. if (labelIdentifier == identifier)
  145. {
  146. label = values.getValue (prefixLabel + "Text", label);
  147. break;
  148. }
  149. }
  150. out.writeShortBigEndian ((short) identifier);
  151. out.writeIntBigEndian (offset);
  152. const int labelLength = jmin (254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring
  153. out.writeByte ((char) labelLength + 1);
  154. out.write (label.toUTF8(), labelLength);
  155. out.writeByte (0);
  156. }
  157. if ((out.getDataSize() & 1) != 0)
  158. out.writeByte (0);
  159. }
  160. }
  161. }
  162. //==============================================================================
  163. namespace COMTChunk
  164. {
  165. void create (MemoryBlock& block, const StringPairArray& values)
  166. {
  167. const int numNotes = values.getValue ("NumCueNotes", "0").getIntValue();
  168. if (numNotes > 0)
  169. {
  170. MemoryOutputStream out (block, false);
  171. out.writeShortBigEndian ((short) numNotes);
  172. for (int i = 0; i < numNotes; ++i)
  173. {
  174. const String prefix ("CueNote" + String (i));
  175. out.writeIntBigEndian (values.getValue (prefix + "TimeStamp", "0").getIntValue());
  176. out.writeShortBigEndian ((short) values.getValue (prefix + "Identifier", "0").getIntValue());
  177. const String comment (values.getValue (prefix + "Text", String::empty));
  178. out.write (comment.toUTF8(), jmin (comment.getNumBytesAsUTF8(), 65534));
  179. out.writeByte (0);
  180. if ((out.getDataSize() & 1) != 0)
  181. out.writeByte (0);
  182. }
  183. }
  184. }
  185. }
  186. }
  187. //==============================================================================
  188. class AiffAudioFormatReader : public AudioFormatReader
  189. {
  190. public:
  191. AiffAudioFormatReader (InputStream* in)
  192. : AudioFormatReader (in, TRANS (aiffFormatName))
  193. {
  194. using namespace AiffFileHelpers;
  195. if (input->readInt() == chunkName ("FORM"))
  196. {
  197. const int len = input->readIntBigEndian();
  198. const int64 end = input->getPosition() + len;
  199. const int nextType = input->readInt();
  200. if (nextType == chunkName ("AIFF") || nextType == chunkName ("AIFC"))
  201. {
  202. bool hasGotVer = false;
  203. bool hasGotData = false;
  204. bool hasGotType = false;
  205. while (input->getPosition() < end)
  206. {
  207. const int type = input->readInt();
  208. const uint32 length = (uint32) input->readIntBigEndian();
  209. const int64 chunkEnd = input->getPosition() + length;
  210. if (type == chunkName ("FVER"))
  211. {
  212. hasGotVer = true;
  213. const int ver = input->readIntBigEndian();
  214. if (ver != 0 && ver != (int) 0xa2805140)
  215. break;
  216. }
  217. else if (type == chunkName ("COMM"))
  218. {
  219. hasGotType = true;
  220. numChannels = (unsigned int) input->readShortBigEndian();
  221. lengthInSamples = input->readIntBigEndian();
  222. bitsPerSample = (unsigned int) input->readShortBigEndian();
  223. bytesPerFrame = (int) ((numChannels * bitsPerSample) >> 3);
  224. unsigned char sampleRateBytes[10];
  225. input->read (sampleRateBytes, 10);
  226. const int byte0 = sampleRateBytes[0];
  227. if ((byte0 & 0x80) != 0
  228. || byte0 <= 0x3F || byte0 > 0x40
  229. || (byte0 == 0x40 && sampleRateBytes[1] > 0x1C))
  230. break;
  231. unsigned int sampRate = ByteOrder::bigEndianInt (sampleRateBytes + 2);
  232. sampRate >>= (16414 - ByteOrder::bigEndianShort (sampleRateBytes));
  233. sampleRate = (int) sampRate;
  234. if (length <= 18)
  235. {
  236. // some types don't have a chunk large enough to include a compression
  237. // type, so assume it's just big-endian pcm
  238. littleEndian = false;
  239. }
  240. else
  241. {
  242. const int compType = input->readInt();
  243. if (compType == chunkName ("NONE") || compType == chunkName ("twos"))
  244. {
  245. littleEndian = false;
  246. }
  247. else if (compType == chunkName ("sowt"))
  248. {
  249. littleEndian = true;
  250. }
  251. else
  252. {
  253. sampleRate = 0;
  254. break;
  255. }
  256. }
  257. }
  258. else if (type == chunkName ("SSND"))
  259. {
  260. hasGotData = true;
  261. const int offset = input->readIntBigEndian();
  262. dataChunkStart = input->getPosition() + 4 + offset;
  263. lengthInSamples = (bytesPerFrame > 0) ? jmin (lengthInSamples, (int64) (length / bytesPerFrame)) : 0;
  264. }
  265. else if (type == chunkName ("MARK"))
  266. {
  267. const uint16 numCues = (uint16) input->readShortBigEndian();
  268. // these two are always the same for AIFF-read files
  269. metadataValues.set ("NumCuePoints", String (numCues));
  270. metadataValues.set ("NumCueLabels", String (numCues));
  271. for (uint16 i = 0; i < numCues; ++i)
  272. {
  273. uint16 identifier = (uint16) input->readShortBigEndian();
  274. uint32 offset = (uint32) input->readIntBigEndian();
  275. uint8 stringLength = (uint8) input->readByte();
  276. MemoryBlock textBlock;
  277. input->readIntoMemoryBlock (textBlock, stringLength);
  278. // if the stringLength is even then read one more byte as the
  279. // string needs to be an even number of bytes INCLUDING the
  280. // leading length character in the pascal string
  281. if ((stringLength & 1) == 0)
  282. input->readByte();
  283. const String prefixCue ("Cue" + String (i));
  284. metadataValues.set (prefixCue + "Identifier", String (identifier));
  285. metadataValues.set (prefixCue + "Offset", String (offset));
  286. const String prefixLabel ("CueLabel" + String (i));
  287. metadataValues.set (prefixLabel + "Identifier", String (identifier));
  288. metadataValues.set (prefixLabel + "Text", textBlock.toString());
  289. }
  290. }
  291. else if (type == chunkName ("COMT"))
  292. {
  293. const uint16 numNotes = (uint16) input->readShortBigEndian();
  294. metadataValues.set ("NumCueNotes", String (numNotes));
  295. for (uint16 i = 0; i < numNotes; ++i)
  296. {
  297. uint32 timestamp = (uint32) input->readIntBigEndian();
  298. uint16 identifier = (uint16) input->readShortBigEndian(); // may be zero in this case
  299. uint16 stringLength = (uint16) input->readShortBigEndian();
  300. MemoryBlock textBlock;
  301. input->readIntoMemoryBlock (textBlock, stringLength + (stringLength & 1));
  302. const String prefix ("CueNote" + String (i));
  303. metadataValues.set (prefix + "TimeStamp", String (timestamp));
  304. metadataValues.set (prefix + "Identifier", String (identifier));
  305. metadataValues.set (prefix + "Text", textBlock.toString());
  306. }
  307. }
  308. else if (type == chunkName ("INST"))
  309. {
  310. HeapBlock <InstChunk> inst;
  311. inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
  312. input->read (inst, (int) length);
  313. inst->copyTo (metadataValues);
  314. }
  315. else if ((hasGotVer && hasGotData && hasGotType)
  316. || chunkEnd < input->getPosition()
  317. || input->isExhausted())
  318. {
  319. break;
  320. }
  321. input->setPosition (chunkEnd + (chunkEnd & 1)); // (chunks should be aligned to an even byte address)
  322. }
  323. }
  324. }
  325. if (metadataValues.size() > 0)
  326. metadataValues.set ("MetaDataSource", "AIFF");
  327. }
  328. //==============================================================================
  329. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  330. int64 startSampleInFile, int numSamples)
  331. {
  332. const int64 samplesAvailable = lengthInSamples - startSampleInFile;
  333. if (samplesAvailable < numSamples)
  334. {
  335. for (int i = numDestChannels; --i >= 0;)
  336. if (destSamples[i] != nullptr)
  337. zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * numSamples);
  338. numSamples = (int) samplesAvailable;
  339. }
  340. if (numSamples <= 0)
  341. return true;
  342. input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame);
  343. while (numSamples > 0)
  344. {
  345. const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
  346. char tempBuffer [tempBufSize];
  347. const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples);
  348. const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);
  349. if (bytesRead < numThisTime * bytesPerFrame)
  350. {
  351. jassert (bytesRead >= 0);
  352. zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead));
  353. }
  354. jassert (! usesFloatingPointData); // (would need to add support for this if it's possible)
  355. if (littleEndian)
  356. {
  357. switch (bitsPerSample)
  358. {
  359. case 8: ReadHelper<AudioData::Int32, AudioData::Int8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
  360. case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
  361. case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
  362. case 32: ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
  363. default: jassertfalse; break;
  364. }
  365. }
  366. else
  367. {
  368. switch (bitsPerSample)
  369. {
  370. case 8: ReadHelper<AudioData::Int32, AudioData::Int8, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
  371. case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
  372. case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
  373. case 32: ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
  374. default: jassertfalse; break;
  375. }
  376. }
  377. startOffsetInDestBuffer += numThisTime;
  378. numSamples -= numThisTime;
  379. }
  380. return true;
  381. }
  382. int bytesPerFrame;
  383. int64 dataChunkStart;
  384. bool littleEndian;
  385. private:
  386. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader);
  387. };
  388. //==============================================================================
  389. class AiffAudioFormatWriter : public AudioFormatWriter
  390. {
  391. public:
  392. AiffAudioFormatWriter (OutputStream* out, double sampleRate_,
  393. unsigned int numChans, unsigned int bits,
  394. const StringPairArray& metadataValues)
  395. : AudioFormatWriter (out, TRANS (aiffFormatName), sampleRate_, numChans, bits),
  396. lengthInSamples (0),
  397. bytesWritten (0),
  398. writeFailed (false)
  399. {
  400. using namespace AiffFileHelpers;
  401. if (metadataValues.size() > 0)
  402. {
  403. // The meta data should have been santised for the AIFF format.
  404. // If it was originally sourced from a WAV file the MetaDataSource
  405. // key should be removed (or set to "AIFF") once this has been done
  406. jassert (metadataValues.getValue ("MetaDataSource", "None") != "WAV");
  407. MarkChunk::create (markChunk, metadataValues);
  408. COMTChunk::create (comtChunk, metadataValues);
  409. InstChunk::create (instChunk, metadataValues);
  410. }
  411. headerPosition = out->getPosition();
  412. writeHeader();
  413. }
  414. ~AiffAudioFormatWriter()
  415. {
  416. if ((bytesWritten & 1) != 0)
  417. output->writeByte (0);
  418. writeHeader();
  419. }
  420. //==============================================================================
  421. bool write (const int** data, int numSamples)
  422. {
  423. jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel!
  424. if (writeFailed)
  425. return false;
  426. const int bytes = numChannels * numSamples * bitsPerSample / 8;
  427. tempBlock.ensureSize ((size_t) bytes, false);
  428. switch (bitsPerSample)
  429. {
  430. case 8: WriteHelper<AudioData::Int8, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  431. case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  432. case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  433. case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  434. default: jassertfalse; break;
  435. }
  436. if (bytesWritten + bytes >= (size_t) 0xfff00000
  437. || ! output->write (tempBlock.getData(), bytes))
  438. {
  439. // failed to write to disk, so let's try writing the header.
  440. // If it's just run out of disk space, then if it does manage
  441. // to write the header, we'll still have a useable file..
  442. writeHeader();
  443. writeFailed = true;
  444. return false;
  445. }
  446. else
  447. {
  448. bytesWritten += bytes;
  449. lengthInSamples += numSamples;
  450. return true;
  451. }
  452. }
  453. private:
  454. MemoryBlock tempBlock, markChunk, comtChunk, instChunk;
  455. uint32 lengthInSamples, bytesWritten;
  456. int64 headerPosition;
  457. bool writeFailed;
  458. void writeHeader()
  459. {
  460. using namespace AiffFileHelpers;
  461. const bool couldSeekOk = output->setPosition (headerPosition);
  462. (void) couldSeekOk;
  463. // if this fails, you've given it an output stream that can't seek! It needs
  464. // to be able to seek back to write the header
  465. jassert (couldSeekOk);
  466. const int headerLen = (int) (54 + (markChunk.getSize() > 0 ? markChunk.getSize() + 8 : 0)
  467. + (comtChunk.getSize() > 0 ? comtChunk.getSize() + 8 : 0)
  468. + (instChunk.getSize() > 0 ? instChunk.getSize() + 8 : 0));
  469. int audioBytes = (int) (lengthInSamples * ((bitsPerSample * numChannels) / 8));
  470. audioBytes += (audioBytes & 1);
  471. output->writeInt (chunkName ("FORM"));
  472. output->writeIntBigEndian (headerLen + audioBytes - 8);
  473. output->writeInt (chunkName ("AIFF"));
  474. output->writeInt (chunkName ("COMM"));
  475. output->writeIntBigEndian (18);
  476. output->writeShortBigEndian ((short) numChannels);
  477. output->writeIntBigEndian ((int) lengthInSamples);
  478. output->writeShortBigEndian ((short) bitsPerSample);
  479. uint8 sampleRateBytes[10] = { 0 };
  480. if (sampleRate <= 1)
  481. {
  482. sampleRateBytes[0] = 0x3f;
  483. sampleRateBytes[1] = 0xff;
  484. sampleRateBytes[2] = 0x80;
  485. }
  486. else
  487. {
  488. int mask = 0x40000000;
  489. sampleRateBytes[0] = 0x40;
  490. if (sampleRate >= mask)
  491. {
  492. jassertfalse;
  493. sampleRateBytes[1] = 0x1d;
  494. }
  495. else
  496. {
  497. int n = (int) sampleRate;
  498. int i;
  499. for (i = 0; i <= 32 ; ++i)
  500. {
  501. if ((n & mask) != 0)
  502. break;
  503. mask >>= 1;
  504. }
  505. n = n << (i + 1);
  506. sampleRateBytes[1] = (uint8) (29 - i);
  507. sampleRateBytes[2] = (uint8) ((n >> 24) & 0xff);
  508. sampleRateBytes[3] = (uint8) ((n >> 16) & 0xff);
  509. sampleRateBytes[4] = (uint8) ((n >> 8) & 0xff);
  510. sampleRateBytes[5] = (uint8) (n & 0xff);
  511. }
  512. }
  513. output->write (sampleRateBytes, 10);
  514. if (markChunk.getSize() > 0)
  515. {
  516. output->writeInt (chunkName ("MARK"));
  517. output->writeIntBigEndian ((int) markChunk.getSize());
  518. *output << markChunk;
  519. }
  520. if (comtChunk.getSize() > 0)
  521. {
  522. output->writeInt (chunkName ("COMT"));
  523. output->writeIntBigEndian ((int) comtChunk.getSize());
  524. *output << comtChunk;
  525. }
  526. if (instChunk.getSize() > 0)
  527. {
  528. output->writeInt (chunkName ("INST"));
  529. output->writeIntBigEndian ((int) instChunk.getSize());
  530. *output << instChunk;
  531. }
  532. output->writeInt (chunkName ("SSND"));
  533. output->writeIntBigEndian (audioBytes + 8);
  534. output->writeInt (0);
  535. output->writeInt (0);
  536. jassert (output->getPosition() == headerLen);
  537. }
  538. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter);
  539. };
  540. //==============================================================================
  541. AiffAudioFormat::AiffAudioFormat()
  542. : AudioFormat (TRANS (aiffFormatName), StringArray (aiffExtensions))
  543. {
  544. }
  545. AiffAudioFormat::~AiffAudioFormat()
  546. {
  547. }
  548. Array<int> AiffAudioFormat::getPossibleSampleRates()
  549. {
  550. const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 };
  551. return Array <int> (rates);
  552. }
  553. Array<int> AiffAudioFormat::getPossibleBitDepths()
  554. {
  555. const int depths[] = { 8, 16, 24, 0 };
  556. return Array <int> (depths);
  557. }
  558. bool AiffAudioFormat::canDoStereo() { return true; }
  559. bool AiffAudioFormat::canDoMono() { return true; }
  560. #if JUCE_MAC
  561. bool AiffAudioFormat::canHandleFile (const File& f)
  562. {
  563. if (AudioFormat::canHandleFile (f))
  564. return true;
  565. const OSType type = f.getMacOSType();
  566. return type == 'AIFF' || type == 'AIFC'
  567. || type == 'aiff' || type == 'aifc';
  568. }
  569. #endif
  570. AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails)
  571. {
  572. ScopedPointer <AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream));
  573. if (w->sampleRate > 0)
  574. return w.release();
  575. if (! deleteStreamIfOpeningFails)
  576. w->input = nullptr;
  577. return nullptr;
  578. }
  579. AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
  580. double sampleRate,
  581. unsigned int numberOfChannels,
  582. int bitsPerSample,
  583. const StringPairArray& metadataValues,
  584. int /*qualityOptionIndex*/)
  585. {
  586. if (getPossibleBitDepths().contains (bitsPerSample))
  587. return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, (unsigned int) bitsPerSample, metadataValues);
  588. return nullptr;
  589. }