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.

1114 lines
49KB

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