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.

1763 lines
79KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. static const char* const wavFormatName = "WAV file";
  18. //==============================================================================
  19. const char* const WavAudioFormat::bwavDescription = "bwav description";
  20. const char* const WavAudioFormat::bwavOriginator = "bwav originator";
  21. const char* const WavAudioFormat::bwavOriginatorRef = "bwav originator ref";
  22. const char* const WavAudioFormat::bwavOriginationDate = "bwav origination date";
  23. const char* const WavAudioFormat::bwavOriginationTime = "bwav origination time";
  24. const char* const WavAudioFormat::bwavTimeReference = "bwav time reference";
  25. const char* const WavAudioFormat::bwavCodingHistory = "bwav coding history";
  26. StringPairArray WavAudioFormat::createBWAVMetadata (const String& description,
  27. const String& originator,
  28. const String& originatorRef,
  29. Time date,
  30. const int64 timeReferenceSamples,
  31. const String& codingHistory)
  32. {
  33. StringPairArray m;
  34. m.set (bwavDescription, description);
  35. m.set (bwavOriginator, originator);
  36. m.set (bwavOriginatorRef, originatorRef);
  37. m.set (bwavOriginationDate, date.formatted ("%Y-%m-%d"));
  38. m.set (bwavOriginationTime, date.formatted ("%H:%M:%S"));
  39. m.set (bwavTimeReference, String (timeReferenceSamples));
  40. m.set (bwavCodingHistory, codingHistory);
  41. return m;
  42. }
  43. const char* const WavAudioFormat::acidOneShot = "acid one shot";
  44. const char* const WavAudioFormat::acidRootSet = "acid root set";
  45. const char* const WavAudioFormat::acidStretch = "acid stretch";
  46. const char* const WavAudioFormat::acidDiskBased = "acid disk based";
  47. const char* const WavAudioFormat::acidizerFlag = "acidizer flag";
  48. const char* const WavAudioFormat::acidRootNote = "acid root note";
  49. const char* const WavAudioFormat::acidBeats = "acid beats";
  50. const char* const WavAudioFormat::acidDenominator = "acid denominator";
  51. const char* const WavAudioFormat::acidNumerator = "acid numerator";
  52. const char* const WavAudioFormat::acidTempo = "acid tempo";
  53. const char* const WavAudioFormat::riffInfoArchivalLocation = "IARL";
  54. const char* const WavAudioFormat::riffInfoArtist = "IART";
  55. const char* const WavAudioFormat::riffInfoBaseURL = "IBSU";
  56. const char* const WavAudioFormat::riffInfoCinematographer = "ICNM";
  57. const char* const WavAudioFormat::riffInfoComment = "CMNT";
  58. const char* const WavAudioFormat::riffInfoComments = "COMM";
  59. const char* const WavAudioFormat::riffInfoCommissioned = "ICMS";
  60. const char* const WavAudioFormat::riffInfoCopyright = "ICOP";
  61. const char* const WavAudioFormat::riffInfoCostumeDesigner = "ICDS";
  62. const char* const WavAudioFormat::riffInfoCountry = "ICNT";
  63. const char* const WavAudioFormat::riffInfoCropped = "ICRP";
  64. const char* const WavAudioFormat::riffInfoDateCreated = "ICRD";
  65. const char* const WavAudioFormat::riffInfoDateTimeOriginal = "IDIT";
  66. const char* const WavAudioFormat::riffInfoDefaultAudioStream = "ICAS";
  67. const char* const WavAudioFormat::riffInfoDimension = "IDIM";
  68. const char* const WavAudioFormat::riffInfoDirectory = "DIRC";
  69. const char* const WavAudioFormat::riffInfoDistributedBy = "IDST";
  70. const char* const WavAudioFormat::riffInfoDotsPerInch = "IDPI";
  71. const char* const WavAudioFormat::riffInfoEditedBy = "IEDT";
  72. const char* const WavAudioFormat::riffInfoEighthLanguage = "IAS8";
  73. const char* const WavAudioFormat::riffInfoEncodedBy = "CODE";
  74. const char* const WavAudioFormat::riffInfoEndTimecode = "TCDO";
  75. const char* const WavAudioFormat::riffInfoEngineer = "IENG";
  76. const char* const WavAudioFormat::riffInfoFifthLanguage = "IAS5";
  77. const char* const WavAudioFormat::riffInfoFirstLanguage = "IAS1";
  78. const char* const WavAudioFormat::riffInfoFourthLanguage = "IAS4";
  79. const char* const WavAudioFormat::riffInfoGenre = "GENR";
  80. const char* const WavAudioFormat::riffInfoKeywords = "IKEY";
  81. const char* const WavAudioFormat::riffInfoLanguage = "LANG";
  82. const char* const WavAudioFormat::riffInfoLength = "TLEN";
  83. const char* const WavAudioFormat::riffInfoLightness = "ILGT";
  84. const char* const WavAudioFormat::riffInfoLocation = "LOCA";
  85. const char* const WavAudioFormat::riffInfoLogoIconURL = "ILIU";
  86. const char* const WavAudioFormat::riffInfoLogoURL = "ILGU";
  87. const char* const WavAudioFormat::riffInfoMedium = "IMED";
  88. const char* const WavAudioFormat::riffInfoMoreInfoBannerImage = "IMBI";
  89. const char* const WavAudioFormat::riffInfoMoreInfoBannerURL = "IMBU";
  90. const char* const WavAudioFormat::riffInfoMoreInfoText = "IMIT";
  91. const char* const WavAudioFormat::riffInfoMoreInfoURL = "IMIU";
  92. const char* const WavAudioFormat::riffInfoMusicBy = "IMUS";
  93. const char* const WavAudioFormat::riffInfoNinthLanguage = "IAS9";
  94. const char* const WavAudioFormat::riffInfoNumberOfParts = "PRT2";
  95. const char* const WavAudioFormat::riffInfoOrganisation = "TORG";
  96. const char* const WavAudioFormat::riffInfoPart = "PRT1";
  97. const char* const WavAudioFormat::riffInfoProducedBy = "IPRO";
  98. const char* const WavAudioFormat::riffInfoProductionDesigner = "IPDS";
  99. const char* const WavAudioFormat::riffInfoProductionStudio = "ISDT";
  100. const char* const WavAudioFormat::riffInfoRate = "RATE";
  101. const char* const WavAudioFormat::riffInfoRated = "AGES";
  102. const char* const WavAudioFormat::riffInfoRating = "IRTD";
  103. const char* const WavAudioFormat::riffInfoRippedBy = "IRIP";
  104. const char* const WavAudioFormat::riffInfoSecondaryGenre = "ISGN";
  105. const char* const WavAudioFormat::riffInfoSecondLanguage = "IAS2";
  106. const char* const WavAudioFormat::riffInfoSeventhLanguage = "IAS7";
  107. const char* const WavAudioFormat::riffInfoSharpness = "ISHP";
  108. const char* const WavAudioFormat::riffInfoSixthLanguage = "IAS6";
  109. const char* const WavAudioFormat::riffInfoSoftware = "ISFT";
  110. const char* const WavAudioFormat::riffInfoSoundSchemeTitle = "DISP";
  111. const char* const WavAudioFormat::riffInfoSource = "ISRC";
  112. const char* const WavAudioFormat::riffInfoSourceFrom = "ISRF";
  113. const char* const WavAudioFormat::riffInfoStarring_ISTR = "ISTR";
  114. const char* const WavAudioFormat::riffInfoStarring_STAR = "STAR";
  115. const char* const WavAudioFormat::riffInfoStartTimecode = "TCOD";
  116. const char* const WavAudioFormat::riffInfoStatistics = "STAT";
  117. const char* const WavAudioFormat::riffInfoSubject = "ISBJ";
  118. const char* const WavAudioFormat::riffInfoTapeName = "TAPE";
  119. const char* const WavAudioFormat::riffInfoTechnician = "ITCH";
  120. const char* const WavAudioFormat::riffInfoThirdLanguage = "IAS3";
  121. const char* const WavAudioFormat::riffInfoTimeCode = "ISMP";
  122. const char* const WavAudioFormat::riffInfoTitle = "INAM";
  123. const char* const WavAudioFormat::riffInfoTrackNumber = "TRCK";
  124. const char* const WavAudioFormat::riffInfoURL = "TURL";
  125. const char* const WavAudioFormat::riffInfoVegasVersionMajor = "VMAJ";
  126. const char* const WavAudioFormat::riffInfoVegasVersionMinor = "VMIN";
  127. const char* const WavAudioFormat::riffInfoVersion = "TVER";
  128. const char* const WavAudioFormat::riffInfoWatermarkURL = "IWMU";
  129. const char* const WavAudioFormat::riffInfoWrittenBy = "IWRI";
  130. const char* const WavAudioFormat::riffInfoYear = "YEAR";
  131. const char* const WavAudioFormat::ISRC = "ISRC";
  132. const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info";
  133. //==============================================================================
  134. namespace WavFileHelpers
  135. {
  136. inline int chunkName (const char* const name) noexcept { return (int) ByteOrder::littleEndianInt (name); }
  137. inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; }
  138. #if JUCE_MSVC
  139. #pragma pack (push, 1)
  140. #endif
  141. struct BWAVChunk
  142. {
  143. char description [256];
  144. char originator [32];
  145. char originatorRef [32];
  146. char originationDate [10];
  147. char originationTime [8];
  148. uint32 timeRefLow;
  149. uint32 timeRefHigh;
  150. uint16 version;
  151. uint8 umid[64];
  152. uint8 reserved[190];
  153. char codingHistory[1];
  154. void copyTo (StringPairArray& values, const int totalSize) const
  155. {
  156. values.set (WavAudioFormat::bwavDescription, String::fromUTF8 (description, sizeof (description)));
  157. values.set (WavAudioFormat::bwavOriginator, String::fromUTF8 (originator, sizeof (originator)));
  158. values.set (WavAudioFormat::bwavOriginatorRef, String::fromUTF8 (originatorRef, sizeof (originatorRef)));
  159. values.set (WavAudioFormat::bwavOriginationDate, String::fromUTF8 (originationDate, sizeof (originationDate)));
  160. values.set (WavAudioFormat::bwavOriginationTime, String::fromUTF8 (originationTime, sizeof (originationTime)));
  161. const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow);
  162. const uint32 timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh);
  163. const int64 time = (((int64) timeHigh) << 32) + timeLow;
  164. values.set (WavAudioFormat::bwavTimeReference, String (time));
  165. values.set (WavAudioFormat::bwavCodingHistory,
  166. String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory)));
  167. }
  168. static MemoryBlock createFrom (const StringPairArray& values)
  169. {
  170. MemoryBlock data (roundUpSize (sizeof (BWAVChunk) + values [WavAudioFormat::bwavCodingHistory].getNumBytesAsUTF8()));
  171. data.fillWith (0);
  172. BWAVChunk* b = (BWAVChunk*) data.getData();
  173. // Allow these calls to overwrite an extra byte at the end, which is fine as long
  174. // as they get called in the right order..
  175. values [WavAudioFormat::bwavDescription] .copyToUTF8 (b->description, 257);
  176. values [WavAudioFormat::bwavOriginator] .copyToUTF8 (b->originator, 33);
  177. values [WavAudioFormat::bwavOriginatorRef] .copyToUTF8 (b->originatorRef, 33);
  178. values [WavAudioFormat::bwavOriginationDate].copyToUTF8 (b->originationDate, 11);
  179. values [WavAudioFormat::bwavOriginationTime].copyToUTF8 (b->originationTime, 9);
  180. const int64 time = values [WavAudioFormat::bwavTimeReference].getLargeIntValue();
  181. b->timeRefLow = ByteOrder::swapIfBigEndian ((uint32) (time & 0xffffffff));
  182. b->timeRefHigh = ByteOrder::swapIfBigEndian ((uint32) (time >> 32));
  183. values [WavAudioFormat::bwavCodingHistory].copyToUTF8 (b->codingHistory, 0x7fffffff);
  184. if (b->description[0] != 0
  185. || b->originator[0] != 0
  186. || b->originationDate[0] != 0
  187. || b->originationTime[0] != 0
  188. || b->codingHistory[0] != 0
  189. || time != 0)
  190. {
  191. return data;
  192. }
  193. return MemoryBlock();
  194. }
  195. } JUCE_PACKED;
  196. //==============================================================================
  197. struct SMPLChunk
  198. {
  199. struct SampleLoop
  200. {
  201. uint32 identifier;
  202. uint32 type; // these are different in AIFF and WAV
  203. uint32 start;
  204. uint32 end;
  205. uint32 fraction;
  206. uint32 playCount;
  207. } JUCE_PACKED;
  208. uint32 manufacturer;
  209. uint32 product;
  210. uint32 samplePeriod;
  211. uint32 midiUnityNote;
  212. uint32 midiPitchFraction;
  213. uint32 smpteFormat;
  214. uint32 smpteOffset;
  215. uint32 numSampleLoops;
  216. uint32 samplerData;
  217. SampleLoop loops[1];
  218. template <typename NameType>
  219. static void setValue (StringPairArray& values, NameType name, uint32 val)
  220. {
  221. values.set (name, String (ByteOrder::swapIfBigEndian (val)));
  222. }
  223. static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val)
  224. {
  225. setValue (values, "Loop" + String (prefix) + name, val);
  226. }
  227. void copyTo (StringPairArray& values, const int totalSize) const
  228. {
  229. setValue (values, "Manufacturer", manufacturer);
  230. setValue (values, "Product", product);
  231. setValue (values, "SamplePeriod", samplePeriod);
  232. setValue (values, "MidiUnityNote", midiUnityNote);
  233. setValue (values, "MidiPitchFraction", midiPitchFraction);
  234. setValue (values, "SmpteFormat", smpteFormat);
  235. setValue (values, "SmpteOffset", smpteOffset);
  236. setValue (values, "NumSampleLoops", numSampleLoops);
  237. setValue (values, "SamplerData", samplerData);
  238. for (int i = 0; i < (int) numSampleLoops; ++i)
  239. {
  240. if ((uint8*) (loops + (i + 1)) > ((uint8*) this) + totalSize)
  241. break;
  242. setValue (values, i, "Identifier", loops[i].identifier);
  243. setValue (values, i, "Type", loops[i].type);
  244. setValue (values, i, "Start", loops[i].start);
  245. setValue (values, i, "End", loops[i].end);
  246. setValue (values, i, "Fraction", loops[i].fraction);
  247. setValue (values, i, "PlayCount", loops[i].playCount);
  248. }
  249. }
  250. template <typename NameType>
  251. static uint32 getValue (const StringPairArray& values, NameType name, const char* def)
  252. {
  253. return ByteOrder::swapIfBigEndian ((uint32) values.getValue (name, def).getIntValue());
  254. }
  255. static uint32 getValue (const StringPairArray& values, int prefix, const char* name, const char* def)
  256. {
  257. return getValue (values, "Loop" + String (prefix) + name, def);
  258. }
  259. static MemoryBlock createFrom (const StringPairArray& values)
  260. {
  261. MemoryBlock data;
  262. const int numLoops = jmin (64, values.getValue ("NumSampleLoops", "0").getIntValue());
  263. if (numLoops > 0)
  264. {
  265. data.setSize (roundUpSize (sizeof (SMPLChunk) + (size_t) (numLoops - 1) * sizeof (SampleLoop)), true);
  266. SMPLChunk* const s = static_cast<SMPLChunk*> (data.getData());
  267. s->manufacturer = getValue (values, "Manufacturer", "0");
  268. s->product = getValue (values, "Product", "0");
  269. s->samplePeriod = getValue (values, "SamplePeriod", "0");
  270. s->midiUnityNote = getValue (values, "MidiUnityNote", "60");
  271. s->midiPitchFraction = getValue (values, "MidiPitchFraction", "0");
  272. s->smpteFormat = getValue (values, "SmpteFormat", "0");
  273. s->smpteOffset = getValue (values, "SmpteOffset", "0");
  274. s->numSampleLoops = ByteOrder::swapIfBigEndian ((uint32) numLoops);
  275. s->samplerData = getValue (values, "SamplerData", "0");
  276. for (int i = 0; i < numLoops; ++i)
  277. {
  278. SampleLoop& loop = s->loops[i];
  279. loop.identifier = getValue (values, i, "Identifier", "0");
  280. loop.type = getValue (values, i, "Type", "0");
  281. loop.start = getValue (values, i, "Start", "0");
  282. loop.end = getValue (values, i, "End", "0");
  283. loop.fraction = getValue (values, i, "Fraction", "0");
  284. loop.playCount = getValue (values, i, "PlayCount", "0");
  285. }
  286. }
  287. return data;
  288. }
  289. } JUCE_PACKED;
  290. //==============================================================================
  291. struct InstChunk
  292. {
  293. int8 baseNote;
  294. int8 detune;
  295. int8 gain;
  296. int8 lowNote;
  297. int8 highNote;
  298. int8 lowVelocity;
  299. int8 highVelocity;
  300. static void setValue (StringPairArray& values, const char* name, int val)
  301. {
  302. values.set (name, String (val));
  303. }
  304. void copyTo (StringPairArray& values) const
  305. {
  306. setValue (values, "MidiUnityNote", baseNote);
  307. setValue (values, "Detune", detune);
  308. setValue (values, "Gain", gain);
  309. setValue (values, "LowNote", lowNote);
  310. setValue (values, "HighNote", highNote);
  311. setValue (values, "LowVelocity", lowVelocity);
  312. setValue (values, "HighVelocity", highVelocity);
  313. }
  314. static int8 getValue (const StringPairArray& values, const char* name, const char* def)
  315. {
  316. return (int8) values.getValue (name, def).getIntValue();
  317. }
  318. static MemoryBlock createFrom (const StringPairArray& values)
  319. {
  320. MemoryBlock data;
  321. const StringArray& keys = values.getAllKeys();
  322. if (keys.contains ("LowNote", true) && keys.contains ("HighNote", true))
  323. {
  324. data.setSize (8, true);
  325. InstChunk* const inst = static_cast<InstChunk*> (data.getData());
  326. inst->baseNote = getValue (values, "MidiUnityNote", "60");
  327. inst->detune = getValue (values, "Detune", "0");
  328. inst->gain = getValue (values, "Gain", "0");
  329. inst->lowNote = getValue (values, "LowNote", "0");
  330. inst->highNote = getValue (values, "HighNote", "127");
  331. inst->lowVelocity = getValue (values, "LowVelocity", "1");
  332. inst->highVelocity = getValue (values, "HighVelocity", "127");
  333. }
  334. return data;
  335. }
  336. } JUCE_PACKED;
  337. //==============================================================================
  338. struct CueChunk
  339. {
  340. struct Cue
  341. {
  342. uint32 identifier;
  343. uint32 order;
  344. uint32 chunkID;
  345. uint32 chunkStart;
  346. uint32 blockStart;
  347. uint32 offset;
  348. } JUCE_PACKED;
  349. uint32 numCues;
  350. Cue cues[1];
  351. static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val)
  352. {
  353. values.set ("Cue" + String (prefix) + name, String (ByteOrder::swapIfBigEndian (val)));
  354. }
  355. void copyTo (StringPairArray& values, const int totalSize) const
  356. {
  357. values.set ("NumCuePoints", String (ByteOrder::swapIfBigEndian (numCues)));
  358. for (int i = 0; i < (int) numCues; ++i)
  359. {
  360. if ((uint8*) (cues + (i + 1)) > ((uint8*) this) + totalSize)
  361. break;
  362. setValue (values, i, "Identifier", cues[i].identifier);
  363. setValue (values, i, "Order", cues[i].order);
  364. setValue (values, i, "ChunkID", cues[i].chunkID);
  365. setValue (values, i, "ChunkStart", cues[i].chunkStart);
  366. setValue (values, i, "BlockStart", cues[i].blockStart);
  367. setValue (values, i, "Offset", cues[i].offset);
  368. }
  369. }
  370. static MemoryBlock createFrom (const StringPairArray& values)
  371. {
  372. MemoryBlock data;
  373. const int numCues = values.getValue ("NumCuePoints", "0").getIntValue();
  374. if (numCues > 0)
  375. {
  376. data.setSize (roundUpSize (sizeof (CueChunk) + (size_t) (numCues - 1) * sizeof (Cue)), true);
  377. CueChunk* const c = static_cast<CueChunk*> (data.getData());
  378. c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues);
  379. const String dataChunkID (chunkName ("data"));
  380. int nextOrder = 0;
  381. #if JUCE_DEBUG
  382. Array<uint32> identifiers;
  383. #endif
  384. for (int i = 0; i < numCues; ++i)
  385. {
  386. const String prefix ("Cue" + String (i));
  387. const uint32 identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue();
  388. #if JUCE_DEBUG
  389. jassert (! identifiers.contains (identifier));
  390. identifiers.add (identifier);
  391. #endif
  392. const int order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue();
  393. nextOrder = jmax (nextOrder, order) + 1;
  394. Cue& cue = c->cues[i];
  395. cue.identifier = ByteOrder::swapIfBigEndian ((uint32) identifier);
  396. cue.order = ByteOrder::swapIfBigEndian ((uint32) order);
  397. cue.chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue());
  398. cue.chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue());
  399. cue.blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue());
  400. cue.offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue());
  401. }
  402. }
  403. return data;
  404. }
  405. } JUCE_PACKED;
  406. //==============================================================================
  407. namespace ListChunk
  408. {
  409. static int getValue (const StringPairArray& values, const String& name)
  410. {
  411. return values.getValue (name, "0").getIntValue();
  412. }
  413. static int getValue (const StringPairArray& values, const String& prefix, const char* name)
  414. {
  415. return getValue (values, prefix + name);
  416. }
  417. static void appendLabelOrNoteChunk (const StringPairArray& values, const String& prefix,
  418. const int chunkType, MemoryOutputStream& out)
  419. {
  420. const String label (values.getValue (prefix + "Text", prefix));
  421. const int labelLength = (int) label.getNumBytesAsUTF8() + 1;
  422. const int chunkLength = 4 + labelLength + (labelLength & 1);
  423. out.writeInt (chunkType);
  424. out.writeInt (chunkLength);
  425. out.writeInt (getValue (values, prefix, "Identifier"));
  426. out.write (label.toUTF8(), (size_t) labelLength);
  427. if ((out.getDataSize() & 1) != 0)
  428. out.writeByte (0);
  429. }
  430. static void appendExtraChunk (const StringPairArray& values, const String& prefix, MemoryOutputStream& out)
  431. {
  432. const String text (values.getValue (prefix + "Text", prefix));
  433. const int textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator
  434. int chunkLength = textLength + 20 + (textLength & 1);
  435. out.writeInt (chunkName ("ltxt"));
  436. out.writeInt (chunkLength);
  437. out.writeInt (getValue (values, prefix, "Identifier"));
  438. out.writeInt (getValue (values, prefix, "SampleLength"));
  439. out.writeInt (getValue (values, prefix, "Purpose"));
  440. out.writeShort ((short) getValue (values, prefix, "Country"));
  441. out.writeShort ((short) getValue (values, prefix, "Language"));
  442. out.writeShort ((short) getValue (values, prefix, "Dialect"));
  443. out.writeShort ((short) getValue (values, prefix, "CodePage"));
  444. out.write (text.toUTF8(), (size_t) textLength);
  445. if ((out.getDataSize() & 1) != 0)
  446. out.writeByte (0);
  447. }
  448. static MemoryBlock createFrom (const StringPairArray& values)
  449. {
  450. const int numCueLabels = getValue (values, "NumCueLabels");
  451. const int numCueNotes = getValue (values, "NumCueNotes");
  452. const int numCueRegions = getValue (values, "NumCueRegions");
  453. MemoryOutputStream out;
  454. if (numCueLabels + numCueNotes + numCueRegions > 0)
  455. {
  456. out.writeInt (chunkName ("adtl"));
  457. for (int i = 0; i < numCueLabels; ++i)
  458. appendLabelOrNoteChunk (values, "CueLabel" + String (i), chunkName ("labl"), out);
  459. for (int i = 0; i < numCueNotes; ++i)
  460. appendLabelOrNoteChunk (values, "CueNote" + String (i), chunkName ("note"), out);
  461. for (int i = 0; i < numCueRegions; ++i)
  462. appendExtraChunk (values, "CueRegion" + String (i), out);
  463. }
  464. return out.getMemoryBlock();
  465. }
  466. }
  467. //==============================================================================
  468. /** Reads a RIFF List Info chunk from a stream positioned just after the size byte. */
  469. namespace ListInfoChunk
  470. {
  471. static const char* const types[] =
  472. {
  473. WavAudioFormat::riffInfoArchivalLocation,
  474. WavAudioFormat::riffInfoArtist,
  475. WavAudioFormat::riffInfoBaseURL,
  476. WavAudioFormat::riffInfoCinematographer,
  477. WavAudioFormat::riffInfoComment,
  478. WavAudioFormat::riffInfoComments,
  479. WavAudioFormat::riffInfoCommissioned,
  480. WavAudioFormat::riffInfoCopyright,
  481. WavAudioFormat::riffInfoCostumeDesigner,
  482. WavAudioFormat::riffInfoCountry,
  483. WavAudioFormat::riffInfoCropped,
  484. WavAudioFormat::riffInfoDateCreated,
  485. WavAudioFormat::riffInfoDateTimeOriginal,
  486. WavAudioFormat::riffInfoDefaultAudioStream,
  487. WavAudioFormat::riffInfoDimension,
  488. WavAudioFormat::riffInfoDirectory,
  489. WavAudioFormat::riffInfoDistributedBy,
  490. WavAudioFormat::riffInfoDotsPerInch,
  491. WavAudioFormat::riffInfoEditedBy,
  492. WavAudioFormat::riffInfoEighthLanguage,
  493. WavAudioFormat::riffInfoEncodedBy,
  494. WavAudioFormat::riffInfoEndTimecode,
  495. WavAudioFormat::riffInfoEngineer,
  496. WavAudioFormat::riffInfoFifthLanguage,
  497. WavAudioFormat::riffInfoFirstLanguage,
  498. WavAudioFormat::riffInfoFourthLanguage,
  499. WavAudioFormat::riffInfoGenre,
  500. WavAudioFormat::riffInfoKeywords,
  501. WavAudioFormat::riffInfoLanguage,
  502. WavAudioFormat::riffInfoLength,
  503. WavAudioFormat::riffInfoLightness,
  504. WavAudioFormat::riffInfoLocation,
  505. WavAudioFormat::riffInfoLogoIconURL,
  506. WavAudioFormat::riffInfoLogoURL,
  507. WavAudioFormat::riffInfoMedium,
  508. WavAudioFormat::riffInfoMoreInfoBannerImage,
  509. WavAudioFormat::riffInfoMoreInfoBannerURL,
  510. WavAudioFormat::riffInfoMoreInfoText,
  511. WavAudioFormat::riffInfoMoreInfoURL,
  512. WavAudioFormat::riffInfoMusicBy,
  513. WavAudioFormat::riffInfoNinthLanguage,
  514. WavAudioFormat::riffInfoNumberOfParts,
  515. WavAudioFormat::riffInfoOrganisation,
  516. WavAudioFormat::riffInfoPart,
  517. WavAudioFormat::riffInfoProducedBy,
  518. WavAudioFormat::riffInfoProductionDesigner,
  519. WavAudioFormat::riffInfoProductionStudio,
  520. WavAudioFormat::riffInfoRate,
  521. WavAudioFormat::riffInfoRated,
  522. WavAudioFormat::riffInfoRating,
  523. WavAudioFormat::riffInfoRippedBy,
  524. WavAudioFormat::riffInfoSecondaryGenre,
  525. WavAudioFormat::riffInfoSecondLanguage,
  526. WavAudioFormat::riffInfoSeventhLanguage,
  527. WavAudioFormat::riffInfoSharpness,
  528. WavAudioFormat::riffInfoSixthLanguage,
  529. WavAudioFormat::riffInfoSoftware,
  530. WavAudioFormat::riffInfoSoundSchemeTitle,
  531. WavAudioFormat::riffInfoSource,
  532. WavAudioFormat::riffInfoSourceFrom,
  533. WavAudioFormat::riffInfoStarring_ISTR,
  534. WavAudioFormat::riffInfoStarring_STAR,
  535. WavAudioFormat::riffInfoStartTimecode,
  536. WavAudioFormat::riffInfoStatistics,
  537. WavAudioFormat::riffInfoSubject,
  538. WavAudioFormat::riffInfoTapeName,
  539. WavAudioFormat::riffInfoTechnician,
  540. WavAudioFormat::riffInfoThirdLanguage,
  541. WavAudioFormat::riffInfoTimeCode,
  542. WavAudioFormat::riffInfoTitle,
  543. WavAudioFormat::riffInfoTrackNumber,
  544. WavAudioFormat::riffInfoURL,
  545. WavAudioFormat::riffInfoVegasVersionMajor,
  546. WavAudioFormat::riffInfoVegasVersionMinor,
  547. WavAudioFormat::riffInfoVersion,
  548. WavAudioFormat::riffInfoWatermarkURL,
  549. WavAudioFormat::riffInfoWrittenBy,
  550. WavAudioFormat::riffInfoYear
  551. };
  552. static bool isMatchingTypeIgnoringCase (const int value, const char* const name) noexcept
  553. {
  554. for (int i = 0; i < 4; ++i)
  555. if ((juce_wchar) name[i] != CharacterFunctions::toUpperCase ((juce_wchar) ((value >> (i * 8)) & 0xff)))
  556. return false;
  557. return true;
  558. }
  559. static void addToMetadata (StringPairArray& values, InputStream& input, int64 chunkEnd)
  560. {
  561. while (input.getPosition() < chunkEnd)
  562. {
  563. const int infoType = input.readInt();
  564. int64 infoLength = chunkEnd - input.getPosition();
  565. if (infoLength > 0)
  566. {
  567. infoLength = jmin (infoLength, (int64) input.readInt());
  568. if (infoLength <= 0)
  569. return;
  570. for (int i = 0; i < numElementsInArray (types); ++i)
  571. {
  572. if (isMatchingTypeIgnoringCase (infoType, types[i]))
  573. {
  574. MemoryBlock mb;
  575. input.readIntoMemoryBlock (mb, (ssize_t) infoLength);
  576. values.set (types[i], String::createStringFromData ((const char*) mb.getData(),
  577. (int) mb.getSize()));
  578. break;
  579. }
  580. }
  581. }
  582. }
  583. }
  584. static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName)
  585. {
  586. const String value (values.getValue (paramName, String()));
  587. if (value.isEmpty())
  588. return false;
  589. const int valueLength = (int) value.getNumBytesAsUTF8() + 1;
  590. const int chunkLength = valueLength + (valueLength & 1);
  591. out.writeInt (chunkName (paramName));
  592. out.writeInt (chunkLength);
  593. out.write (value.toUTF8(), (size_t) valueLength);
  594. if ((out.getDataSize() & 1) != 0)
  595. out.writeByte (0);
  596. return true;
  597. }
  598. static MemoryBlock createFrom (const StringPairArray& values)
  599. {
  600. MemoryOutputStream out;
  601. out.writeInt (chunkName ("INFO"));
  602. bool anyParamsDefined = false;
  603. for (int i = 0; i < numElementsInArray (types); ++i)
  604. if (writeValue (values, out, types[i]))
  605. anyParamsDefined = true;
  606. return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock();
  607. }
  608. }
  609. //==============================================================================
  610. struct AcidChunk
  611. {
  612. /** Reads an acid RIFF chunk from a stream positioned just after the size byte. */
  613. AcidChunk (InputStream& input, size_t length)
  614. {
  615. zerostruct (*this);
  616. input.read (this, (int) jmin (sizeof (*this), length));
  617. }
  618. AcidChunk (const StringPairArray& values)
  619. {
  620. zerostruct (*this);
  621. flags = getFlagIfPresent (values, WavAudioFormat::acidOneShot, 0x01)
  622. | getFlagIfPresent (values, WavAudioFormat::acidRootSet, 0x02)
  623. | getFlagIfPresent (values, WavAudioFormat::acidStretch, 0x04)
  624. | getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08)
  625. | getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10);
  626. if (values[WavAudioFormat::acidRootSet].getIntValue() != 0)
  627. rootNote = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidRootNote].getIntValue());
  628. numBeats = ByteOrder::swapIfBigEndian ((uint32) values[WavAudioFormat::acidBeats].getIntValue());
  629. meterDenominator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidDenominator].getIntValue());
  630. meterNumerator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidNumerator].getIntValue());
  631. if (values.containsKey (WavAudioFormat::acidTempo))
  632. tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue());
  633. }
  634. static MemoryBlock createFrom (const StringPairArray& values)
  635. {
  636. return AcidChunk (values).toMemoryBlock();
  637. }
  638. MemoryBlock toMemoryBlock() const
  639. {
  640. return (flags != 0 || rootNote != 0 || numBeats != 0 || meterDenominator != 0 || meterNumerator != 0)
  641. ? MemoryBlock (this, sizeof (*this)) : MemoryBlock();
  642. }
  643. void addToMetadata (StringPairArray& values) const
  644. {
  645. setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01);
  646. setBoolFlag (values, WavAudioFormat::acidRootSet, 0x02);
  647. setBoolFlag (values, WavAudioFormat::acidStretch, 0x04);
  648. setBoolFlag (values, WavAudioFormat::acidDiskBased, 0x08);
  649. setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10);
  650. if (flags & 0x02) // root note set
  651. values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote)));
  652. values.set (WavAudioFormat::acidBeats, String (ByteOrder::swapIfBigEndian (numBeats)));
  653. values.set (WavAudioFormat::acidDenominator, String (ByteOrder::swapIfBigEndian (meterDenominator)));
  654. values.set (WavAudioFormat::acidNumerator, String (ByteOrder::swapIfBigEndian (meterNumerator)));
  655. values.set (WavAudioFormat::acidTempo, String (swapFloatByteOrder (tempo)));
  656. }
  657. void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const
  658. {
  659. values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0");
  660. }
  661. static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag)
  662. {
  663. return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0;
  664. }
  665. static float swapFloatByteOrder (const float x) noexcept
  666. {
  667. #ifdef JUCE_BIG_ENDIAN
  668. union { uint32 asInt; float asFloat; } n;
  669. n.asFloat = x;
  670. n.asInt = ByteOrder::swap (n.asInt);
  671. return n.asFloat;
  672. #else
  673. return x;
  674. #endif
  675. }
  676. uint32 flags;
  677. uint16 rootNote;
  678. uint16 reserved1;
  679. float reserved2;
  680. uint32 numBeats;
  681. uint16 meterDenominator;
  682. uint16 meterNumerator;
  683. float tempo;
  684. } JUCE_PACKED;
  685. //==============================================================================
  686. struct TracktionChunk
  687. {
  688. static MemoryBlock createFrom (const StringPairArray& values)
  689. {
  690. MemoryOutputStream out;
  691. const String s (values[WavAudioFormat::tracktionLoopInfo]);
  692. if (s.isNotEmpty())
  693. {
  694. out.writeString (s);
  695. if ((out.getDataSize() & 1) != 0)
  696. out.writeByte (0);
  697. }
  698. return out.getMemoryBlock();
  699. }
  700. };
  701. //==============================================================================
  702. namespace AXMLChunk
  703. {
  704. static void addToMetadata (StringPairArray& destValues, const String& source)
  705. {
  706. ScopedPointer<XmlElement> xml (XmlDocument::parse (source));
  707. if (xml != nullptr && xml->hasTagName ("ebucore:ebuCoreMain"))
  708. {
  709. if (XmlElement* xml2 = xml->getChildByName ("ebucore:coreMetadata"))
  710. {
  711. if (XmlElement* xml3 = xml2->getChildByName ("ebucore:identifier"))
  712. {
  713. if (XmlElement* xml4 = xml3->getChildByName ("dc:identifier"))
  714. {
  715. const String ISRCCode (xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true));
  716. if (ISRCCode.isNotEmpty())
  717. destValues.set (WavAudioFormat::ISRC, ISRCCode);
  718. }
  719. }
  720. }
  721. }
  722. }
  723. static MemoryBlock createFrom (const StringPairArray& values)
  724. {
  725. const String ISRC (values.getValue (WavAudioFormat::ISRC, String()));
  726. MemoryOutputStream xml;
  727. if (ISRC.isNotEmpty())
  728. {
  729. xml << "<ebucore:ebuCoreMain xmlns:dc=\" http://purl.org/dc/elements/1.1/\" "
  730. "xmlns:ebucore=\"urn:ebu:metadata-schema:ebuCore_2012\">"
  731. "<ebucore:coreMetadata>"
  732. "<ebucore:identifier typeLabel=\"GUID\" "
  733. "typeDefinition=\"Globally Unique Identifier\" "
  734. "formatLabel=\"ISRC\" "
  735. "formatDefinition=\"International Standard Recording Code\" "
  736. "formatLink=\"http://www.ebu.ch/metadata/cs/ebu_IdentifierTypeCodeCS.xml#3.7\">"
  737. "<dc:identifier>ISRC:" << ISRC << "</dc:identifier>"
  738. "</ebucore:identifier>"
  739. "</ebucore:coreMetadata>"
  740. "</ebucore:ebuCoreMain>";
  741. xml.writeRepeatedByte (0, xml.getDataSize()); // ensures even size, null termination and room for future growing
  742. }
  743. return xml.getMemoryBlock();
  744. }
  745. };
  746. //==============================================================================
  747. struct ExtensibleWavSubFormat
  748. {
  749. uint32 data1;
  750. uint16 data2;
  751. uint16 data3;
  752. uint8 data4[8];
  753. bool operator== (const ExtensibleWavSubFormat& other) const noexcept { return memcmp (this, &other, sizeof (*this)) == 0; }
  754. bool operator!= (const ExtensibleWavSubFormat& other) const noexcept { return ! operator== (other); }
  755. } JUCE_PACKED;
  756. static const ExtensibleWavSubFormat pcmFormat = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
  757. static const ExtensibleWavSubFormat IEEEFloatFormat = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
  758. static const ExtensibleWavSubFormat ambisonicFormat = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } };
  759. struct DataSize64Chunk // chunk ID = 'ds64' if data size > 0xffffffff, 'JUNK' otherwise
  760. {
  761. uint32 riffSizeLow; // low 4 byte size of RF64 block
  762. uint32 riffSizeHigh; // high 4 byte size of RF64 block
  763. uint32 dataSizeLow; // low 4 byte size of data chunk
  764. uint32 dataSizeHigh; // high 4 byte size of data chunk
  765. uint32 sampleCountLow; // low 4 byte sample count of fact chunk
  766. uint32 sampleCountHigh; // high 4 byte sample count of fact chunk
  767. uint32 tableLength; // number of valid entries in array 'table'
  768. } JUCE_PACKED;
  769. #if JUCE_MSVC
  770. #pragma pack (pop)
  771. #endif
  772. }
  773. //==============================================================================
  774. class WavAudioFormatReader : public AudioFormatReader
  775. {
  776. public:
  777. WavAudioFormatReader (InputStream* const in)
  778. : AudioFormatReader (in, wavFormatName),
  779. bwavChunkStart (0),
  780. bwavSize (0),
  781. dataLength (0),
  782. isRF64 (false)
  783. {
  784. using namespace WavFileHelpers;
  785. uint64 len = 0;
  786. uint64 end = 0;
  787. int cueNoteIndex = 0;
  788. int cueLabelIndex = 0;
  789. int cueRegionIndex = 0;
  790. const int firstChunkType = input->readInt();
  791. if (firstChunkType == chunkName ("RF64"))
  792. {
  793. input->skipNextBytes (4); // size is -1 for RF64
  794. isRF64 = true;
  795. }
  796. else if (firstChunkType == chunkName ("RIFF"))
  797. {
  798. len = (uint64) (uint32) input->readInt();
  799. end = len + (uint64) input->getPosition();
  800. }
  801. else
  802. {
  803. return;
  804. }
  805. const int64 startOfRIFFChunk = input->getPosition();
  806. if (input->readInt() == chunkName ("WAVE"))
  807. {
  808. if (isRF64 && input->readInt() == chunkName ("ds64"))
  809. {
  810. const uint32 length = (uint32) input->readInt();
  811. if (length < 28)
  812. return;
  813. const int64 chunkEnd = input->getPosition() + length + (length & 1);
  814. len = (uint64) input->readInt64();
  815. end = len + (uint64) startOfRIFFChunk;
  816. dataLength = input->readInt64();
  817. input->setPosition (chunkEnd);
  818. }
  819. while ((uint64) input->getPosition() < end && ! input->isExhausted())
  820. {
  821. const int chunkType = input->readInt();
  822. uint32 length = (uint32) input->readInt();
  823. const int64 chunkEnd = input->getPosition() + length + (length & 1);
  824. if (chunkType == chunkName ("fmt "))
  825. {
  826. // read the format chunk
  827. const unsigned short format = (unsigned short) input->readShort();
  828. numChannels = (unsigned int) input->readShort();
  829. sampleRate = input->readInt();
  830. const int bytesPerSec = input->readInt();
  831. input->skipNextBytes (2);
  832. bitsPerSample = (unsigned int) (int) input->readShort();
  833. if (bitsPerSample > 64)
  834. {
  835. bytesPerFrame = bytesPerSec / (int) sampleRate;
  836. bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels;
  837. }
  838. else
  839. {
  840. bytesPerFrame = numChannels * bitsPerSample / 8;
  841. }
  842. if (format == 3)
  843. {
  844. usesFloatingPointData = true;
  845. }
  846. else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/)
  847. {
  848. if (length < 40) // too short
  849. {
  850. bytesPerFrame = 0;
  851. }
  852. else
  853. {
  854. input->skipNextBytes (4); // skip over size and bitsPerSample
  855. metadataValues.set ("ChannelMask", String (input->readInt()));
  856. ExtensibleWavSubFormat subFormat;
  857. subFormat.data1 = (uint32) input->readInt();
  858. subFormat.data2 = (uint16) input->readShort();
  859. subFormat.data3 = (uint16) input->readShort();
  860. input->read (subFormat.data4, sizeof (subFormat.data4));
  861. if (subFormat == IEEEFloatFormat)
  862. usesFloatingPointData = true;
  863. else if (subFormat != pcmFormat && subFormat != ambisonicFormat)
  864. bytesPerFrame = 0;
  865. }
  866. }
  867. else if (format != 1)
  868. {
  869. bytesPerFrame = 0;
  870. }
  871. }
  872. else if (chunkType == chunkName ("data"))
  873. {
  874. if (! isRF64) // data size is expected to be -1, actual data size is in ds64 chunk
  875. dataLength = length;
  876. dataChunkStart = input->getPosition();
  877. lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0;
  878. }
  879. else if (chunkType == chunkName ("bext"))
  880. {
  881. bwavChunkStart = input->getPosition();
  882. bwavSize = length;
  883. HeapBlock<BWAVChunk> bwav;
  884. bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1);
  885. input->read (bwav, (int) length);
  886. bwav->copyTo (metadataValues, (int) length);
  887. }
  888. else if (chunkType == chunkName ("smpl"))
  889. {
  890. HeapBlock<SMPLChunk> smpl;
  891. smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1);
  892. input->read (smpl, (int) length);
  893. smpl->copyTo (metadataValues, (int) length);
  894. }
  895. else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which...
  896. {
  897. HeapBlock<InstChunk> inst;
  898. inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
  899. input->read (inst, (int) length);
  900. inst->copyTo (metadataValues);
  901. }
  902. else if (chunkType == chunkName ("cue "))
  903. {
  904. HeapBlock<CueChunk> cue;
  905. cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1);
  906. input->read (cue, (int) length);
  907. cue->copyTo (metadataValues, (int) length);
  908. }
  909. else if (chunkType == chunkName ("axml"))
  910. {
  911. MemoryBlock axml;
  912. input->readIntoMemoryBlock (axml, (ssize_t) length);
  913. AXMLChunk::addToMetadata (metadataValues, axml.toString());
  914. }
  915. else if (chunkType == chunkName ("LIST"))
  916. {
  917. const int subChunkType = input->readInt();
  918. if (subChunkType == chunkName ("info") || subChunkType == chunkName ("INFO"))
  919. {
  920. ListInfoChunk::addToMetadata (metadataValues, *input, chunkEnd);
  921. }
  922. else if (subChunkType == chunkName ("adtl"))
  923. {
  924. while (input->getPosition() < chunkEnd)
  925. {
  926. const int adtlChunkType = input->readInt();
  927. const uint32 adtlLength = (uint32) input->readInt();
  928. const int64 adtlChunkEnd = input->getPosition() + (adtlLength + (adtlLength & 1));
  929. if (adtlChunkType == chunkName ("labl") || adtlChunkType == chunkName ("note"))
  930. {
  931. String prefix;
  932. if (adtlChunkType == chunkName ("labl"))
  933. prefix << "CueLabel" << cueLabelIndex++;
  934. else if (adtlChunkType == chunkName ("note"))
  935. prefix << "CueNote" << cueNoteIndex++;
  936. const uint32 identifier = (uint32) input->readInt();
  937. const int stringLength = (int) adtlLength - 4;
  938. MemoryBlock textBlock;
  939. input->readIntoMemoryBlock (textBlock, stringLength);
  940. metadataValues.set (prefix + "Identifier", String (identifier));
  941. metadataValues.set (prefix + "Text", textBlock.toString());
  942. }
  943. else if (adtlChunkType == chunkName ("ltxt"))
  944. {
  945. const String prefix ("CueRegion" + String (cueRegionIndex++));
  946. const uint32 identifier = (uint32) input->readInt();
  947. const uint32 sampleLength = (uint32) input->readInt();
  948. const uint32 purpose = (uint32) input->readInt();
  949. const uint16 country = (uint16) input->readInt();
  950. const uint16 language = (uint16) input->readInt();
  951. const uint16 dialect = (uint16) input->readInt();
  952. const uint16 codePage = (uint16) input->readInt();
  953. const uint32 stringLength = adtlLength - 20;
  954. MemoryBlock textBlock;
  955. input->readIntoMemoryBlock (textBlock, (int) stringLength);
  956. metadataValues.set (prefix + "Identifier", String (identifier));
  957. metadataValues.set (prefix + "SampleLength", String (sampleLength));
  958. metadataValues.set (prefix + "Purpose", String (purpose));
  959. metadataValues.set (prefix + "Country", String (country));
  960. metadataValues.set (prefix + "Language", String (language));
  961. metadataValues.set (prefix + "Dialect", String (dialect));
  962. metadataValues.set (prefix + "CodePage", String (codePage));
  963. metadataValues.set (prefix + "Text", textBlock.toString());
  964. }
  965. input->setPosition (adtlChunkEnd);
  966. }
  967. }
  968. }
  969. else if (chunkType == chunkName ("acid"))
  970. {
  971. AcidChunk (*input, length).addToMetadata (metadataValues);
  972. }
  973. else if (chunkType == chunkName ("Trkn"))
  974. {
  975. MemoryBlock tracktion;
  976. input->readIntoMemoryBlock (tracktion, (ssize_t) length);
  977. metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString());
  978. }
  979. else if (chunkEnd <= input->getPosition())
  980. {
  981. break;
  982. }
  983. input->setPosition (chunkEnd);
  984. }
  985. }
  986. if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex));
  987. if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex));
  988. if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex));
  989. if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV");
  990. }
  991. //==============================================================================
  992. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  993. int64 startSampleInFile, int numSamples) override
  994. {
  995. clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
  996. startSampleInFile, numSamples, lengthInSamples);
  997. if (numSamples <= 0)
  998. return true;
  999. input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame);
  1000. while (numSamples > 0)
  1001. {
  1002. const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
  1003. char tempBuffer [tempBufSize];
  1004. const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples);
  1005. const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);
  1006. if (bytesRead < numThisTime * bytesPerFrame)
  1007. {
  1008. jassert (bytesRead >= 0);
  1009. zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead));
  1010. }
  1011. copySampleData (bitsPerSample, usesFloatingPointData,
  1012. destSamples, startOffsetInDestBuffer, numDestChannels,
  1013. tempBuffer, (int) numChannels, numThisTime);
  1014. startOffsetInDestBuffer += numThisTime;
  1015. numSamples -= numThisTime;
  1016. }
  1017. return true;
  1018. }
  1019. static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData,
  1020. int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels,
  1021. const void* sourceData, int numChannels, int numSamples) noexcept
  1022. {
  1023. switch (bitsPerSample)
  1024. {
  1025. case 8: ReadHelper<AudioData::Int32, AudioData::UInt8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
  1026. case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
  1027. case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
  1028. case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
  1029. else ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
  1030. default: jassertfalse; break;
  1031. }
  1032. }
  1033. int64 bwavChunkStart, bwavSize;
  1034. int64 dataChunkStart, dataLength;
  1035. int bytesPerFrame;
  1036. bool isRF64;
  1037. private:
  1038. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader)
  1039. };
  1040. //==============================================================================
  1041. class WavAudioFormatWriter : public AudioFormatWriter
  1042. {
  1043. public:
  1044. WavAudioFormatWriter (OutputStream* const out, const double rate,
  1045. const unsigned int numChans, const unsigned int bits,
  1046. const StringPairArray& metadataValues)
  1047. : AudioFormatWriter (out, wavFormatName, rate, numChans, bits),
  1048. lengthInSamples (0),
  1049. bytesWritten (0),
  1050. writeFailed (false)
  1051. {
  1052. using namespace WavFileHelpers;
  1053. if (metadataValues.size() > 0)
  1054. {
  1055. // The meta data should have been sanitised for the WAV format.
  1056. // If it was originally sourced from an AIFF file the MetaDataSource
  1057. // key should be removed (or set to "WAV") once this has been done
  1058. jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF");
  1059. bwavChunk = BWAVChunk::createFrom (metadataValues);
  1060. axmlChunk = AXMLChunk::createFrom (metadataValues);
  1061. smplChunk = SMPLChunk::createFrom (metadataValues);
  1062. instChunk = InstChunk::createFrom (metadataValues);
  1063. cueChunk = CueChunk ::createFrom (metadataValues);
  1064. listChunk = ListChunk::createFrom (metadataValues);
  1065. listInfoChunk = ListInfoChunk::createFrom (metadataValues);
  1066. acidChunk = AcidChunk::createFrom (metadataValues);
  1067. trckChunk = TracktionChunk::createFrom (metadataValues);
  1068. }
  1069. headerPosition = out->getPosition();
  1070. writeHeader();
  1071. }
  1072. ~WavAudioFormatWriter()
  1073. {
  1074. writeHeader();
  1075. }
  1076. //==============================================================================
  1077. bool write (const int** data, int numSamples) override
  1078. {
  1079. jassert (numSamples >= 0);
  1080. jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel!
  1081. if (writeFailed)
  1082. return false;
  1083. const size_t bytes = numChannels * (size_t) numSamples * bitsPerSample / 8;
  1084. tempBlock.ensureSize (bytes, false);
  1085. switch (bitsPerSample)
  1086. {
  1087. case 8: WriteHelper<AudioData::UInt8, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  1088. case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  1089. case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  1090. case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
  1091. default: jassertfalse; break;
  1092. }
  1093. if (! output->write (tempBlock.getData(), bytes))
  1094. {
  1095. // failed to write to disk, so let's try writing the header.
  1096. // If it's just run out of disk space, then if it does manage
  1097. // to write the header, we'll still have a useable file..
  1098. writeHeader();
  1099. writeFailed = true;
  1100. return false;
  1101. }
  1102. bytesWritten += bytes;
  1103. lengthInSamples += (uint64) numSamples;
  1104. return true;
  1105. }
  1106. bool flush() override
  1107. {
  1108. const int64 lastWritePos = output->getPosition();
  1109. writeHeader();
  1110. if (output->setPosition (lastWritePos))
  1111. return true;
  1112. // if this fails, you've given it an output stream that can't seek! It needs
  1113. // to be able to seek back to write the header
  1114. jassertfalse;
  1115. return false;
  1116. }
  1117. private:
  1118. MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk;
  1119. uint64 lengthInSamples, bytesWritten;
  1120. int64 headerPosition;
  1121. bool writeFailed;
  1122. static int getChannelMask (const int numChannels) noexcept
  1123. {
  1124. switch (numChannels)
  1125. {
  1126. case 1: return 0;
  1127. case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
  1128. case 3: return 1 + 2 + 4; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER
  1129. case 4: return 1 + 2 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
  1130. case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
  1131. 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
  1132. 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
  1133. 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
  1134. default: break;
  1135. }
  1136. return 0;
  1137. }
  1138. void writeHeader()
  1139. {
  1140. if ((bytesWritten & 1) != 0) // pad to an even length
  1141. output->writeByte (0);
  1142. using namespace WavFileHelpers;
  1143. if (headerPosition != output->getPosition() && ! output->setPosition (headerPosition))
  1144. {
  1145. // if this fails, you've given it an output stream that can't seek! It needs to be
  1146. // able to seek back to go back and write the header after the data has been written.
  1147. jassertfalse;
  1148. return;
  1149. }
  1150. const size_t bytesPerFrame = numChannels * bitsPerSample / 8;
  1151. uint64 audioDataSize = bytesPerFrame * lengthInSamples;
  1152. const bool isRF64 = (bytesWritten >= 0x100000000LL);
  1153. const bool isWaveFmtEx = isRF64 || (numChannels > 2);
  1154. int64 riffChunkSize = (int64) (4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */
  1155. + 8 + audioDataSize + (audioDataSize & 1)
  1156. + chunkSize (bwavChunk)
  1157. + chunkSize (axmlChunk)
  1158. + chunkSize (smplChunk)
  1159. + chunkSize (instChunk)
  1160. + chunkSize (cueChunk)
  1161. + chunkSize (listChunk)
  1162. + chunkSize (listInfoChunk)
  1163. + chunkSize (acidChunk)
  1164. + chunkSize (trckChunk)
  1165. + (8 + 28)); // (ds64 chunk)
  1166. riffChunkSize += (riffChunkSize & 1);
  1167. if (isRF64)
  1168. writeChunkHeader (chunkName ("RF64"), -1);
  1169. else
  1170. writeChunkHeader (chunkName ("RIFF"), (int) riffChunkSize);
  1171. output->writeInt (chunkName ("WAVE"));
  1172. if (! isRF64)
  1173. {
  1174. #if ! JUCE_WAV_DO_NOT_PAD_HEADER_SIZE
  1175. /* NB: This junk chunk is added for padding, so that the header is a fixed size
  1176. regardless of whether it's RF64 or not. That way, we can begin recording a file,
  1177. and when it's finished, can go back and write either a RIFF or RF64 header,
  1178. depending on whether more than 2^32 samples were written.
  1179. The JUCE_WAV_DO_NOT_PAD_HEADER_SIZE macro allows you to disable this feature in case
  1180. you need to create files for crappy WAV players with bugs that stop them skipping chunks
  1181. which they don't recognise. But DO NOT USE THIS option unless you really have no choice,
  1182. because it means that if you write more than 2^32 samples to the file, you'll corrupt it.
  1183. */
  1184. writeChunkHeader (chunkName ("JUNK"), 28 + (isWaveFmtEx? 0 : 24));
  1185. output->writeRepeatedByte (0, 28 /* ds64 */ + (isWaveFmtEx? 0 : 24));
  1186. #endif
  1187. }
  1188. else
  1189. {
  1190. #if JUCE_WAV_DO_NOT_PAD_HEADER_SIZE
  1191. // If you disable padding, then you MUST NOT write more than 2^32 samples to a file.
  1192. jassertfalse;
  1193. #endif
  1194. writeChunkHeader (chunkName ("ds64"), 28); // chunk size for uncompressed data (no table)
  1195. output->writeInt64 (riffChunkSize);
  1196. output->writeInt64 ((int64) audioDataSize);
  1197. output->writeRepeatedByte (0, 12);
  1198. }
  1199. if (isWaveFmtEx)
  1200. {
  1201. writeChunkHeader (chunkName ("fmt "), 40);
  1202. output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE
  1203. }
  1204. else
  1205. {
  1206. writeChunkHeader (chunkName ("fmt "), 16);
  1207. output->writeShort (bitsPerSample < 32 ? (short) 1 /*WAVE_FORMAT_PCM*/
  1208. : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/);
  1209. }
  1210. output->writeShort ((short) numChannels);
  1211. output->writeInt ((int) sampleRate);
  1212. output->writeInt ((int) (bytesPerFrame * sampleRate)); // nAvgBytesPerSec
  1213. output->writeShort ((short) bytesPerFrame); // nBlockAlign
  1214. output->writeShort ((short) bitsPerSample); // wBitsPerSample
  1215. if (isWaveFmtEx)
  1216. {
  1217. output->writeShort (22); // cbSize (size of the extension)
  1218. output->writeShort ((short) bitsPerSample); // wValidBitsPerSample
  1219. output->writeInt (getChannelMask ((int) numChannels));
  1220. const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat;
  1221. output->writeInt ((int) subFormat.data1);
  1222. output->writeShort ((short) subFormat.data2);
  1223. output->writeShort ((short) subFormat.data3);
  1224. output->write (subFormat.data4, sizeof (subFormat.data4));
  1225. }
  1226. writeChunk (bwavChunk, chunkName ("bext"));
  1227. writeChunk (axmlChunk, chunkName ("axml"));
  1228. writeChunk (smplChunk, chunkName ("smpl"));
  1229. writeChunk (instChunk, chunkName ("inst"), 7);
  1230. writeChunk (cueChunk, chunkName ("cue "));
  1231. writeChunk (listChunk, chunkName ("LIST"));
  1232. writeChunk (listInfoChunk, chunkName ("LIST"));
  1233. writeChunk (acidChunk, chunkName ("acid"));
  1234. writeChunk (trckChunk, chunkName ("Trkn"));
  1235. writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame));
  1236. usesFloatingPointData = (bitsPerSample == 32);
  1237. }
  1238. static size_t chunkSize (const MemoryBlock& data) noexcept { return data.getSize() > 0 ? (8 + data.getSize()) : 0; }
  1239. void writeChunkHeader (int chunkType, int size) const
  1240. {
  1241. output->writeInt (chunkType);
  1242. output->writeInt (size);
  1243. }
  1244. void writeChunk (const MemoryBlock& data, int chunkType, int size = 0) const
  1245. {
  1246. if (data.getSize() > 0)
  1247. {
  1248. writeChunkHeader (chunkType, size != 0 ? size : (int) data.getSize());
  1249. *output << data;
  1250. }
  1251. }
  1252. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatWriter)
  1253. };
  1254. //==============================================================================
  1255. class MemoryMappedWavReader : public MemoryMappedAudioFormatReader
  1256. {
  1257. public:
  1258. MemoryMappedWavReader (const File& wavFile, const WavAudioFormatReader& reader)
  1259. : MemoryMappedAudioFormatReader (wavFile, reader, reader.dataChunkStart,
  1260. reader.dataLength, reader.bytesPerFrame)
  1261. {
  1262. }
  1263. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  1264. int64 startSampleInFile, int numSamples) override
  1265. {
  1266. clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
  1267. startSampleInFile, numSamples, lengthInSamples);
  1268. if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
  1269. {
  1270. jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
  1271. return false;
  1272. }
  1273. WavAudioFormatReader::copySampleData (bitsPerSample, usesFloatingPointData,
  1274. destSamples, startOffsetInDestBuffer, numDestChannels,
  1275. sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
  1276. return true;
  1277. }
  1278. void getSample (int64 sample, float* result) const noexcept override
  1279. {
  1280. const int num = (int) numChannels;
  1281. if (map == nullptr || ! mappedSection.contains (sample))
  1282. {
  1283. jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
  1284. zeromem (result, sizeof (float) * (size_t) num);
  1285. return;
  1286. }
  1287. float** dest = &result;
  1288. const void* source = sampleToPointer (sample);
  1289. switch (bitsPerSample)
  1290. {
  1291. case 8: ReadHelper<AudioData::Float32, AudioData::UInt8, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
  1292. case 16: ReadHelper<AudioData::Float32, AudioData::Int16, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
  1293. case 24: ReadHelper<AudioData::Float32, AudioData::Int24, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
  1294. case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num);
  1295. else ReadHelper<AudioData::Float32, AudioData::Int32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
  1296. default: jassertfalse; break;
  1297. }
  1298. }
  1299. void readMaxLevels (int64 startSampleInFile, int64 numSamples, Range<float>* results, int numChannelsToRead) override
  1300. {
  1301. numSamples = jmin (numSamples, lengthInSamples - startSampleInFile);
  1302. if (map == nullptr || numSamples <= 0 || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
  1303. {
  1304. jassert (numSamples <= 0); // you must make sure that the window contains all the samples you're going to attempt to read.
  1305. for (int i = 0; i < numChannelsToRead; ++i)
  1306. results[i] = Range<float>();
  1307. return;
  1308. }
  1309. switch (bitsPerSample)
  1310. {
  1311. case 8: scanMinAndMax<AudioData::UInt8> (startSampleInFile, numSamples, results, numChannelsToRead); break;
  1312. case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, results, numChannelsToRead); break;
  1313. case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, results, numChannelsToRead); break;
  1314. case 32: if (usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, results, numChannelsToRead);
  1315. else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, results, numChannelsToRead); break;
  1316. default: jassertfalse; break;
  1317. }
  1318. }
  1319. private:
  1320. template <typename SampleType>
  1321. void scanMinAndMax (int64 startSampleInFile, int64 numSamples, Range<float>* results, int numChannelsToRead) const noexcept
  1322. {
  1323. for (int i = 0; i < numChannelsToRead; ++i)
  1324. results[i] = scanMinAndMaxInterleaved<SampleType, AudioData::LittleEndian> (i, startSampleInFile, numSamples);
  1325. }
  1326. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedWavReader)
  1327. };
  1328. //==============================================================================
  1329. WavAudioFormat::WavAudioFormat() : AudioFormat (wavFormatName, ".wav .bwf") {}
  1330. WavAudioFormat::~WavAudioFormat() {}
  1331. Array<int> WavAudioFormat::getPossibleSampleRates()
  1332. {
  1333. const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, 44100,
  1334. 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
  1335. return Array<int> (rates, numElementsInArray (rates));
  1336. }
  1337. Array<int> WavAudioFormat::getPossibleBitDepths()
  1338. {
  1339. const int depths[] = { 8, 16, 24, 32 };
  1340. return Array<int> (depths, numElementsInArray (depths));
  1341. }
  1342. bool WavAudioFormat::canDoStereo() { return true; }
  1343. bool WavAudioFormat::canDoMono() { return true; }
  1344. AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream,
  1345. const bool deleteStreamIfOpeningFails)
  1346. {
  1347. ScopedPointer<WavAudioFormatReader> r (new WavAudioFormatReader (sourceStream));
  1348. if (r->sampleRate > 0 && r->numChannels > 0 && r->bytesPerFrame > 0)
  1349. return r.release();
  1350. if (! deleteStreamIfOpeningFails)
  1351. r->input = nullptr;
  1352. return nullptr;
  1353. }
  1354. MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (const File& file)
  1355. {
  1356. if (FileInputStream* fin = file.createInputStream())
  1357. {
  1358. WavAudioFormatReader reader (fin);
  1359. if (reader.lengthInSamples > 0)
  1360. return new MemoryMappedWavReader (file, reader);
  1361. }
  1362. return nullptr;
  1363. }
  1364. AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate,
  1365. unsigned int numChannels, int bitsPerSample,
  1366. const StringPairArray& metadataValues, int /*qualityOptionIndex*/)
  1367. {
  1368. if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
  1369. return new WavAudioFormatWriter (out, sampleRate, (unsigned int) numChannels,
  1370. (unsigned int) bitsPerSample, metadataValues);
  1371. return nullptr;
  1372. }
  1373. namespace WavFileHelpers
  1374. {
  1375. static bool slowCopyWavFileWithNewMetadata (const File& file, const StringPairArray& metadata)
  1376. {
  1377. TemporaryFile tempFile (file);
  1378. WavAudioFormat wav;
  1379. ScopedPointer<AudioFormatReader> reader (wav.createReaderFor (file.createInputStream(), true));
  1380. if (reader != nullptr)
  1381. {
  1382. ScopedPointer<OutputStream> outStream (tempFile.getFile().createOutputStream());
  1383. if (outStream != nullptr)
  1384. {
  1385. ScopedPointer<AudioFormatWriter> writer (wav.createWriterFor (outStream, reader->sampleRate,
  1386. reader->numChannels, (int) reader->bitsPerSample,
  1387. metadata, 0));
  1388. if (writer != nullptr)
  1389. {
  1390. outStream.release();
  1391. bool ok = writer->writeFromAudioReader (*reader, 0, -1);
  1392. writer = nullptr;
  1393. reader = nullptr;
  1394. return ok && tempFile.overwriteTargetFileWithTemporary();
  1395. }
  1396. }
  1397. }
  1398. return false;
  1399. }
  1400. }
  1401. bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata)
  1402. {
  1403. using namespace WavFileHelpers;
  1404. ScopedPointer<WavAudioFormatReader> reader (static_cast<WavAudioFormatReader*> (createReaderFor (wavFile.createInputStream(), true)));
  1405. if (reader != nullptr)
  1406. {
  1407. const int64 bwavPos = reader->bwavChunkStart;
  1408. const int64 bwavSize = reader->bwavSize;
  1409. reader = nullptr;
  1410. if (bwavSize > 0)
  1411. {
  1412. MemoryBlock chunk (BWAVChunk::createFrom (newMetadata));
  1413. if (chunk.getSize() <= (size_t) bwavSize)
  1414. {
  1415. // the new one will fit in the space available, so write it directly..
  1416. const int64 oldSize = wavFile.getSize();
  1417. {
  1418. FileOutputStream out (wavFile);
  1419. if (! out.failedToOpen())
  1420. {
  1421. out.setPosition (bwavPos);
  1422. out << chunk;
  1423. out.setPosition (oldSize);
  1424. }
  1425. }
  1426. jassert (wavFile.getSize() == oldSize);
  1427. return true;
  1428. }
  1429. }
  1430. }
  1431. return slowCopyWavFileWithNewMetadata (wavFile, newMetadata);
  1432. }
  1433. //==============================================================================
  1434. #if JUCE_UNIT_TESTS
  1435. class WaveAudioFormatTests : public UnitTest
  1436. {
  1437. public:
  1438. WaveAudioFormatTests() : UnitTest ("Wave audio format tests") {}
  1439. void runTest() override
  1440. {
  1441. beginTest ("Setting up metadata");
  1442. StringPairArray metadataValues = WavAudioFormat::createBWAVMetadata ("description",
  1443. "originator",
  1444. "originatorRef",
  1445. Time::getCurrentTime(),
  1446. numTestAudioBufferSamples,
  1447. "codingHistory");
  1448. for (int i = numElementsInArray (WavFileHelpers::ListInfoChunk::types); --i >= 0;)
  1449. metadataValues.set (WavFileHelpers::ListInfoChunk::types[i],
  1450. WavFileHelpers::ListInfoChunk::types[i]);
  1451. if (metadataValues.size() > 0)
  1452. metadataValues.set ("MetaDataSource", "WAV");
  1453. WavAudioFormat format;
  1454. MemoryBlock memoryBlock;
  1455. {
  1456. beginTest ("Creating a basic wave writer");
  1457. ScopedPointer<AudioFormatWriter> writer (format.createWriterFor (new MemoryOutputStream (memoryBlock, false),
  1458. 44100.0, numTestAudioBufferChannels,
  1459. 32, metadataValues, 0));
  1460. expect (writer != nullptr);
  1461. AudioSampleBuffer buffer (numTestAudioBufferChannels, numTestAudioBufferSamples);
  1462. buffer.clear();
  1463. beginTest ("Writing audio data to the basic wave writer");
  1464. expect (writer->writeFromAudioSampleBuffer (buffer, 0, numTestAudioBufferSamples));
  1465. }
  1466. {
  1467. beginTest ("Creating a basic wave reader");
  1468. ScopedPointer<AudioFormatReader> reader (format.createReaderFor (new MemoryInputStream (memoryBlock, false), false));
  1469. expect (reader != nullptr);
  1470. expect (reader->metadataValues == metadataValues, "Somehow, the metadata is different!");
  1471. }
  1472. }
  1473. private:
  1474. enum
  1475. {
  1476. numTestAudioBufferChannels = 2,
  1477. numTestAudioBufferSamples = 256
  1478. };
  1479. JUCE_DECLARE_NON_COPYABLE (WaveAudioFormatTests)
  1480. };
  1481. static const WaveAudioFormatTests waveAudioFormatTests;
  1482. #endif