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.

1273 lines
55KB

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