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.

1272 lines
54KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. static const char* const wavFormatName = "WAV file";
  18. static const char* const wavExtensions[] = { ".wav", ".bwf", 0 };
  19. //==============================================================================
  20. const char* const WavAudioFormat::bwavDescription = "bwav description";
  21. const char* const WavAudioFormat::bwavOriginator = "bwav originator";
  22. const char* const WavAudioFormat::bwavOriginatorRef = "bwav originator ref";
  23. const char* const WavAudioFormat::bwavOriginationDate = "bwav origination date";
  24. const char* const WavAudioFormat::bwavOriginationTime = "bwav origination time";
  25. const char* const WavAudioFormat::bwavTimeReference = "bwav time reference";
  26. const char* const WavAudioFormat::bwavCodingHistory = "bwav coding history";
  27. StringPairArray WavAudioFormat::createBWAVMetadata (const String& description,
  28. const String& originator,
  29. const String& originatorRef,
  30. const Time date,
  31. const int64 timeReferenceSamples,
  32. const String& codingHistory)
  33. {
  34. StringPairArray m;
  35. m.set (bwavDescription, description);
  36. m.set (bwavOriginator, originator);
  37. m.set (bwavOriginatorRef, originatorRef);
  38. m.set (bwavOriginationDate, date.formatted ("%Y-%m-%d"));
  39. m.set (bwavOriginationTime, date.formatted ("%H:%M:%S"));
  40. m.set (bwavTimeReference, String (timeReferenceSamples));
  41. m.set (bwavCodingHistory, codingHistory);
  42. return m;
  43. }
  44. const char* const WavAudioFormat::acidOneShot = "acid one shot";
  45. const char* const WavAudioFormat::acidRootSet = "acid root set";
  46. const char* const WavAudioFormat::acidStretch = "acid stretch";
  47. const char* const WavAudioFormat::acidDiskBased = "acid disk based";
  48. const char* const WavAudioFormat::acidizerFlag = "acidizer flag";
  49. const char* const WavAudioFormat::acidRootNote = "acid root note";
  50. const char* const WavAudioFormat::acidBeats = "acid beats";
  51. const char* const WavAudioFormat::acidDenominator = "acid denominator";
  52. const char* const WavAudioFormat::acidNumerator = "acid numerator";
  53. const char* const WavAudioFormat::acidTempo = "acid tempo";
  54. //==============================================================================
  55. namespace WavFileHelpers
  56. {
  57. inline int chunkName (const char* const name) noexcept { return (int) ByteOrder::littleEndianInt (name); }
  58. #if JUCE_MSVC
  59. #pragma pack (push, 1)
  60. #endif
  61. struct BWAVChunk
  62. {
  63. char description [256];
  64. char originator [32];
  65. char originatorRef [32];
  66. char originationDate [10];
  67. char originationTime [8];
  68. uint32 timeRefLow;
  69. uint32 timeRefHigh;
  70. uint16 version;
  71. uint8 umid[64];
  72. uint8 reserved[190];
  73. char codingHistory[1];
  74. void copyTo (StringPairArray& values, const int totalSize) const
  75. {
  76. values.set (WavAudioFormat::bwavDescription, String::fromUTF8 (description, sizeof (description)));
  77. values.set (WavAudioFormat::bwavOriginator, String::fromUTF8 (originator, sizeof (originator)));
  78. values.set (WavAudioFormat::bwavOriginatorRef, String::fromUTF8 (originatorRef, sizeof (originatorRef)));
  79. values.set (WavAudioFormat::bwavOriginationDate, String::fromUTF8 (originationDate, sizeof (originationDate)));
  80. values.set (WavAudioFormat::bwavOriginationTime, String::fromUTF8 (originationTime, sizeof (originationTime)));
  81. const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow);
  82. const uint32 timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh);
  83. const int64 time = (((int64)timeHigh) << 32) + timeLow;
  84. values.set (WavAudioFormat::bwavTimeReference, String (time));
  85. values.set (WavAudioFormat::bwavCodingHistory,
  86. String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory)));
  87. }
  88. static MemoryBlock createFrom (const StringPairArray& values)
  89. {
  90. const size_t sizeNeeded = sizeof (BWAVChunk) + values [WavAudioFormat::bwavCodingHistory].getNumBytesAsUTF8();
  91. MemoryBlock data ((sizeNeeded + 3) & ~3);
  92. data.fillWith (0);
  93. BWAVChunk* b = (BWAVChunk*) data.getData();
  94. // Allow these calls to overwrite an extra byte at the end, which is fine as long
  95. // as they get called in the right order..
  96. values [WavAudioFormat::bwavDescription] .copyToUTF8 (b->description, 257);
  97. values [WavAudioFormat::bwavOriginator] .copyToUTF8 (b->originator, 33);
  98. values [WavAudioFormat::bwavOriginatorRef] .copyToUTF8 (b->originatorRef, 33);
  99. values [WavAudioFormat::bwavOriginationDate].copyToUTF8 (b->originationDate, 11);
  100. values [WavAudioFormat::bwavOriginationTime].copyToUTF8 (b->originationTime, 9);
  101. const int64 time = values [WavAudioFormat::bwavTimeReference].getLargeIntValue();
  102. b->timeRefLow = ByteOrder::swapIfBigEndian ((uint32) (time & 0xffffffff));
  103. b->timeRefHigh = ByteOrder::swapIfBigEndian ((uint32) (time >> 32));
  104. values [WavAudioFormat::bwavCodingHistory].copyToUTF8 (b->codingHistory, 0x7fffffff);
  105. if (b->description[0] != 0
  106. || b->originator[0] != 0
  107. || b->originationDate[0] != 0
  108. || b->originationTime[0] != 0
  109. || b->codingHistory[0] != 0
  110. || time != 0)
  111. {
  112. return data;
  113. }
  114. return MemoryBlock();
  115. }
  116. } JUCE_PACKED;
  117. //==============================================================================
  118. struct SMPLChunk
  119. {
  120. struct SampleLoop
  121. {
  122. uint32 identifier;
  123. uint32 type; // these are different in AIFF and WAV
  124. uint32 start;
  125. uint32 end;
  126. uint32 fraction;
  127. uint32 playCount;
  128. } JUCE_PACKED;
  129. uint32 manufacturer;
  130. uint32 product;
  131. uint32 samplePeriod;
  132. uint32 midiUnityNote;
  133. uint32 midiPitchFraction;
  134. uint32 smpteFormat;
  135. uint32 smpteOffset;
  136. uint32 numSampleLoops;
  137. uint32 samplerData;
  138. SampleLoop loops[1];
  139. template <typename NameType>
  140. static void setValue (StringPairArray& values, NameType name, uint32 val)
  141. {
  142. values.set (name, String (ByteOrder::swapIfBigEndian (val)));
  143. }
  144. static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val)
  145. {
  146. setValue (values, "Loop" + String (prefix) + name, val);
  147. }
  148. void copyTo (StringPairArray& values, const int totalSize) const
  149. {
  150. setValue (values, "Manufacturer", manufacturer);
  151. setValue (values, "Product", product);
  152. setValue (values, "SamplePeriod", samplePeriod);
  153. setValue (values, "MidiUnityNote", midiUnityNote);
  154. setValue (values, "MidiPitchFraction", midiPitchFraction);
  155. setValue (values, "SmpteFormat", smpteFormat);
  156. setValue (values, "SmpteOffset", smpteOffset);
  157. setValue (values, "NumSampleLoops", numSampleLoops);
  158. setValue (values, "SamplerData", samplerData);
  159. for (int i = 0; i < (int) numSampleLoops; ++i)
  160. {
  161. if ((uint8*) (loops + (i + 1)) > ((uint8*) this) + totalSize)
  162. break;
  163. setValue (values, i, "Identifier", loops[i].identifier);
  164. setValue (values, i, "Type", loops[i].type);
  165. setValue (values, i, "Start", loops[i].start);
  166. setValue (values, i, "End", loops[i].end);
  167. setValue (values, i, "Fraction", loops[i].fraction);
  168. setValue (values, i, "PlayCount", loops[i].playCount);
  169. }
  170. }
  171. template <typename NameType>
  172. static uint32 getValue (const StringPairArray& values, NameType name, const char* def)
  173. {
  174. return ByteOrder::swapIfBigEndian ((uint32) values.getValue (name, def).getIntValue());
  175. }
  176. static uint32 getValue (const StringPairArray& values, int prefix, const char* name, const char* def)
  177. {
  178. return getValue (values, "Loop" + String (prefix) + name, def);
  179. }
  180. static MemoryBlock createFrom (const StringPairArray& values)
  181. {
  182. MemoryBlock data;
  183. const int numLoops = jmin (64, values.getValue ("NumSampleLoops", "0").getIntValue());
  184. if (numLoops > 0)
  185. {
  186. const size_t sizeNeeded = sizeof (SMPLChunk) + (size_t) (numLoops - 1) * sizeof (SampleLoop);
  187. data.setSize ((sizeNeeded + 3) & ~3, true);
  188. SMPLChunk* const s = static_cast <SMPLChunk*> (data.getData());
  189. s->manufacturer = getValue (values, "Manufacturer", "0");
  190. s->product = getValue (values, "Product", "0");
  191. s->samplePeriod = getValue (values, "SamplePeriod", "0");
  192. s->midiUnityNote = getValue (values, "MidiUnityNote", "60");
  193. s->midiPitchFraction = getValue (values, "MidiPitchFraction", "0");
  194. s->smpteFormat = getValue (values, "SmpteFormat", "0");
  195. s->smpteOffset = getValue (values, "SmpteOffset", "0");
  196. s->numSampleLoops = ByteOrder::swapIfBigEndian ((uint32) numLoops);
  197. s->samplerData = getValue (values, "SamplerData", "0");
  198. for (int i = 0; i < numLoops; ++i)
  199. {
  200. s->loops[i].identifier = getValue (values, i, "Identifier", "0");
  201. s->loops[i].type = getValue (values, i, "Type", "0");
  202. s->loops[i].start = getValue (values, i, "Start", "0");
  203. s->loops[i].end = getValue (values, i, "End", "0");
  204. s->loops[i].fraction = getValue (values, i, "Fraction", "0");
  205. s->loops[i].playCount = getValue (values, i, "PlayCount", "0");
  206. }
  207. }
  208. return data;
  209. }
  210. } JUCE_PACKED;
  211. //==============================================================================
  212. struct InstChunk
  213. {
  214. int8 baseNote;
  215. int8 detune;
  216. int8 gain;
  217. int8 lowNote;
  218. int8 highNote;
  219. int8 lowVelocity;
  220. int8 highVelocity;
  221. static void setValue (StringPairArray& values, const char* name, int val)
  222. {
  223. values.set (name, String (val));
  224. }
  225. void copyTo (StringPairArray& values) const
  226. {
  227. setValue (values, "MidiUnityNote", baseNote);
  228. setValue (values, "Detune", detune);
  229. setValue (values, "Gain", gain);
  230. setValue (values, "LowNote", lowNote);
  231. setValue (values, "HighNote", highNote);
  232. setValue (values, "LowVelocity", lowVelocity);
  233. setValue (values, "HighVelocity", highVelocity);
  234. }
  235. static int8 getValue (const StringPairArray& values, const char* name, const char* def)
  236. {
  237. return (int8) values.getValue (name, def).getIntValue();
  238. }
  239. static MemoryBlock createFrom (const StringPairArray& values)
  240. {
  241. MemoryBlock data;
  242. const StringArray& keys = values.getAllKeys();
  243. if (keys.contains ("LowNote", true) && keys.contains ("HighNote", true))
  244. {
  245. data.setSize (8, true);
  246. InstChunk* const inst = static_cast <InstChunk*> (data.getData());
  247. inst->baseNote = getValue (values, "MidiUnityNote", "60");
  248. inst->detune = getValue (values, "Detune", "0");
  249. inst->gain = getValue (values, "Gain", "0");
  250. inst->lowNote = getValue (values, "LowNote", "0");
  251. inst->highNote = getValue (values, "HighNote", "127");
  252. inst->lowVelocity = getValue (values, "LowVelocity", "1");
  253. inst->highVelocity = getValue (values, "HighVelocity", "127");
  254. }
  255. return data;
  256. }
  257. } JUCE_PACKED;
  258. //==============================================================================
  259. struct CueChunk
  260. {
  261. struct Cue
  262. {
  263. uint32 identifier;
  264. uint32 order;
  265. uint32 chunkID;
  266. uint32 chunkStart;
  267. uint32 blockStart;
  268. uint32 offset;
  269. } JUCE_PACKED;
  270. uint32 numCues;
  271. Cue cues[1];
  272. static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val)
  273. {
  274. values.set ("Cue" + String (prefix) + name, String (ByteOrder::swapIfBigEndian (val)));
  275. }
  276. void copyTo (StringPairArray& values, const int totalSize) const
  277. {
  278. values.set ("NumCuePoints", String (ByteOrder::swapIfBigEndian (numCues)));
  279. for (int i = 0; i < (int) numCues; ++i)
  280. {
  281. if ((uint8*) (cues + (i + 1)) > ((uint8*) this) + totalSize)
  282. break;
  283. setValue (values, i, "Identifier", cues[i].identifier);
  284. setValue (values, i, "Order", cues[i].order);
  285. setValue (values, i, "ChunkID", cues[i].chunkID);
  286. setValue (values, i, "ChunkStart", cues[i].chunkStart);
  287. setValue (values, i, "BlockStart", cues[i].blockStart);
  288. setValue (values, i, "Offset", cues[i].offset);
  289. }
  290. }
  291. static void create (MemoryBlock& data, const StringPairArray& values)
  292. {
  293. const int numCues = values.getValue ("NumCuePoints", "0").getIntValue();
  294. if (numCues > 0)
  295. {
  296. const size_t sizeNeeded = sizeof (CueChunk) + (size_t) (numCues - 1) * sizeof (Cue);
  297. data.setSize ((sizeNeeded + 3) & ~3, true);
  298. CueChunk* const c = static_cast <CueChunk*> (data.getData());
  299. c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues);
  300. const String dataChunkID (chunkName ("data"));
  301. int nextOrder = 0;
  302. #if JUCE_DEBUG
  303. Array<uint32> identifiers;
  304. #endif
  305. for (int i = 0; i < numCues; ++i)
  306. {
  307. const String prefix ("Cue" + String (i));
  308. uint32 identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue();
  309. #if JUCE_DEBUG
  310. jassert (! identifiers.contains (identifier));
  311. identifiers.add (identifier);
  312. #endif
  313. c->cues[i].identifier = ByteOrder::swapIfBigEndian ((uint32) identifier);
  314. const int order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue();
  315. nextOrder = jmax (nextOrder, order) + 1;
  316. c->cues[i].order = ByteOrder::swapIfBigEndian ((uint32) order);
  317. c->cues[i].chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue());
  318. c->cues[i].chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue());
  319. c->cues[i].blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue());
  320. c->cues[i].offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue());
  321. }
  322. }
  323. }
  324. } JUCE_PACKED;
  325. //==============================================================================
  326. namespace ListChunk
  327. {
  328. static int getValue (const StringPairArray& values, const String& name)
  329. {
  330. return values.getValue (name, "0").getIntValue();
  331. }
  332. static int getValue (const StringPairArray& values, const String& prefix, const char* name)
  333. {
  334. return getValue (values, prefix + name);
  335. }
  336. static void appendLabelOrNoteChunk (const StringPairArray& values, const String& prefix,
  337. const int chunkType, MemoryOutputStream& out)
  338. {
  339. const String label (values.getValue (prefix + "Text", prefix));
  340. const int labelLength = (int) label.getNumBytesAsUTF8() + 1;
  341. const int chunkLength = 4 + labelLength + (labelLength & 1);
  342. out.writeInt (chunkType);
  343. out.writeInt (chunkLength);
  344. out.writeInt (getValue (values, prefix, "Identifier"));
  345. out.write (label.toUTF8(), labelLength);
  346. if ((out.getDataSize() & 1) != 0)
  347. out.writeByte (0);
  348. }
  349. static void appendExtraChunk (const StringPairArray& values, const String& prefix, MemoryOutputStream& out)
  350. {
  351. const String text (values.getValue (prefix + "Text", prefix));
  352. const int textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator
  353. int chunkLength = textLength + 20 + (textLength & 1);
  354. out.writeInt (chunkName ("ltxt"));
  355. out.writeInt (chunkLength);
  356. out.writeInt (getValue (values, prefix, "Identifier"));
  357. out.writeInt (getValue (values, prefix, "SampleLength"));
  358. out.writeInt (getValue (values, prefix, "Purpose"));
  359. out.writeShort ((short) getValue (values, prefix, "Country"));
  360. out.writeShort ((short) getValue (values, prefix, "Language"));
  361. out.writeShort ((short) getValue (values, prefix, "Dialect"));
  362. out.writeShort ((short) getValue (values, prefix, "CodePage"));
  363. out.write (text.toUTF8(), textLength);
  364. if ((out.getDataSize() & 1) != 0)
  365. out.writeByte (0);
  366. }
  367. static void create (MemoryBlock& block, const StringPairArray& values)
  368. {
  369. const int numCueLabels = getValue (values, "NumCueLabels");
  370. const int numCueNotes = getValue (values, "NumCueNotes");
  371. const int numCueRegions = getValue (values, "NumCueRegions");
  372. if (numCueLabels > 0 || numCueNotes > 0 || numCueRegions > 0)
  373. {
  374. MemoryOutputStream out (block, false);
  375. for (int i = 0; i < numCueLabels; ++i)
  376. appendLabelOrNoteChunk (values, "CueLabel" + String (i), chunkName ("labl"), out);
  377. for (int i = 0; i < numCueNotes; ++i)
  378. appendLabelOrNoteChunk (values, "CueNote" + String (i), chunkName ("note"), out);
  379. for (int i = 0; i < numCueRegions; ++i)
  380. appendExtraChunk (values, "CueRegion" + String (i), out);
  381. }
  382. }
  383. }
  384. //==============================================================================
  385. struct AcidChunk
  386. {
  387. /** Reads an acid RIFF chunk from a stream positioned just after the size byte. */
  388. AcidChunk (InputStream& input, int length)
  389. {
  390. zerostruct (*this);
  391. input.read (this, jmin ((int) sizeof (*this), length));
  392. }
  393. void addToMetadata (StringPairArray& values) const
  394. {
  395. setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01);
  396. setBoolFlag (values, WavAudioFormat::acidRootSet, 0x02);
  397. setBoolFlag (values, WavAudioFormat::acidStretch, 0x04);
  398. setBoolFlag (values, WavAudioFormat::acidDiskBased, 0x08);
  399. setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10);
  400. if (flags & 0x02) // root note set
  401. values.set (WavAudioFormat::acidRootNote, String (rootNote));
  402. values.set (WavAudioFormat::acidBeats, String (numBeats));
  403. values.set (WavAudioFormat::acidDenominator, String (meterDenominator));
  404. values.set (WavAudioFormat::acidNumerator, String (meterNumerator));
  405. values.set (WavAudioFormat::acidTempo, String (tempo));
  406. }
  407. void setBoolFlag (StringPairArray& values, const char* name, int32 mask) const
  408. {
  409. values.set (name, (flags & mask) ? "1" : "0");
  410. }
  411. int32 flags;
  412. int16 rootNote;
  413. int16 reserved1;
  414. float reserved2;
  415. int32 numBeats;
  416. int16 meterDenominator;
  417. int16 meterNumerator;
  418. float tempo;
  419. } JUCE_PACKED;
  420. //==============================================================================
  421. struct ExtensibleWavSubFormat
  422. {
  423. uint32 data1;
  424. uint16 data2;
  425. uint16 data3;
  426. uint8 data4[8];
  427. } JUCE_PACKED;
  428. static const ExtensibleWavSubFormat pcmFormat = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
  429. static const ExtensibleWavSubFormat IEEEFloatFormat = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
  430. static const ExtensibleWavSubFormat ambisonicFormat = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } };
  431. struct DataSize64Chunk // chunk ID = 'ds64' if data size > 0xffffffff, 'JUNK' otherwise
  432. {
  433. uint32 riffSizeLow; // low 4 byte size of RF64 block
  434. uint32 riffSizeHigh; // high 4 byte size of RF64 block
  435. uint32 dataSizeLow; // low 4 byte size of data chunk
  436. uint32 dataSizeHigh; // high 4 byte size of data chunk
  437. uint32 sampleCountLow; // low 4 byte sample count of fact chunk
  438. uint32 sampleCountHigh; // high 4 byte sample count of fact chunk
  439. uint32 tableLength; // number of valid entries in array 'table'
  440. } JUCE_PACKED;
  441. #if JUCE_MSVC
  442. #pragma pack (pop)
  443. #endif
  444. }
  445. //==============================================================================
  446. class WavAudioFormatReader : public AudioFormatReader
  447. {
  448. public:
  449. WavAudioFormatReader (InputStream* const in)
  450. : AudioFormatReader (in, TRANS (wavFormatName)),
  451. bwavChunkStart (0),
  452. bwavSize (0),
  453. dataLength (0),
  454. isRF64 (false)
  455. {
  456. using namespace WavFileHelpers;
  457. uint64 len = 0;
  458. uint64 end = 0;
  459. int cueNoteIndex = 0;
  460. int cueLabelIndex = 0;
  461. int cueRegionIndex = 0;
  462. const int firstChunkType = input->readInt();
  463. if (firstChunkType == chunkName ("RF64"))
  464. {
  465. input->skipNextBytes (4); // size is -1 for RF64
  466. isRF64 = true;
  467. }
  468. else if (firstChunkType == chunkName ("RIFF"))
  469. {
  470. len = (uint64) (uint32) input->readInt();
  471. end = len + (uint64) input->getPosition();
  472. }
  473. else
  474. {
  475. return;
  476. }
  477. const int64 startOfRIFFChunk = input->getPosition();
  478. if (input->readInt() == chunkName ("WAVE"))
  479. {
  480. if (isRF64 && input->readInt() == chunkName ("ds64"))
  481. {
  482. const uint32 length = (uint32) input->readInt();
  483. if (length < 28)
  484. return;
  485. const int64 chunkEnd = input->getPosition() + length + (length & 1);
  486. len = (uint64) input->readInt64();
  487. end = len + (uint64) startOfRIFFChunk;
  488. dataLength = input->readInt64();
  489. input->setPosition (chunkEnd);
  490. }
  491. while ((uint64) input->getPosition() < end && ! input->isExhausted())
  492. {
  493. const int chunkType = input->readInt();
  494. uint32 length = (uint32) input->readInt();
  495. const int64 chunkEnd = input->getPosition() + length + (length & 1);
  496. if (chunkType == chunkName ("fmt "))
  497. {
  498. // read the format chunk
  499. const unsigned short format = (unsigned short) input->readShort();
  500. numChannels = (unsigned int) input->readShort();
  501. sampleRate = input->readInt();
  502. const int bytesPerSec = input->readInt();
  503. input->skipNextBytes (2);
  504. bitsPerSample = (unsigned int) (int) input->readShort();
  505. if (bitsPerSample > 64)
  506. {
  507. bytesPerFrame = bytesPerSec / (int) sampleRate;
  508. bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels;
  509. }
  510. else
  511. {
  512. bytesPerFrame = numChannels * bitsPerSample / 8;
  513. }
  514. if (format == 3)
  515. {
  516. usesFloatingPointData = true;
  517. }
  518. else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/)
  519. {
  520. if (length < 40) // too short
  521. {
  522. bytesPerFrame = 0;
  523. }
  524. else
  525. {
  526. input->skipNextBytes (4); // skip over size and bitsPerSample
  527. metadataValues.set ("ChannelMask", String (input->readInt()));
  528. ExtensibleWavSubFormat subFormat;
  529. subFormat.data1 = (uint32) input->readInt();
  530. subFormat.data2 = (uint16) input->readShort();
  531. subFormat.data3 = (uint16) input->readShort();
  532. input->read (subFormat.data4, sizeof (subFormat.data4));
  533. if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0
  534. && memcmp (&subFormat, &IEEEFloatFormat, sizeof (subFormat)) != 0
  535. && memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0)
  536. bytesPerFrame = 0;
  537. }
  538. }
  539. else if (format != 1)
  540. {
  541. bytesPerFrame = 0;
  542. }
  543. }
  544. else if (chunkType == chunkName ("data"))
  545. {
  546. if (! isRF64) // data size is expected to be -1, actual data size is in ds64 chunk
  547. dataLength = length;
  548. dataChunkStart = input->getPosition();
  549. lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0;
  550. }
  551. else if (chunkType == chunkName ("bext"))
  552. {
  553. bwavChunkStart = input->getPosition();
  554. bwavSize = length;
  555. HeapBlock <BWAVChunk> bwav;
  556. bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1);
  557. input->read (bwav, (int) length);
  558. bwav->copyTo (metadataValues, (int) length);
  559. }
  560. else if (chunkType == chunkName ("smpl"))
  561. {
  562. HeapBlock <SMPLChunk> smpl;
  563. smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1);
  564. input->read (smpl, (int) length);
  565. smpl->copyTo (metadataValues, (int) length);
  566. }
  567. else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which...
  568. {
  569. HeapBlock <InstChunk> inst;
  570. inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
  571. input->read (inst, (int) length);
  572. inst->copyTo (metadataValues);
  573. }
  574. else if (chunkType == chunkName ("cue "))
  575. {
  576. HeapBlock <CueChunk> cue;
  577. cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1);
  578. input->read (cue, (int) length);
  579. cue->copyTo (metadataValues, (int) length);
  580. }
  581. else if (chunkType == chunkName ("LIST"))
  582. {
  583. if (input->readInt() == chunkName ("adtl"))
  584. {
  585. while (input->getPosition() < chunkEnd)
  586. {
  587. const int adtlChunkType = input->readInt();
  588. const uint32 adtlLength = (uint32) input->readInt();
  589. const int64 adtlChunkEnd = input->getPosition() + (adtlLength + (adtlLength & 1));
  590. if (adtlChunkType == chunkName ("labl") || adtlChunkType == chunkName ("note"))
  591. {
  592. String prefix;
  593. if (adtlChunkType == chunkName ("labl"))
  594. prefix << "CueLabel" << cueLabelIndex++;
  595. else if (adtlChunkType == chunkName ("note"))
  596. prefix << "CueNote" << cueNoteIndex++;
  597. const uint32 identifier = (uint32) input->readInt();
  598. const int stringLength = (int) adtlLength - 4;
  599. MemoryBlock textBlock;
  600. input->readIntoMemoryBlock (textBlock, stringLength);
  601. metadataValues.set (prefix + "Identifier", String (identifier));
  602. metadataValues.set (prefix + "Text", textBlock.toString());
  603. }
  604. else if (adtlChunkType == chunkName ("ltxt"))
  605. {
  606. const String prefix ("CueRegion" + String (cueRegionIndex++));
  607. const uint32 identifier = (uint32) input->readInt();
  608. const uint32 sampleLength = (uint32) input->readInt();
  609. const uint32 purpose = (uint32) input->readInt();
  610. const uint16 country = (uint16) input->readInt();
  611. const uint16 language = (uint16) input->readInt();
  612. const uint16 dialect = (uint16) input->readInt();
  613. const uint16 codePage = (uint16) input->readInt();
  614. const uint32 stringLength = adtlLength - 20;
  615. MemoryBlock textBlock;
  616. input->readIntoMemoryBlock (textBlock, (int) stringLength);
  617. metadataValues.set (prefix + "Identifier", String (identifier));
  618. metadataValues.set (prefix + "SampleLength", String (sampleLength));
  619. metadataValues.set (prefix + "Purpose", String (purpose));
  620. metadataValues.set (prefix + "Country", String (country));
  621. metadataValues.set (prefix + "Language", String (language));
  622. metadataValues.set (prefix + "Dialect", String (dialect));
  623. metadataValues.set (prefix + "CodePage", String (codePage));
  624. metadataValues.set (prefix + "Text", textBlock.toString());
  625. }
  626. input->setPosition (adtlChunkEnd);
  627. }
  628. }
  629. }
  630. else if (chunkType == chunkName ("acid"))
  631. {
  632. AcidChunk (*input, length).addToMetadata (metadataValues);
  633. }
  634. else if (chunkEnd <= input->getPosition())
  635. {
  636. break;
  637. }
  638. input->setPosition (chunkEnd);
  639. }
  640. }
  641. if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex));
  642. if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex));
  643. if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex));
  644. if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV");
  645. }
  646. //==============================================================================
  647. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  648. int64 startSampleInFile, int numSamples)
  649. {
  650. clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
  651. startSampleInFile, numSamples, lengthInSamples);
  652. if (numSamples <= 0)
  653. return true;
  654. input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame);
  655. while (numSamples > 0)
  656. {
  657. const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
  658. char tempBuffer [tempBufSize];
  659. const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples);
  660. const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);
  661. if (bytesRead < numThisTime * bytesPerFrame)
  662. {
  663. jassert (bytesRead >= 0);
  664. zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead));
  665. }
  666. copySampleData (bitsPerSample, usesFloatingPointData,
  667. destSamples, startOffsetInDestBuffer, numDestChannels,
  668. tempBuffer, (int) numChannels, numThisTime);
  669. startOffsetInDestBuffer += numThisTime;
  670. numSamples -= numThisTime;
  671. }
  672. return true;
  673. }
  674. static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData,
  675. int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels,
  676. const void* sourceData, int numChannels, int numSamples) noexcept
  677. {
  678. switch (bitsPerSample)
  679. {
  680. case 8: ReadHelper<AudioData::Int32, AudioData::UInt8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
  681. case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
  682. case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
  683. case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
  684. else ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
  685. default: jassertfalse; break;
  686. }
  687. }
  688. int64 bwavChunkStart, bwavSize;
  689. int64 dataChunkStart, dataLength;
  690. int bytesPerFrame;
  691. bool isRF64;
  692. private:
  693. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader)
  694. };
  695. //==============================================================================
  696. class WavAudioFormatWriter : public AudioFormatWriter
  697. {
  698. public:
  699. WavAudioFormatWriter (OutputStream* const out, const double sampleRate_,
  700. const unsigned int numChannels_, const unsigned int bits,
  701. const StringPairArray& metadataValues)
  702. : AudioFormatWriter (out, TRANS (wavFormatName), sampleRate_, numChannels_, bits),
  703. lengthInSamples (0),
  704. bytesWritten (0),
  705. writeFailed (false)
  706. {
  707. using namespace WavFileHelpers;
  708. if (metadataValues.size() > 0)
  709. {
  710. // The meta data should have been santised for the WAV format.
  711. // If it was originally sourced from an AIFF file the MetaDataSource
  712. // key should be removed (or set to "WAV") once this has been done
  713. jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF");
  714. bwavChunk = BWAVChunk::createFrom (metadataValues);
  715. smplChunk = SMPLChunk::createFrom (metadataValues);
  716. instChunk = InstChunk::createFrom (metadataValues);
  717. CueChunk ::create (cueChunk, metadataValues);
  718. ListChunk::create (listChunk, metadataValues);
  719. }
  720. headerPosition = out->getPosition();
  721. writeHeader();
  722. }
  723. ~WavAudioFormatWriter()
  724. {
  725. if ((bytesWritten & 1) != 0) // pad to an even length
  726. {
  727. ++bytesWritten;
  728. output->writeByte (0);
  729. }
  730. writeHeader();
  731. }
  732. //==============================================================================
  733. bool write (const int** data, int numSamples)
  734. {
  735. jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel!
  736. if (writeFailed)
  737. return false;
  738. const size_t bytes = numChannels * (unsigned int) numSamples * bitsPerSample / 8;
  739. tempBlock.ensureSize (bytes, false);
  740. switch (bitsPerSample)
  741. {
  742. case 8: WriteHelper<AudioData::UInt8, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  743. case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  744. case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  745. case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  746. default: jassertfalse; break;
  747. }
  748. if (! output->write (tempBlock.getData(), bytes))
  749. {
  750. // failed to write to disk, so let's try writing the header.
  751. // If it's just run out of disk space, then if it does manage
  752. // to write the header, we'll still have a useable file..
  753. writeHeader();
  754. writeFailed = true;
  755. return false;
  756. }
  757. else
  758. {
  759. bytesWritten += bytes;
  760. lengthInSamples += numSamples;
  761. return true;
  762. }
  763. }
  764. private:
  765. MemoryBlock tempBlock, bwavChunk, smplChunk, instChunk, cueChunk, listChunk;
  766. uint64 lengthInSamples, bytesWritten;
  767. int64 headerPosition;
  768. bool writeFailed;
  769. static int getChannelMask (const int numChannels) noexcept
  770. {
  771. switch (numChannels)
  772. {
  773. case 1: return 0;
  774. case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
  775. case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
  776. case 6: return 1 + 2 + 4 + 8 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
  777. case 7: return 1 + 2 + 4 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
  778. case 8: return 1 + 2 + 4 + 8 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
  779. default: break;
  780. }
  781. return 0;
  782. }
  783. void writeHeader()
  784. {
  785. using namespace WavFileHelpers;
  786. const bool seekedOk = output->setPosition (headerPosition);
  787. (void) seekedOk;
  788. // if this fails, you've given it an output stream that can't seek! It needs
  789. // to be able to seek back to write the header
  790. jassert (seekedOk);
  791. const size_t bytesPerFrame = numChannels * bitsPerSample / 8;
  792. uint64 audioDataSize = bytesPerFrame * lengthInSamples;
  793. const bool isRF64 = (bytesWritten >= 0x100000000LL);
  794. const bool isWaveFmtEx = isRF64 || (numChannels > 2);
  795. int64 riffChunkSize = (int64) (4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */
  796. + 8 + audioDataSize + (audioDataSize & 1)
  797. + (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0)
  798. + (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0)
  799. + (instChunk.getSize() > 0 ? (8 + instChunk.getSize()) : 0)
  800. + (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0)
  801. + (listChunk.getSize() > 0 ? (12 + listChunk.getSize()) : 0)
  802. + (8 + 28)); // (ds64 chunk)
  803. riffChunkSize += (riffChunkSize & 1);
  804. output->writeInt (chunkName (isRF64 ? "RF64" : "RIFF"));
  805. output->writeInt (isRF64 ? -1 : (int) riffChunkSize);
  806. output->writeInt (chunkName ("WAVE"));
  807. if (! isRF64)
  808. {
  809. output->writeInt (chunkName ("JUNK"));
  810. output->writeInt (28 + (isWaveFmtEx? 0 : 24));
  811. output->writeRepeatedByte (0, 28 /* ds64 */ + (isWaveFmtEx? 0 : 24));
  812. }
  813. else
  814. {
  815. // write ds64 chunk
  816. output->writeInt (chunkName ("ds64"));
  817. output->writeInt (28); // chunk size for uncompressed data (no table)
  818. output->writeInt64 (riffChunkSize);
  819. output->writeInt64 ((int64) audioDataSize);
  820. output->writeRepeatedByte (0, 12);
  821. }
  822. output->writeInt (chunkName ("fmt "));
  823. if (isWaveFmtEx)
  824. {
  825. output->writeInt (40); // chunk size
  826. output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE
  827. }
  828. else
  829. {
  830. output->writeInt (16); // chunk size
  831. output->writeShort (bitsPerSample < 32 ? (short) 1 /*WAVE_FORMAT_PCM*/
  832. : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/);
  833. }
  834. output->writeShort ((short) numChannels);
  835. output->writeInt ((int) sampleRate);
  836. output->writeInt ((int) (bytesPerFrame * sampleRate)); // nAvgBytesPerSec
  837. output->writeShort ((short) bytesPerFrame); // nBlockAlign
  838. output->writeShort ((short) bitsPerSample); // wBitsPerSample
  839. if (isWaveFmtEx)
  840. {
  841. output->writeShort (22); // cbSize (size of the extension)
  842. output->writeShort ((short) bitsPerSample); // wValidBitsPerSample
  843. output->writeInt (getChannelMask ((int) numChannels));
  844. const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat;
  845. output->writeInt ((int) subFormat.data1);
  846. output->writeShort ((short) subFormat.data2);
  847. output->writeShort ((short) subFormat.data3);
  848. output->write (subFormat.data4, sizeof (subFormat.data4));
  849. }
  850. if (bwavChunk.getSize() > 0)
  851. {
  852. output->writeInt (chunkName ("bext"));
  853. output->writeInt ((int) bwavChunk.getSize());
  854. *output << bwavChunk;
  855. }
  856. if (smplChunk.getSize() > 0)
  857. {
  858. output->writeInt (chunkName ("smpl"));
  859. output->writeInt ((int) smplChunk.getSize());
  860. *output << smplChunk;
  861. }
  862. if (instChunk.getSize() > 0)
  863. {
  864. output->writeInt (chunkName ("inst"));
  865. output->writeInt (7);
  866. *output << instChunk;
  867. }
  868. if (cueChunk.getSize() > 0)
  869. {
  870. output->writeInt (chunkName ("cue "));
  871. output->writeInt ((int) cueChunk.getSize());
  872. *output << cueChunk;
  873. }
  874. if (listChunk.getSize() > 0)
  875. {
  876. output->writeInt (chunkName ("LIST"));
  877. output->writeInt ((int) listChunk.getSize() + 4);
  878. output->writeInt (chunkName ("adtl"));
  879. *output << listChunk;
  880. }
  881. output->writeInt (chunkName ("data"));
  882. output->writeInt (isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame));
  883. usesFloatingPointData = (bitsPerSample == 32);
  884. }
  885. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatWriter)
  886. };
  887. //==============================================================================
  888. class MemoryMappedWavReader : public MemoryMappedAudioFormatReader
  889. {
  890. public:
  891. MemoryMappedWavReader (const File& file, const WavAudioFormatReader& reader)
  892. : MemoryMappedAudioFormatReader (file, reader, reader.dataChunkStart,
  893. reader.dataLength, reader.bytesPerFrame)
  894. {
  895. }
  896. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  897. int64 startSampleInFile, int numSamples)
  898. {
  899. clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
  900. startSampleInFile, numSamples, lengthInSamples);
  901. if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
  902. {
  903. jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
  904. return false;
  905. }
  906. WavAudioFormatReader::copySampleData (bitsPerSample, usesFloatingPointData,
  907. destSamples, startOffsetInDestBuffer, numDestChannels,
  908. sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
  909. return true;
  910. }
  911. void readMaxLevels (int64 startSampleInFile, int64 numSamples,
  912. float& min0, float& max0, float& min1, float& max1)
  913. {
  914. if (numSamples <= 0)
  915. {
  916. min0 = max0 = min1 = max1 = 0;
  917. return;
  918. }
  919. if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
  920. {
  921. jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
  922. min0 = max0 = min1 = max1 = 0;
  923. return;
  924. }
  925. switch (bitsPerSample)
  926. {
  927. case 8: scanMinAndMax<AudioData::UInt8> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
  928. case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
  929. case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
  930. case 32: if (usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, min0, max0, min1, max1);
  931. else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
  932. default: jassertfalse; break;
  933. }
  934. }
  935. private:
  936. template <typename SampleType>
  937. void scanMinAndMax (int64 startSampleInFile, int64 numSamples,
  938. float& min0, float& max0, float& min1, float& max1) const noexcept
  939. {
  940. scanMinAndMaxInterleaved<SampleType, AudioData::LittleEndian> (0, startSampleInFile, numSamples, min0, max0);
  941. if (numChannels > 1)
  942. scanMinAndMaxInterleaved<SampleType, AudioData::LittleEndian> (1, startSampleInFile, numSamples, min1, max1);
  943. else
  944. min1 = max1 = 0;
  945. }
  946. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedWavReader)
  947. };
  948. //==============================================================================
  949. WavAudioFormat::WavAudioFormat()
  950. : AudioFormat (TRANS (wavFormatName), StringArray (wavExtensions))
  951. {
  952. }
  953. WavAudioFormat::~WavAudioFormat()
  954. {
  955. }
  956. Array<int> WavAudioFormat::getPossibleSampleRates()
  957. {
  958. const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000,
  959. 44100, 48000, 88200, 96000, 176400, 192000 };
  960. return Array<int> (rates, numElementsInArray (rates));
  961. }
  962. Array<int> WavAudioFormat::getPossibleBitDepths()
  963. {
  964. const int depths[] = { 8, 16, 24, 32 };
  965. return Array<int> (depths, numElementsInArray (depths));
  966. }
  967. bool WavAudioFormat::canDoStereo() { return true; }
  968. bool WavAudioFormat::canDoMono() { return true; }
  969. AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream,
  970. const bool deleteStreamIfOpeningFails)
  971. {
  972. ScopedPointer <WavAudioFormatReader> r (new WavAudioFormatReader (sourceStream));
  973. if (r->sampleRate > 0 && r->numChannels > 0)
  974. return r.release();
  975. if (! deleteStreamIfOpeningFails)
  976. r->input = nullptr;
  977. return nullptr;
  978. }
  979. MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (const File& file)
  980. {
  981. if (FileInputStream* fin = file.createInputStream())
  982. {
  983. WavAudioFormatReader reader (fin);
  984. if (reader.lengthInSamples > 0)
  985. return new MemoryMappedWavReader (file, reader);
  986. }
  987. return nullptr;
  988. }
  989. AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate,
  990. unsigned int numChannels, int bitsPerSample,
  991. const StringPairArray& metadataValues, int /*qualityOptionIndex*/)
  992. {
  993. if (getPossibleBitDepths().contains (bitsPerSample))
  994. return new WavAudioFormatWriter (out, sampleRate, (unsigned int) numChannels,
  995. (unsigned int) bitsPerSample, metadataValues);
  996. return nullptr;
  997. }
  998. namespace WavFileHelpers
  999. {
  1000. static bool slowCopyWavFileWithNewMetadata (const File& file, const StringPairArray& metadata)
  1001. {
  1002. TemporaryFile tempFile (file);
  1003. WavAudioFormat wav;
  1004. ScopedPointer <AudioFormatReader> reader (wav.createReaderFor (file.createInputStream(), true));
  1005. if (reader != nullptr)
  1006. {
  1007. ScopedPointer <OutputStream> outStream (tempFile.getFile().createOutputStream());
  1008. if (outStream != nullptr)
  1009. {
  1010. ScopedPointer <AudioFormatWriter> writer (wav.createWriterFor (outStream, reader->sampleRate,
  1011. reader->numChannels, (int) reader->bitsPerSample,
  1012. metadata, 0));
  1013. if (writer != nullptr)
  1014. {
  1015. outStream.release();
  1016. bool ok = writer->writeFromAudioReader (*reader, 0, -1);
  1017. writer = nullptr;
  1018. reader = nullptr;
  1019. return ok && tempFile.overwriteTargetFileWithTemporary();
  1020. }
  1021. }
  1022. }
  1023. return false;
  1024. }
  1025. }
  1026. bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata)
  1027. {
  1028. using namespace WavFileHelpers;
  1029. ScopedPointer <WavAudioFormatReader> reader (static_cast <WavAudioFormatReader*> (createReaderFor (wavFile.createInputStream(), true)));
  1030. if (reader != nullptr)
  1031. {
  1032. const int64 bwavPos = reader->bwavChunkStart;
  1033. const int64 bwavSize = reader->bwavSize;
  1034. reader = nullptr;
  1035. if (bwavSize > 0)
  1036. {
  1037. MemoryBlock chunk (BWAVChunk::createFrom (newMetadata));
  1038. if (chunk.getSize() <= (size_t) bwavSize)
  1039. {
  1040. // the new one will fit in the space available, so write it directly..
  1041. const int64 oldSize = wavFile.getSize();
  1042. {
  1043. FileOutputStream out (wavFile);
  1044. if (! out.failedToOpen())
  1045. {
  1046. out.setPosition (bwavPos);
  1047. out << chunk;
  1048. out.setPosition (oldSize);
  1049. }
  1050. }
  1051. jassert (wavFile.getSize() == oldSize);
  1052. return true;
  1053. }
  1054. }
  1055. }
  1056. return slowCopyWavFileWithNewMetadata (wavFile, newMetadata);
  1057. }