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.

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