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.

1215 lines
52KB

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