OpenShot Audio Library | OpenShotAudio: juce_WavAudioFormat.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_WavAudioFormat.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29using StringMap = std::unordered_map<String, String>;
30
31static auto toMap (const StringPairArray& array)
32{
33 StringMap result;
34
35 for (auto i = 0; i < array.size(); ++i)
36 result[array.getAllKeys()[i]] = array.getAllValues()[i];
37
38 return result;
39}
40
41static auto getValueWithDefault (const StringMap& m, const String& key, const String& fallback = {})
42{
43 const auto iter = m.find (key);
44 return iter != m.cend() ? iter->second : fallback;
45}
46
47static const char* const wavFormatName = "WAV file";
48
49//==============================================================================
50const char* const WavAudioFormat::bwavDescription = "bwav description";
51const char* const WavAudioFormat::bwavOriginator = "bwav originator";
52const char* const WavAudioFormat::bwavOriginatorRef = "bwav originator ref";
53const char* const WavAudioFormat::bwavOriginationDate = "bwav origination date";
54const char* const WavAudioFormat::bwavOriginationTime = "bwav origination time";
55const char* const WavAudioFormat::bwavTimeReference = "bwav time reference";
56const char* const WavAudioFormat::bwavCodingHistory = "bwav coding history";
57
58 StringPairArray WavAudioFormat::createBWAVMetadata (const String& description,
59 const String& originator,
60 const String& originatorRef,
61 Time date,
62 int64 timeReferenceSamples,
63 const String& codingHistory)
64{
65 StringPairArray m;
66
67 m.set (bwavDescription, description);
68 m.set (bwavOriginator, originator);
69 m.set (bwavOriginatorRef, originatorRef);
70 m.set (bwavOriginationDate, date.formatted ("%Y-%m-%d"));
71 m.set (bwavOriginationTime, date.formatted ("%H:%M:%S"));
72 m.set (bwavTimeReference, String (timeReferenceSamples));
73 m.set (bwavCodingHistory, codingHistory);
74
75 return m;
76}
77
78const char* const WavAudioFormat::acidOneShot = "acid one shot";
79const char* const WavAudioFormat::acidRootSet = "acid root set";
80const char* const WavAudioFormat::acidStretch = "acid stretch";
81const char* const WavAudioFormat::acidDiskBased = "acid disk based";
82const char* const WavAudioFormat::acidizerFlag = "acidizer flag";
83const char* const WavAudioFormat::acidRootNote = "acid root note";
84const char* const WavAudioFormat::acidBeats = "acid beats";
85const char* const WavAudioFormat::acidDenominator = "acid denominator";
86const char* const WavAudioFormat::acidNumerator = "acid numerator";
87const char* const WavAudioFormat::acidTempo = "acid tempo";
88
89const char* const WavAudioFormat::riffInfoArchivalLocation = "IARL";
90const char* const WavAudioFormat::riffInfoArtist = "IART";
91const char* const WavAudioFormat::riffInfoBaseURL = "IBSU";
92const char* const WavAudioFormat::riffInfoCinematographer = "ICNM";
93const char* const WavAudioFormat::riffInfoComment = "CMNT";
94const char* const WavAudioFormat::riffInfoComment2 = "ICMT";
95const char* const WavAudioFormat::riffInfoComments = "COMM";
96const char* const WavAudioFormat::riffInfoCommissioned = "ICMS";
97const char* const WavAudioFormat::riffInfoCopyright = "ICOP";
98const char* const WavAudioFormat::riffInfoCostumeDesigner = "ICDS";
99const char* const WavAudioFormat::riffInfoCountry = "ICNT";
100const char* const WavAudioFormat::riffInfoCropped = "ICRP";
101const char* const WavAudioFormat::riffInfoDateCreated = "ICRD";
102const char* const WavAudioFormat::riffInfoDateTimeOriginal = "IDIT";
103const char* const WavAudioFormat::riffInfoDefaultAudioStream = "ICAS";
104const char* const WavAudioFormat::riffInfoDimension = "IDIM";
105const char* const WavAudioFormat::riffInfoDirectory = "DIRC";
106const char* const WavAudioFormat::riffInfoDistributedBy = "IDST";
107const char* const WavAudioFormat::riffInfoDotsPerInch = "IDPI";
108const char* const WavAudioFormat::riffInfoEditedBy = "IEDT";
109const char* const WavAudioFormat::riffInfoEighthLanguage = "IAS8";
110const char* const WavAudioFormat::riffInfoEncodedBy = "CODE";
111const char* const WavAudioFormat::riffInfoEndTimecode = "TCDO";
112const char* const WavAudioFormat::riffInfoEngineer = "IENG";
113const char* const WavAudioFormat::riffInfoFifthLanguage = "IAS5";
114const char* const WavAudioFormat::riffInfoFirstLanguage = "IAS1";
115const char* const WavAudioFormat::riffInfoFourthLanguage = "IAS4";
116const char* const WavAudioFormat::riffInfoGenre = "GENR";
117const char* const WavAudioFormat::riffInfoKeywords = "IKEY";
118const char* const WavAudioFormat::riffInfoLanguage = "LANG";
119const char* const WavAudioFormat::riffInfoLength = "TLEN";
120const char* const WavAudioFormat::riffInfoLightness = "ILGT";
121const char* const WavAudioFormat::riffInfoLocation = "LOCA";
122const char* const WavAudioFormat::riffInfoLogoIconURL = "ILIU";
123const char* const WavAudioFormat::riffInfoLogoURL = "ILGU";
124const char* const WavAudioFormat::riffInfoMedium = "IMED";
125const char* const WavAudioFormat::riffInfoMoreInfoBannerImage = "IMBI";
126const char* const WavAudioFormat::riffInfoMoreInfoBannerURL = "IMBU";
127const char* const WavAudioFormat::riffInfoMoreInfoText = "IMIT";
128const char* const WavAudioFormat::riffInfoMoreInfoURL = "IMIU";
129const char* const WavAudioFormat::riffInfoMusicBy = "IMUS";
130const char* const WavAudioFormat::riffInfoNinthLanguage = "IAS9";
131const char* const WavAudioFormat::riffInfoNumberOfParts = "PRT2";
132const char* const WavAudioFormat::riffInfoOrganisation = "TORG";
133const char* const WavAudioFormat::riffInfoPart = "PRT1";
134const char* const WavAudioFormat::riffInfoProducedBy = "IPRO";
135const char* const WavAudioFormat::riffInfoProductName = "IPRD";
136const char* const WavAudioFormat::riffInfoProductionDesigner = "IPDS";
137const char* const WavAudioFormat::riffInfoProductionStudio = "ISDT";
138const char* const WavAudioFormat::riffInfoRate = "RATE";
139const char* const WavAudioFormat::riffInfoRated = "AGES";
140const char* const WavAudioFormat::riffInfoRating = "IRTD";
141const char* const WavAudioFormat::riffInfoRippedBy = "IRIP";
142const char* const WavAudioFormat::riffInfoSecondaryGenre = "ISGN";
143const char* const WavAudioFormat::riffInfoSecondLanguage = "IAS2";
144const char* const WavAudioFormat::riffInfoSeventhLanguage = "IAS7";
145const char* const WavAudioFormat::riffInfoSharpness = "ISHP";
146const char* const WavAudioFormat::riffInfoSixthLanguage = "IAS6";
147const char* const WavAudioFormat::riffInfoSoftware = "ISFT";
148const char* const WavAudioFormat::riffInfoSoundSchemeTitle = "DISP";
149const char* const WavAudioFormat::riffInfoSource = "ISRC";
150const char* const WavAudioFormat::riffInfoSourceFrom = "ISRF";
151const char* const WavAudioFormat::riffInfoStarring_ISTR = "ISTR";
152const char* const WavAudioFormat::riffInfoStarring_STAR = "STAR";
153const char* const WavAudioFormat::riffInfoStartTimecode = "TCOD";
154const char* const WavAudioFormat::riffInfoStatistics = "STAT";
155const char* const WavAudioFormat::riffInfoSubject = "ISBJ";
156const char* const WavAudioFormat::riffInfoTapeName = "TAPE";
157const char* const WavAudioFormat::riffInfoTechnician = "ITCH";
158const char* const WavAudioFormat::riffInfoThirdLanguage = "IAS3";
159const char* const WavAudioFormat::riffInfoTimeCode = "ISMP";
160const char* const WavAudioFormat::riffInfoTitle = "INAM";
161const char* const WavAudioFormat::riffInfoTrackNo = "IPRT";
162const char* const WavAudioFormat::riffInfoTrackNumber = "TRCK";
163const char* const WavAudioFormat::riffInfoURL = "TURL";
164const char* const WavAudioFormat::riffInfoVegasVersionMajor = "VMAJ";
165const char* const WavAudioFormat::riffInfoVegasVersionMinor = "VMIN";
166const char* const WavAudioFormat::riffInfoVersion = "TVER";
167const char* const WavAudioFormat::riffInfoWatermarkURL = "IWMU";
168const char* const WavAudioFormat::riffInfoWrittenBy = "IWRI";
169const char* const WavAudioFormat::riffInfoYear = "YEAR";
170
171const char* const WavAudioFormat::aswgContentType = "contentType";
172const char* const WavAudioFormat::aswgProject = "project";
173const char* const WavAudioFormat::aswgOriginator = "originator";
174const char* const WavAudioFormat::aswgOriginatorStudio = "originatorStudio";
175const char* const WavAudioFormat::aswgNotes = "notes";
176const char* const WavAudioFormat::aswgSession = "session";
177const char* const WavAudioFormat::aswgState = "state";
178const char* const WavAudioFormat::aswgEditor = "editor";
179const char* const WavAudioFormat::aswgMixer = "mixer";
180const char* const WavAudioFormat::aswgFxChainName = "fxChainName";
181const char* const WavAudioFormat::aswgChannelConfig = "channelConfig";
182const char* const WavAudioFormat::aswgAmbisonicFormat = "ambisonicFormat";
183const char* const WavAudioFormat::aswgAmbisonicChnOrder = "ambisonicChnOrder";
184const char* const WavAudioFormat::aswgAmbisonicNorm = "ambisonicNorm";
185const char* const WavAudioFormat::aswgMicType = "micType";
186const char* const WavAudioFormat::aswgMicConfig = "micConfig";
187const char* const WavAudioFormat::aswgMicDistance = "micDistance";
188const char* const WavAudioFormat::aswgRecordingLoc = "recordingLoc";
189const char* const WavAudioFormat::aswgIsDesigned = "isDesigned";
190const char* const WavAudioFormat::aswgRecEngineer = "recEngineer";
191const char* const WavAudioFormat::aswgRecStudio = "recStudio";
192const char* const WavAudioFormat::aswgImpulseLocation = "impulseLocation";
193const char* const WavAudioFormat::aswgCategory = "category";
194const char* const WavAudioFormat::aswgSubCategory = "subCategory";
195const char* const WavAudioFormat::aswgCatId = "catId";
196const char* const WavAudioFormat::aswgUserCategory = "userCategory";
197const char* const WavAudioFormat::aswgUserData = "userData";
198const char* const WavAudioFormat::aswgVendorCategory = "vendorCategory";
199const char* const WavAudioFormat::aswgFxName = "fxName";
200const char* const WavAudioFormat::aswgLibrary = "library";
201const char* const WavAudioFormat::aswgCreatorId = "creatorId";
202const char* const WavAudioFormat::aswgSourceId = "sourceId";
203const char* const WavAudioFormat::aswgRmsPower = "rmsPower";
204const char* const WavAudioFormat::aswgLoudness = "loudness";
205const char* const WavAudioFormat::aswgLoudnessRange = "loudnessRange";
206const char* const WavAudioFormat::aswgMaxPeak = "maxPeak";
207const char* const WavAudioFormat::aswgSpecDensity = "specDensity";
208const char* const WavAudioFormat::aswgZeroCrossRate = "zeroCrossRate";
209const char* const WavAudioFormat::aswgPapr = "papr";
210const char* const WavAudioFormat::aswgText = "text";
211const char* const WavAudioFormat::aswgEfforts = "efforts";
212const char* const WavAudioFormat::aswgEffortType = "effortType";
213const char* const WavAudioFormat::aswgProjection = "projection";
214const char* const WavAudioFormat::aswgLanguage = "language";
215const char* const WavAudioFormat::aswgTimingRestriction = "timingRestriction";
216const char* const WavAudioFormat::aswgCharacterName = "characterName";
217const char* const WavAudioFormat::aswgCharacterGender = "characterGender";
218const char* const WavAudioFormat::aswgCharacterAge = "characterAge";
219const char* const WavAudioFormat::aswgCharacterRole = "characterRole";
220const char* const WavAudioFormat::aswgActorName = "actorName";
221const char* const WavAudioFormat::aswgActorGender = "actorGender";
222const char* const WavAudioFormat::aswgDirector = "director";
223const char* const WavAudioFormat::aswgDirection = "direction";
224const char* const WavAudioFormat::aswgFxUsed = "fxUsed";
225const char* const WavAudioFormat::aswgUsageRights = "usageRights";
226const char* const WavAudioFormat::aswgIsUnion = "isUnion";
227const char* const WavAudioFormat::aswgAccent = "accent";
228const char* const WavAudioFormat::aswgEmotion = "emotion";
229const char* const WavAudioFormat::aswgComposor = "composor";
230const char* const WavAudioFormat::aswgArtist = "artist";
231const char* const WavAudioFormat::aswgSongTitle = "songTitle";
232const char* const WavAudioFormat::aswgGenre = "genre";
233const char* const WavAudioFormat::aswgSubGenre = "subGenre";
234const char* const WavAudioFormat::aswgProducer = "producer";
235const char* const WavAudioFormat::aswgMusicSup = "musicSup";
236const char* const WavAudioFormat::aswgInstrument = "instrument";
237const char* const WavAudioFormat::aswgMusicPublisher = "musicPublisher";
238const char* const WavAudioFormat::aswgRightsOwner = "rightsOwner";
239const char* const WavAudioFormat::aswgIsSource = "isSource";
240const char* const WavAudioFormat::aswgIsLoop = "isLoop";
241const char* const WavAudioFormat::aswgIntensity = "intensity";
242const char* const WavAudioFormat::aswgIsFinal = "isFinal";
243const char* const WavAudioFormat::aswgOrderRef = "orderRef";
244const char* const WavAudioFormat::aswgIsOst = "isOst";
245const char* const WavAudioFormat::aswgIsCinematic = "isCinematic";
246const char* const WavAudioFormat::aswgIsLicensed = "isLicensed";
247const char* const WavAudioFormat::aswgIsDiegetic = "isDiegetic";
248const char* const WavAudioFormat::aswgMusicVersion = "musicVersion";
249const char* const WavAudioFormat::aswgIsrcId = "isrcId";
250const char* const WavAudioFormat::aswgTempo = "tempo";
251const char* const WavAudioFormat::aswgTimeSig = "timeSig";
252const char* const WavAudioFormat::aswgInKey = "inKey";
253const char* const WavAudioFormat::aswgBillingCode = "billingCode";
254const char* const WavAudioFormat::aswgVersion = "IXML_VERSION";
255
256const char* const WavAudioFormat::ISRC = "ISRC";
257const char* const WavAudioFormat::internationalStandardRecordingCode = "international standard recording code";
258const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info";
259
260//==============================================================================
261namespace WavFileHelpers
262{
263 constexpr inline int chunkName (const char* name) noexcept { return (int) ByteOrder::littleEndianInt (name); }
264 constexpr inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; }
265
266 #if JUCE_MSVC
267 #pragma pack (push, 1)
268 #endif
269
270 struct BWAVChunk
271 {
272 char description[256];
273 char originator[32];
274 char originatorRef[32];
275 char originationDate[10];
276 char originationTime[8];
277 uint32 timeRefLow;
278 uint32 timeRefHigh;
279 uint16 version;
280 uint8 umid[64];
281 uint8 reserved[190];
282 char codingHistory[1];
283
284 void copyTo (StringMap& values, const int totalSize) const
285 {
286 values[WavAudioFormat::bwavDescription] = String::fromUTF8 (description, sizeof (description));
287 values[WavAudioFormat::bwavOriginator] = String::fromUTF8 (originator, sizeof (originator));
288 values[WavAudioFormat::bwavOriginatorRef] = String::fromUTF8 (originatorRef, sizeof (originatorRef));
289 values[WavAudioFormat::bwavOriginationDate] = String::fromUTF8 (originationDate, sizeof (originationDate));
290 values[WavAudioFormat::bwavOriginationTime] = String::fromUTF8 (originationTime, sizeof (originationTime));
291
292 auto timeLow = ByteOrder::swapIfBigEndian (timeRefLow);
293 auto timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh);
294 auto time = (((int64) timeHigh) << 32) + timeLow;
295
296 values[WavAudioFormat::bwavTimeReference] = String (time);
297 values[WavAudioFormat::bwavCodingHistory] = String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory));
298 }
299
300 static MemoryBlock createFrom (const StringMap& values)
301 {
302 MemoryBlock data (roundUpSize (sizeof (BWAVChunk) + getValueWithDefault (values, WavAudioFormat::bwavCodingHistory).getNumBytesAsUTF8()));
303 data.fillWith (0);
304
305 auto* b = (BWAVChunk*) data.getData();
306
307 // Allow these calls to overwrite an extra byte at the end, which is fine as long
308 // as they get called in the right order.
309 getValueWithDefault (values, WavAudioFormat::bwavDescription) .copyToUTF8 (b->description, 257);
310 getValueWithDefault (values, WavAudioFormat::bwavOriginator) .copyToUTF8 (b->originator, 33);
311 getValueWithDefault (values, WavAudioFormat::bwavOriginatorRef) .copyToUTF8 (b->originatorRef, 33);
312 getValueWithDefault (values, WavAudioFormat::bwavOriginationDate).copyToUTF8 (b->originationDate, 11);
313 getValueWithDefault (values, WavAudioFormat::bwavOriginationTime).copyToUTF8 (b->originationTime, 9);
314
315 auto time = getValueWithDefault (values, WavAudioFormat::bwavTimeReference).getLargeIntValue();
316 b->timeRefLow = ByteOrder::swapIfBigEndian ((uint32) (time & 0xffffffff));
317 b->timeRefHigh = ByteOrder::swapIfBigEndian ((uint32) (time >> 32));
318
319 getValueWithDefault (values, WavAudioFormat::bwavCodingHistory).copyToUTF8 (b->codingHistory, 0x7fffffff);
320
321 if (b->description[0] != 0
322 || b->originator[0] != 0
323 || b->originationDate[0] != 0
324 || b->originationTime[0] != 0
325 || b->codingHistory[0] != 0
326 || time != 0)
327 {
328 return data;
329 }
330
331 return {};
332 }
333
334 } JUCE_PACKED;
335
336 //==============================================================================
337 inline AudioChannelSet canonicalWavChannelSet (int numChannels)
338 {
339 if (numChannels == 1) return AudioChannelSet::mono();
340 if (numChannels == 2) return AudioChannelSet::stereo();
341 if (numChannels == 3) return AudioChannelSet::createLCR();
342 if (numChannels == 4) return AudioChannelSet::quadraphonic();
343 if (numChannels == 5) return AudioChannelSet::create5point0();
344 if (numChannels == 6) return AudioChannelSet::create5point1();
345 if (numChannels == 7) return AudioChannelSet::create7point0SDDS();
346 if (numChannels == 8) return AudioChannelSet::create7point1SDDS();
347
348 return AudioChannelSet::discreteChannels (numChannels);
349 }
350
351 //==============================================================================
352 struct SMPLChunk
353 {
354 struct SampleLoop
355 {
356 uint32 identifier;
357 uint32 type; // these are different in AIFF and WAV
358 uint32 start;
359 uint32 end;
360 uint32 fraction;
361 uint32 playCount;
362 } JUCE_PACKED;
363
364 uint32 manufacturer;
365 uint32 product;
366 uint32 samplePeriod;
367 uint32 midiUnityNote;
368 uint32 midiPitchFraction;
369 uint32 smpteFormat;
370 uint32 smpteOffset;
371 uint32 numSampleLoops;
372 uint32 samplerData;
373 SampleLoop loops[1];
374
375 template <typename NameType>
376 static void setValue (StringMap& values, NameType name, uint32 val)
377 {
378 values[name] = String (ByteOrder::swapIfBigEndian (val));
379 }
380
381 static void setValue (StringMap& values, int prefix, const char* name, uint32 val)
382 {
383 setValue (values, "Loop" + String (prefix) + name, val);
384 }
385
386 void copyTo (StringMap& values, const int totalSize) const
387 {
388 setValue (values, "Manufacturer", manufacturer);
389 setValue (values, "Product", product);
390 setValue (values, "SamplePeriod", samplePeriod);
391 setValue (values, "MidiUnityNote", midiUnityNote);
392 setValue (values, "MidiPitchFraction", midiPitchFraction);
393 setValue (values, "SmpteFormat", smpteFormat);
394 setValue (values, "SmpteOffset", smpteOffset);
395 setValue (values, "NumSampleLoops", numSampleLoops);
396 setValue (values, "SamplerData", samplerData);
397
398 for (int i = 0; i < (int) numSampleLoops; ++i)
399 {
400 if ((uint8*) (loops + (i + 1)) > ((uint8*) this) + totalSize)
401 break;
402
403 setValue (values, i, "Identifier", loops[i].identifier);
404 setValue (values, i, "Type", loops[i].type);
405 setValue (values, i, "Start", loops[i].start);
406 setValue (values, i, "End", loops[i].end);
407 setValue (values, i, "Fraction", loops[i].fraction);
408 setValue (values, i, "PlayCount", loops[i].playCount);
409 }
410 }
411
412 template <typename NameType>
413 static uint32 getValue (const StringMap& values, NameType name, const char* def)
414 {
415 return ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, name, def).getIntValue());
416 }
417
418 static uint32 getValue (const StringMap& values, int prefix, const char* name, const char* def)
419 {
420 return getValue (values, "Loop" + String (prefix) + name, def);
421 }
422
423 static MemoryBlock createFrom (const StringMap& values)
424 {
425 MemoryBlock data;
426 auto numLoops = jmin (64, getValueWithDefault (values, "NumSampleLoops", "0").getIntValue());
427
428 data.setSize (roundUpSize (sizeof (SMPLChunk) + (size_t) (jmax (0, numLoops - 1)) * sizeof (SampleLoop)), true);
429
430 auto s = static_cast<SMPLChunk*> (data.getData());
431
432 s->manufacturer = getValue (values, "Manufacturer", "0");
433 s->product = getValue (values, "Product", "0");
434 s->samplePeriod = getValue (values, "SamplePeriod", "0");
435 s->midiUnityNote = getValue (values, "MidiUnityNote", "60");
436 s->midiPitchFraction = getValue (values, "MidiPitchFraction", "0");
437 s->smpteFormat = getValue (values, "SmpteFormat", "0");
438 s->smpteOffset = getValue (values, "SmpteOffset", "0");
439 s->numSampleLoops = ByteOrder::swapIfBigEndian ((uint32) numLoops);
440 s->samplerData = getValue (values, "SamplerData", "0");
441
442 for (int i = 0; i < numLoops; ++i)
443 {
444 auto& loop = s->loops[i];
445 loop.identifier = getValue (values, i, "Identifier", "0");
446 loop.type = getValue (values, i, "Type", "0");
447 loop.start = getValue (values, i, "Start", "0");
448 loop.end = getValue (values, i, "End", "0");
449 loop.fraction = getValue (values, i, "Fraction", "0");
450 loop.playCount = getValue (values, i, "PlayCount", "0");
451 }
452
453 return data;
454 }
455 } JUCE_PACKED;
456
457 //==============================================================================
458 struct InstChunk
459 {
460 int8 baseNote;
461 int8 detune;
462 int8 gain;
463 int8 lowNote;
464 int8 highNote;
465 int8 lowVelocity;
466 int8 highVelocity;
467
468 static void setValue (StringMap& values, const char* name, int val)
469 {
470 values[name] = String (val);
471 }
472
473 void copyTo (StringMap& values) const
474 {
475 setValue (values, "MidiUnityNote", baseNote);
476 setValue (values, "Detune", detune);
477 setValue (values, "Gain", gain);
478 setValue (values, "LowNote", lowNote);
479 setValue (values, "HighNote", highNote);
480 setValue (values, "LowVelocity", lowVelocity);
481 setValue (values, "HighVelocity", highVelocity);
482 }
483
484 static int8 getValue (const StringMap& values, const char* name, const char* def)
485 {
486 return (int8) getValueWithDefault (values, name, def).getIntValue();
487 }
488
489 static MemoryBlock createFrom (const StringMap& values)
490 {
491 MemoryBlock data;
492
493 if ( values.find ("LowNote") != values.cend()
494 && values.find ("HighNote") != values.cend())
495 {
496 data.setSize (8, true);
497 auto* inst = static_cast<InstChunk*> (data.getData());
498
499 inst->baseNote = getValue (values, "MidiUnityNote", "60");
500 inst->detune = getValue (values, "Detune", "0");
501 inst->gain = getValue (values, "Gain", "0");
502 inst->lowNote = getValue (values, "LowNote", "0");
503 inst->highNote = getValue (values, "HighNote", "127");
504 inst->lowVelocity = getValue (values, "LowVelocity", "1");
505 inst->highVelocity = getValue (values, "HighVelocity", "127");
506 }
507
508 return data;
509 }
510 } JUCE_PACKED;
511
512 //==============================================================================
513 struct CueChunk
514 {
515 struct Cue
516 {
517 uint32 identifier;
518 uint32 order;
519 uint32 chunkID;
520 uint32 chunkStart;
521 uint32 blockStart;
522 uint32 offset;
523 } JUCE_PACKED;
524
525 uint32 numCues;
526 Cue cues[1];
527
528 static void setValue (StringMap& values, int prefix, const char* name, uint32 val)
529 {
530 values["Cue" + String (prefix) + name] = String (ByteOrder::swapIfBigEndian (val));
531 }
532
533 void copyTo (StringMap& values, const int totalSize) const
534 {
535 values["NumCuePoints"] = String (ByteOrder::swapIfBigEndian (numCues));
536
537 for (int i = 0; i < (int) numCues; ++i)
538 {
539 if ((uint8*) (cues + (i + 1)) > ((uint8*) this) + totalSize)
540 break;
541
542 setValue (values, i, "Identifier", cues[i].identifier);
543 setValue (values, i, "Order", cues[i].order);
544 setValue (values, i, "ChunkID", cues[i].chunkID);
545 setValue (values, i, "ChunkStart", cues[i].chunkStart);
546 setValue (values, i, "BlockStart", cues[i].blockStart);
547 setValue (values, i, "Offset", cues[i].offset);
548 }
549 }
550
551 static MemoryBlock createFrom (const StringMap& values)
552 {
553 MemoryBlock data;
554 const int numCues = getValueWithDefault (values, "NumCuePoints", "0").getIntValue();
555
556 if (numCues > 0)
557 {
558 data.setSize (roundUpSize (sizeof (CueChunk) + (size_t) (numCues - 1) * sizeof (Cue)), true);
559
560 auto c = static_cast<CueChunk*> (data.getData());
561
562 c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues);
563
564 const String dataChunkID (chunkName ("data"));
565 int nextOrder = 0;
566
567 #if JUCE_DEBUG
568 Array<uint32> identifiers;
569 #endif
570
571 for (int i = 0; i < numCues; ++i)
572 {
573 auto prefix = "Cue" + String (i);
574 auto identifier = (uint32) getValueWithDefault (values, prefix + "Identifier", "0").getIntValue();
575
576 #if JUCE_DEBUG
577 jassert (! identifiers.contains (identifier));
578 identifiers.add (identifier);
579 #endif
580
581 auto order = getValueWithDefault (values, prefix + "Order", String (nextOrder)).getIntValue();
582 nextOrder = jmax (nextOrder, order) + 1;
583
584 auto& cue = c->cues[i];
585 cue.identifier = ByteOrder::swapIfBigEndian ((uint32) identifier);
586 cue.order = ByteOrder::swapIfBigEndian ((uint32) order);
587 cue.chunkID = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "ChunkID", dataChunkID).getIntValue());
588 cue.chunkStart = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "ChunkStart", "0").getIntValue());
589 cue.blockStart = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "BlockStart", "0").getIntValue());
590 cue.offset = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "Offset", "0").getIntValue());
591 }
592 }
593
594 return data;
595 }
596
597 } JUCE_PACKED;
598
599 //==============================================================================
600 namespace ListChunk
601 {
602 static int getValue (const StringMap& values, const String& name)
603 {
604 return getValueWithDefault (values, name, "0").getIntValue();
605 }
606
607 static int getValue (const StringMap& values, const String& prefix, const char* name)
608 {
609 return getValue (values, prefix + name);
610 }
611
612 static void appendLabelOrNoteChunk (const StringMap& values, const String& prefix,
613 const int chunkType, MemoryOutputStream& out)
614 {
615 auto label = getValueWithDefault (values, prefix + "Text", prefix);
616 auto labelLength = (int) label.getNumBytesAsUTF8() + 1;
617 auto chunkLength = 4 + labelLength + (labelLength & 1);
618
619 out.writeInt (chunkType);
620 out.writeInt (chunkLength);
621 out.writeInt (getValue (values, prefix, "Identifier"));
622 out.write (label.toUTF8(), (size_t) labelLength);
623
624 if ((out.getDataSize() & 1) != 0)
625 out.writeByte (0);
626 }
627
628 static void appendExtraChunk (const StringMap& values, const String& prefix, MemoryOutputStream& out)
629 {
630 auto text = getValueWithDefault (values, prefix + "Text", prefix);
631
632 auto textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator
633 auto chunkLength = textLength + 20 + (textLength & 1);
634
635 out.writeInt (chunkName ("ltxt"));
636 out.writeInt (chunkLength);
637 out.writeInt (getValue (values, prefix, "Identifier"));
638 out.writeInt (getValue (values, prefix, "SampleLength"));
639 out.writeInt (getValue (values, prefix, "Purpose"));
640 out.writeShort ((short) getValue (values, prefix, "Country"));
641 out.writeShort ((short) getValue (values, prefix, "Language"));
642 out.writeShort ((short) getValue (values, prefix, "Dialect"));
643 out.writeShort ((short) getValue (values, prefix, "CodePage"));
644 out.write (text.toUTF8(), (size_t) textLength);
645
646 if ((out.getDataSize() & 1) != 0)
647 out.writeByte (0);
648 }
649
650 static MemoryBlock createFrom (const StringMap& values)
651 {
652 auto numCueLabels = getValue (values, "NumCueLabels");
653 auto numCueNotes = getValue (values, "NumCueNotes");
654 auto numCueRegions = getValue (values, "NumCueRegions");
655
656 MemoryOutputStream out;
657
658 if (numCueLabels + numCueNotes + numCueRegions > 0)
659 {
660 out.writeInt (chunkName ("adtl"));
661
662 for (int i = 0; i < numCueLabels; ++i)
663 appendLabelOrNoteChunk (values, "CueLabel" + String (i), chunkName ("labl"), out);
664
665 for (int i = 0; i < numCueNotes; ++i)
666 appendLabelOrNoteChunk (values, "CueNote" + String (i), chunkName ("note"), out);
667
668 for (int i = 0; i < numCueRegions; ++i)
669 appendExtraChunk (values, "CueRegion" + String (i), out);
670 }
671
672 return out.getMemoryBlock();
673 }
674 }
675
676 //==============================================================================
678 namespace ListInfoChunk
679 {
680 static const char* const types[] =
681 {
682 WavAudioFormat::riffInfoArchivalLocation,
683 WavAudioFormat::riffInfoArtist,
684 WavAudioFormat::riffInfoBaseURL,
685 WavAudioFormat::riffInfoCinematographer,
686 WavAudioFormat::riffInfoComment,
687 WavAudioFormat::riffInfoComments,
688 WavAudioFormat::riffInfoComment2,
689 WavAudioFormat::riffInfoCommissioned,
690 WavAudioFormat::riffInfoCopyright,
691 WavAudioFormat::riffInfoCostumeDesigner,
692 WavAudioFormat::riffInfoCountry,
693 WavAudioFormat::riffInfoCropped,
694 WavAudioFormat::riffInfoDateCreated,
695 WavAudioFormat::riffInfoDateTimeOriginal,
696 WavAudioFormat::riffInfoDefaultAudioStream,
697 WavAudioFormat::riffInfoDimension,
698 WavAudioFormat::riffInfoDirectory,
699 WavAudioFormat::riffInfoDistributedBy,
700 WavAudioFormat::riffInfoDotsPerInch,
701 WavAudioFormat::riffInfoEditedBy,
702 WavAudioFormat::riffInfoEighthLanguage,
703 WavAudioFormat::riffInfoEncodedBy,
704 WavAudioFormat::riffInfoEndTimecode,
705 WavAudioFormat::riffInfoEngineer,
706 WavAudioFormat::riffInfoFifthLanguage,
707 WavAudioFormat::riffInfoFirstLanguage,
708 WavAudioFormat::riffInfoFourthLanguage,
709 WavAudioFormat::riffInfoGenre,
710 WavAudioFormat::riffInfoKeywords,
711 WavAudioFormat::riffInfoLanguage,
712 WavAudioFormat::riffInfoLength,
713 WavAudioFormat::riffInfoLightness,
714 WavAudioFormat::riffInfoLocation,
715 WavAudioFormat::riffInfoLogoIconURL,
716 WavAudioFormat::riffInfoLogoURL,
717 WavAudioFormat::riffInfoMedium,
718 WavAudioFormat::riffInfoMoreInfoBannerImage,
719 WavAudioFormat::riffInfoMoreInfoBannerURL,
720 WavAudioFormat::riffInfoMoreInfoText,
721 WavAudioFormat::riffInfoMoreInfoURL,
722 WavAudioFormat::riffInfoMusicBy,
723 WavAudioFormat::riffInfoNinthLanguage,
724 WavAudioFormat::riffInfoNumberOfParts,
725 WavAudioFormat::riffInfoOrganisation,
726 WavAudioFormat::riffInfoPart,
727 WavAudioFormat::riffInfoProducedBy,
728 WavAudioFormat::riffInfoProductName,
729 WavAudioFormat::riffInfoProductionDesigner,
730 WavAudioFormat::riffInfoProductionStudio,
731 WavAudioFormat::riffInfoRate,
732 WavAudioFormat::riffInfoRated,
733 WavAudioFormat::riffInfoRating,
734 WavAudioFormat::riffInfoRippedBy,
735 WavAudioFormat::riffInfoSecondaryGenre,
736 WavAudioFormat::riffInfoSecondLanguage,
737 WavAudioFormat::riffInfoSeventhLanguage,
738 WavAudioFormat::riffInfoSharpness,
739 WavAudioFormat::riffInfoSixthLanguage,
740 WavAudioFormat::riffInfoSoftware,
741 WavAudioFormat::riffInfoSoundSchemeTitle,
742 WavAudioFormat::riffInfoSource,
743 WavAudioFormat::riffInfoSourceFrom,
744 WavAudioFormat::riffInfoStarring_ISTR,
745 WavAudioFormat::riffInfoStarring_STAR,
746 WavAudioFormat::riffInfoStartTimecode,
747 WavAudioFormat::riffInfoStatistics,
748 WavAudioFormat::riffInfoSubject,
749 WavAudioFormat::riffInfoTapeName,
750 WavAudioFormat::riffInfoTechnician,
751 WavAudioFormat::riffInfoThirdLanguage,
752 WavAudioFormat::riffInfoTimeCode,
753 WavAudioFormat::riffInfoTitle,
754 WavAudioFormat::riffInfoTrackNo,
755 WavAudioFormat::riffInfoTrackNumber,
756 WavAudioFormat::riffInfoURL,
757 WavAudioFormat::riffInfoVegasVersionMajor,
758 WavAudioFormat::riffInfoVegasVersionMinor,
759 WavAudioFormat::riffInfoVersion,
760 WavAudioFormat::riffInfoWatermarkURL,
761 WavAudioFormat::riffInfoWrittenBy,
762 WavAudioFormat::riffInfoYear
763 };
764
765 static bool isMatchingTypeIgnoringCase (const int value, const char* const name) noexcept
766 {
767 for (int i = 0; i < 4; ++i)
768 if ((juce_wchar) name[i] != CharacterFunctions::toUpperCase ((juce_wchar) ((value >> (i * 8)) & 0xff)))
769 return false;
770
771 return true;
772 }
773
774 static void addToMetadata (StringMap& values, InputStream& input, int64 chunkEnd)
775 {
776 while (input.getPosition() < chunkEnd)
777 {
778 auto infoType = input.readInt();
779 auto infoLength = chunkEnd - input.getPosition();
780
781 if (infoLength > 0)
782 {
783 infoLength = jmin (infoLength, (int64) input.readInt());
784
785 if (infoLength <= 0)
786 return;
787
788 for (auto& type : types)
789 {
790 if (isMatchingTypeIgnoringCase (infoType, type))
791 {
792 MemoryBlock mb;
793 input.readIntoMemoryBlock (mb, (ssize_t) infoLength);
794 values[type] = String::createStringFromData ((const char*) mb.getData(),
795 (int) mb.getSize());
796 break;
797 }
798 }
799 }
800 }
801 }
802
803 static bool writeValue (const StringMap& values, MemoryOutputStream& out, const char* paramName)
804 {
805 auto value = getValueWithDefault (values, paramName, {});
806
807 if (value.isEmpty())
808 return false;
809
810 auto valueLength = (int) value.getNumBytesAsUTF8() + 1;
811 auto chunkLength = valueLength + (valueLength & 1);
812
813 out.writeInt (chunkName (paramName));
814 out.writeInt (chunkLength);
815 out.write (value.toUTF8(), (size_t) valueLength);
816
817 if ((out.getDataSize() & 1) != 0)
818 out.writeByte (0);
819
820 return true;
821 }
822
823 static MemoryBlock createFrom (const StringMap& values)
824 {
825 MemoryOutputStream out;
826 out.writeInt (chunkName ("INFO"));
827 bool anyParamsDefined = false;
828
829 for (auto& type : types)
830 if (writeValue (values, out, type))
831 anyParamsDefined = true;
832
833 return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock();
834 }
835 }
836
837 //==============================================================================
838 struct AcidChunk
839 {
841 AcidChunk (InputStream& input, size_t length)
842 {
843 zerostruct (*this);
844 input.read (this, (int) jmin (sizeof (*this), length));
845 }
846
847 AcidChunk (const StringMap& values)
848 {
849 zerostruct (*this);
850
851 flags = getFlagIfPresent (values, WavAudioFormat::acidOneShot, 0x01)
852 | getFlagIfPresent (values, WavAudioFormat::acidRootSet, 0x02)
853 | getFlagIfPresent (values, WavAudioFormat::acidStretch, 0x04)
854 | getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08)
855 | getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10);
856
857 if (getValueWithDefault (values, WavAudioFormat::acidRootSet).getIntValue() != 0)
858 rootNote = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidRootNote).getIntValue());
859
860 numBeats = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, WavAudioFormat::acidBeats).getIntValue());
861 meterDenominator = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidDenominator).getIntValue());
862 meterNumerator = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidNumerator).getIntValue());
863
864 const auto iter = values.find (WavAudioFormat::acidTempo);
865
866 if (iter != values.cend())
867 tempo = swapFloatByteOrder (iter->second.getFloatValue());
868 }
869
870 static MemoryBlock createFrom (const StringMap& values)
871 {
872 return AcidChunk (values).toMemoryBlock();
873 }
874
875 MemoryBlock toMemoryBlock() const
876 {
877 return (flags != 0 || rootNote != 0 || numBeats != 0 || meterDenominator != 0 || meterNumerator != 0)
878 ? MemoryBlock (this, sizeof (*this)) : MemoryBlock();
879 }
880
881 void addToMetadata (StringMap& values) const
882 {
883 setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01);
884 setBoolFlag (values, WavAudioFormat::acidRootSet, 0x02);
885 setBoolFlag (values, WavAudioFormat::acidStretch, 0x04);
886 setBoolFlag (values, WavAudioFormat::acidDiskBased, 0x08);
887 setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10);
888
889 if (flags & 0x02) // root note set
890 values[WavAudioFormat::acidRootNote] = String (ByteOrder::swapIfBigEndian (rootNote));
891
892 values[WavAudioFormat::acidBeats] = String (ByteOrder::swapIfBigEndian (numBeats));
893 values[WavAudioFormat::acidDenominator] = String (ByteOrder::swapIfBigEndian (meterDenominator));
894 values[WavAudioFormat::acidNumerator] = String (ByteOrder::swapIfBigEndian (meterNumerator));
895 values[WavAudioFormat::acidTempo] = String (swapFloatByteOrder (tempo));
896 }
897
898 void setBoolFlag (StringMap& values, const char* name, uint32 mask) const
899 {
900 values[name] = (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0";
901 }
902
903 static uint32 getFlagIfPresent (const StringMap& values, const char* name, uint32 flag)
904 {
905 return getValueWithDefault (values, name).getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0;
906 }
907
908 static float swapFloatByteOrder (const float x) noexcept
909 {
910 #ifdef JUCE_BIG_ENDIAN
911 union { uint32 asInt; float asFloat; } n;
912 n.asFloat = x;
913 n.asInt = ByteOrder::swap (n.asInt);
914 return n.asFloat;
915 #else
916 return x;
917 #endif
918 }
919
920 uint32 flags;
921 uint16 rootNote;
922 uint16 reserved1;
923 float reserved2;
924 uint32 numBeats;
925 uint16 meterDenominator;
926 uint16 meterNumerator;
927 float tempo;
928
929 } JUCE_PACKED;
930
931 //==============================================================================
932 struct TracktionChunk
933 {
934 static MemoryBlock createFrom (const StringMap& values)
935 {
936 MemoryOutputStream out;
937 auto s = getValueWithDefault (values, WavAudioFormat::tracktionLoopInfo);
938
939 if (s.isNotEmpty())
940 {
941 out.writeString (s);
942
943 if ((out.getDataSize() & 1) != 0)
944 out.writeByte (0);
945 }
946
947 return out.getMemoryBlock();
948 }
949 };
950
951 //==============================================================================
952 namespace IXMLChunk
953 {
954 static const std::unordered_set<String> aswgMetadataKeys
955 {
956 WavAudioFormat::aswgContentType,
957 WavAudioFormat::aswgProject,
958 WavAudioFormat::aswgOriginator,
959 WavAudioFormat::aswgOriginatorStudio,
960 WavAudioFormat::aswgNotes,
961 WavAudioFormat::aswgSession,
962 WavAudioFormat::aswgState,
963 WavAudioFormat::aswgEditor,
964 WavAudioFormat::aswgMixer,
965 WavAudioFormat::aswgFxChainName,
966 WavAudioFormat::aswgChannelConfig,
967 WavAudioFormat::aswgAmbisonicFormat,
968 WavAudioFormat::aswgAmbisonicChnOrder,
969 WavAudioFormat::aswgAmbisonicNorm,
970 WavAudioFormat::aswgMicType,
971 WavAudioFormat::aswgMicConfig,
972 WavAudioFormat::aswgMicDistance,
973 WavAudioFormat::aswgRecordingLoc,
974 WavAudioFormat::aswgIsDesigned,
975 WavAudioFormat::aswgRecEngineer,
976 WavAudioFormat::aswgRecStudio,
977 WavAudioFormat::aswgImpulseLocation,
978 WavAudioFormat::aswgCategory,
979 WavAudioFormat::aswgSubCategory,
980 WavAudioFormat::aswgCatId,
981 WavAudioFormat::aswgUserCategory,
982 WavAudioFormat::aswgUserData,
983 WavAudioFormat::aswgVendorCategory,
984 WavAudioFormat::aswgFxName,
985 WavAudioFormat::aswgLibrary,
986 WavAudioFormat::aswgCreatorId,
987 WavAudioFormat::aswgSourceId,
988 WavAudioFormat::aswgRmsPower,
989 WavAudioFormat::aswgLoudness,
990 WavAudioFormat::aswgLoudnessRange,
991 WavAudioFormat::aswgMaxPeak,
992 WavAudioFormat::aswgSpecDensity,
993 WavAudioFormat::aswgZeroCrossRate,
994 WavAudioFormat::aswgPapr,
995 WavAudioFormat::aswgText,
996 WavAudioFormat::aswgEfforts,
997 WavAudioFormat::aswgEffortType,
998 WavAudioFormat::aswgProjection,
999 WavAudioFormat::aswgLanguage,
1000 WavAudioFormat::aswgTimingRestriction,
1001 WavAudioFormat::aswgCharacterName,
1002 WavAudioFormat::aswgCharacterGender,
1003 WavAudioFormat::aswgCharacterAge,
1004 WavAudioFormat::aswgCharacterRole,
1005 WavAudioFormat::aswgActorName,
1006 WavAudioFormat::aswgActorGender,
1007 WavAudioFormat::aswgDirector,
1008 WavAudioFormat::aswgDirection,
1009 WavAudioFormat::aswgFxUsed,
1010 WavAudioFormat::aswgUsageRights,
1011 WavAudioFormat::aswgIsUnion,
1012 WavAudioFormat::aswgAccent,
1013 WavAudioFormat::aswgEmotion,
1014 WavAudioFormat::aswgComposor,
1015 WavAudioFormat::aswgArtist,
1016 WavAudioFormat::aswgSongTitle,
1017 WavAudioFormat::aswgGenre,
1018 WavAudioFormat::aswgSubGenre,
1019 WavAudioFormat::aswgProducer,
1020 WavAudioFormat::aswgMusicSup,
1021 WavAudioFormat::aswgInstrument,
1022 WavAudioFormat::aswgMusicPublisher,
1023 WavAudioFormat::aswgRightsOwner,
1024 WavAudioFormat::aswgIsSource,
1025 WavAudioFormat::aswgIsLoop,
1026 WavAudioFormat::aswgIntensity,
1027 WavAudioFormat::aswgIsFinal,
1028 WavAudioFormat::aswgOrderRef,
1029 WavAudioFormat::aswgIsOst,
1030 WavAudioFormat::aswgIsCinematic,
1031 WavAudioFormat::aswgIsLicensed,
1032 WavAudioFormat::aswgIsDiegetic,
1033 WavAudioFormat::aswgMusicVersion,
1034 WavAudioFormat::aswgIsrcId,
1035 WavAudioFormat::aswgTempo,
1036 WavAudioFormat::aswgTimeSig,
1037 WavAudioFormat::aswgInKey,
1038 WavAudioFormat::aswgBillingCode
1039 };
1040
1041 static void addToMetadata (StringMap& destValues, const String& source)
1042 {
1043 if (auto xml = parseXML (source))
1044 {
1045 if (xml->hasTagName ("BWFXML"))
1046 {
1047 if (const auto* entry = xml->getChildByName (WavAudioFormat::aswgVersion))
1048 destValues[WavAudioFormat::aswgVersion] = entry->getAllSubText();
1049
1050 if (const auto* aswgElement = xml->getChildByName ("ASWG"))
1051 {
1052 for (const auto* entry : aswgElement->getChildIterator())
1053 {
1054 const auto& tag = entry->getTagName();
1055
1056 if (aswgMetadataKeys.find (tag) != aswgMetadataKeys.end())
1057 destValues[tag] = entry->getAllSubText();
1058 }
1059 }
1060 }
1061 }
1062 }
1063
1064 static MemoryBlock createFrom (const StringMap& values)
1065 {
1066 auto createTextElement = [] (const StringRef& key, const StringRef& value)
1067 {
1068 auto* elem = new XmlElement (key);
1069 elem->addTextElement (value);
1070 return elem;
1071 };
1072
1073 std::unique_ptr<XmlElement> aswgElement;
1074
1075 for (const auto& pair : values)
1076 {
1077 if (aswgMetadataKeys.find (pair.first) != aswgMetadataKeys.end())
1078 {
1079 if (aswgElement == nullptr)
1080 aswgElement = std::make_unique<XmlElement> ("ASWG");
1081
1082 aswgElement->addChildElement (createTextElement (pair.first, pair.second));
1083 }
1084 }
1085
1086 MemoryOutputStream outputStream;
1087
1088 if (aswgElement != nullptr)
1089 {
1090 XmlElement xml ("BWFXML");
1091 auto aswgVersion = getValueWithDefault (values, WavAudioFormat::aswgVersion, "3.01");
1092 xml.addChildElement (createTextElement (WavAudioFormat::aswgVersion, aswgVersion));
1093 xml.addChildElement (aswgElement.release());
1094 xml.writeTo (outputStream);
1095 outputStream.writeRepeatedByte (0, outputStream.getDataSize());
1096 }
1097
1098 return outputStream.getMemoryBlock();
1099 }
1100 }
1101
1102 //==============================================================================
1103 namespace AXMLChunk
1104 {
1105 static void addToMetadata (StringMap& destValues, const String& source)
1106 {
1107 if (auto xml = parseXML (source))
1108 {
1109 if (xml->hasTagName ("ebucore:ebuCoreMain"))
1110 {
1111 if (auto xml2 = xml->getChildByName ("ebucore:coreMetadata"))
1112 {
1113 if (auto xml3 = xml2->getChildByName ("ebucore:identifier"))
1114 {
1115 if (auto xml4 = xml3->getChildByName ("dc:identifier"))
1116 {
1117 auto ISRCCode = xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true);
1118
1119 if (ISRCCode.isNotEmpty())
1120 {
1121 // We set ISRC here for backwards compatibility.
1122 // If the INFO 'source' field is set in the info chunk, then the
1123 // value for this key will be overwritten later.
1124 destValues[WavAudioFormat::riffInfoSource] = destValues[WavAudioFormat::internationalStandardRecordingCode] = ISRCCode;
1125 }
1126 }
1127 }
1128 }
1129 }
1130 }
1131 }
1132
1133 static MemoryBlock createFrom (const StringMap& values)
1134 {
1135 // Use the new ISRC key if it is present, but fall back to the
1136 // INFO 'source' value for backwards compatibility.
1137 auto ISRC = getValueWithDefault (values,
1138 WavAudioFormat::internationalStandardRecordingCode,
1139 getValueWithDefault (values, WavAudioFormat::riffInfoSource));
1140
1141 MemoryOutputStream xml;
1142
1143 if (ISRC.isNotEmpty())
1144 {
1145 // If you are trying to set the ISRC, make sure that you are using
1146 // WavAudioFormat::internationalStandardRecordingCode as the metadata key,
1147 // and that the value is 12 characters long. If you are trying to set the
1148 // 'source' field in the INFO chunk, set the
1149 // WavAudioFormat::internationalStandardRecordingCode metadata field to the
1150 // empty string to silence this assertion.
1151 jassert (ISRC.length() == 12);
1152
1153 xml << "<ebucore:ebuCoreMain xmlns:dc=\" http://purl.org/dc/elements/1.1/\" "
1154 "xmlns:ebucore=\"urn:ebu:metadata-schema:ebuCore_2012\">"
1155 "<ebucore:coreMetadata>"
1156 "<ebucore:identifier typeLabel=\"GUID\" "
1157 "typeDefinition=\"Globally Unique Identifier\" "
1158 "formatLabel=\"ISRC\" "
1159 "formatDefinition=\"International Standard Recording Code\" "
1160 "formatLink=\"http://www.ebu.ch/metadata/cs/ebu_IdentifierTypeCodeCS.xml#3.7\">"
1161 "<dc:identifier>ISRC:" << ISRC << "</dc:identifier>"
1162 "</ebucore:identifier>"
1163 "</ebucore:coreMetadata>"
1164 "</ebucore:ebuCoreMain>";
1165
1166 xml.writeRepeatedByte (0, xml.getDataSize()); // ensures even size, null termination and room for future growing
1167 }
1168
1169 return xml.getMemoryBlock();
1170 }
1171 }
1172
1173 //==============================================================================
1174 struct ExtensibleWavSubFormat
1175 {
1176 uint32 data1;
1177 uint16 data2;
1178 uint16 data3;
1179 uint8 data4[8];
1180
1181 bool operator== (const ExtensibleWavSubFormat& other) const noexcept { return memcmp (this, &other, sizeof (*this)) == 0; }
1182 bool operator!= (const ExtensibleWavSubFormat& other) const noexcept { return ! operator== (other); }
1183
1184 } JUCE_PACKED;
1185
1186 static const ExtensibleWavSubFormat pcmFormat = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
1187 static const ExtensibleWavSubFormat IEEEFloatFormat = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
1188 static const ExtensibleWavSubFormat ambisonicFormat = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } };
1189
1190 struct DataSize64Chunk // chunk ID = 'ds64' if data size > 0xffffffff, 'JUNK' otherwise
1191 {
1192 uint32 riffSizeLow; // low 4 byte size of RF64 block
1193 uint32 riffSizeHigh; // high 4 byte size of RF64 block
1194 uint32 dataSizeLow; // low 4 byte size of data chunk
1195 uint32 dataSizeHigh; // high 4 byte size of data chunk
1196 uint32 sampleCountLow; // low 4 byte sample count of fact chunk
1197 uint32 sampleCountHigh; // high 4 byte sample count of fact chunk
1198 uint32 tableLength; // number of valid entries in array 'table'
1199 } JUCE_PACKED;
1200
1201 #if JUCE_MSVC
1202 #pragma pack (pop)
1203 #endif
1204}
1205
1206//==============================================================================
1207class WavAudioFormatReader final : public AudioFormatReader
1208{
1209public:
1210 WavAudioFormatReader (InputStream* in) : AudioFormatReader (in, wavFormatName)
1211 {
1212 using namespace WavFileHelpers;
1213 uint64 len = 0, end = 0;
1214 int cueNoteIndex = 0;
1215 int cueLabelIndex = 0;
1216 int cueRegionIndex = 0;
1217
1218 StringMap dict;
1219
1220 auto streamStartPos = input->getPosition();
1221 auto firstChunkType = input->readInt();
1222
1223 if (firstChunkType == chunkName ("RF64"))
1224 {
1225 input->skipNextBytes (4); // size is -1 for RF64
1226 isRF64 = true;
1227 }
1228 else if (firstChunkType == chunkName ("RIFF"))
1229 {
1230 len = (uint64) (uint32) input->readInt();
1231 end = len + (uint64) input->getPosition();
1232 }
1233 else
1234 {
1235 return;
1236 }
1237
1238 auto startOfRIFFChunk = input->getPosition();
1239
1240 if (input->readInt() == chunkName ("WAVE"))
1241 {
1242 if (isRF64 && input->readInt() == chunkName ("ds64"))
1243 {
1244 auto length = (uint32) input->readInt();
1245
1246 if (length < 28)
1247 return;
1248
1249 auto chunkEnd = input->getPosition() + length + (length & 1);
1250 len = (uint64) input->readInt64();
1251 end = len + (uint64) startOfRIFFChunk;
1252 dataLength = input->readInt64();
1253 input->setPosition (chunkEnd);
1254 }
1255
1256 while ((uint64) input->getPosition() < end && ! input->isExhausted())
1257 {
1258 auto chunkType = input->readInt();
1259 auto length = (uint32) input->readInt();
1260 auto chunkEnd = input->getPosition() + length + (length & 1);
1261
1262 if (chunkType == chunkName ("fmt "))
1263 {
1264 // read the format chunk
1265 auto format = (unsigned short) input->readShort();
1266 numChannels = (unsigned int) input->readShort();
1267 sampleRate = input->readInt();
1268 auto bytesPerSec = input->readInt();
1269 input->skipNextBytes (2);
1270 bitsPerSample = (unsigned int) (int) input->readShort();
1271
1272 if (bitsPerSample > 64 && (int) sampleRate != 0)
1273 {
1274 bytesPerFrame = bytesPerSec / (int) sampleRate;
1275
1276 if (numChannels != 0)
1277 bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels;
1278 }
1279 else
1280 {
1281 bytesPerFrame = (int) (numChannels * bitsPerSample / 8);
1282 }
1283
1284 if (format == 3)
1285 {
1286 usesFloatingPointData = true;
1287 }
1288 else if (format == 0xfffe) // WAVE_FORMAT_EXTENSIBLE
1289 {
1290 if (length < 40) // too short
1291 {
1292 bytesPerFrame = 0;
1293 }
1294 else
1295 {
1296 input->skipNextBytes (4); // skip over size and bitsPerSample
1297 auto channelMask = input->readInt();
1298 dict["ChannelMask"] = String (channelMask);
1299 channelLayout = getChannelLayoutFromMask (channelMask, numChannels);
1300
1301 ExtensibleWavSubFormat subFormat;
1302 subFormat.data1 = (uint32) input->readInt();
1303 subFormat.data2 = (uint16) input->readShort();
1304 subFormat.data3 = (uint16) input->readShort();
1305 input->read (subFormat.data4, sizeof (subFormat.data4));
1306
1307 if (subFormat == IEEEFloatFormat)
1308 usesFloatingPointData = true;
1309 else if (subFormat != pcmFormat && subFormat != ambisonicFormat)
1310 bytesPerFrame = 0;
1311 }
1312 }
1313 else if (format == 0x674f // WAVE_FORMAT_OGG_VORBIS_MODE_1
1314 || format == 0x6750 // WAVE_FORMAT_OGG_VORBIS_MODE_2
1315 || format == 0x6751 // WAVE_FORMAT_OGG_VORBIS_MODE_3
1316 || format == 0x676f // WAVE_FORMAT_OGG_VORBIS_MODE_1_PLUS
1317 || format == 0x6770 // WAVE_FORMAT_OGG_VORBIS_MODE_2_PLUS
1318 || format == 0x6771) // WAVE_FORMAT_OGG_VORBIS_MODE_3_PLUS
1319 {
1320 isSubformatOggVorbis = true;
1321 sampleRate = 0; // to mark the wav reader as failed
1322 input->setPosition (streamStartPos);
1323 return;
1324 }
1325 else if (format != 1)
1326 {
1327 bytesPerFrame = 0;
1328 }
1329 }
1330 else if (chunkType == chunkName ("data"))
1331 {
1332 if (isRF64)
1333 {
1334 if (dataLength > 0)
1335 chunkEnd = input->getPosition() + dataLength + (dataLength & 1);
1336 }
1337 else
1338 {
1339 dataLength = length;
1340 }
1341
1342 dataChunkStart = input->getPosition();
1343 lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0;
1344 }
1345 else if (chunkType == chunkName ("bext"))
1346 {
1347 bwavChunkStart = input->getPosition();
1348 bwavSize = length;
1349
1350 HeapBlock<BWAVChunk> bwav;
1351 bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1);
1352 input->read (bwav, (int) length);
1353 bwav->copyTo (dict, (int) length);
1354 }
1355 else if (chunkType == chunkName ("smpl"))
1356 {
1357 HeapBlock<SMPLChunk> smpl;
1358 smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1);
1359 input->read (smpl, (int) length);
1360 smpl->copyTo (dict, (int) length);
1361 }
1362 else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which...
1363 {
1364 HeapBlock<InstChunk> inst;
1365 inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
1366 input->read (inst, (int) length);
1367 inst->copyTo (dict);
1368 }
1369 else if (chunkType == chunkName ("cue "))
1370 {
1371 HeapBlock<CueChunk> cue;
1372 cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1);
1373 input->read (cue, (int) length);
1374 cue->copyTo (dict, (int) length);
1375 }
1376 else if (chunkType == chunkName ("axml"))
1377 {
1378 MemoryBlock axml;
1379 input->readIntoMemoryBlock (axml, (ssize_t) length);
1380 AXMLChunk::addToMetadata (dict, axml.toString());
1381 }
1382 else if (chunkType == chunkName ("iXML"))
1383 {
1384 MemoryBlock ixml;
1385 input->readIntoMemoryBlock (ixml, (ssize_t) length);
1386 IXMLChunk::addToMetadata (dict, ixml.toString());
1387 }
1388 else if (chunkType == chunkName ("LIST"))
1389 {
1390 auto subChunkType = input->readInt();
1391
1392 if (subChunkType == chunkName ("info") || subChunkType == chunkName ("INFO"))
1393 {
1394 ListInfoChunk::addToMetadata (dict, *input, chunkEnd);
1395 }
1396 else if (subChunkType == chunkName ("adtl"))
1397 {
1398 while (input->getPosition() < chunkEnd)
1399 {
1400 auto adtlChunkType = input->readInt();
1401 auto adtlLength = (uint32) input->readInt();
1402 auto adtlChunkEnd = input->getPosition() + (adtlLength + (adtlLength & 1));
1403
1404 if (adtlChunkType == chunkName ("labl") || adtlChunkType == chunkName ("note"))
1405 {
1406 String prefix;
1407
1408 if (adtlChunkType == chunkName ("labl"))
1409 prefix << "CueLabel" << cueLabelIndex++;
1410 else if (adtlChunkType == chunkName ("note"))
1411 prefix << "CueNote" << cueNoteIndex++;
1412
1413 auto identifier = (uint32) input->readInt();
1414 auto stringLength = (int) adtlLength - 4;
1415
1416 MemoryBlock textBlock;
1417 input->readIntoMemoryBlock (textBlock, stringLength);
1418
1419 dict[prefix + "Identifier"] = String (identifier);
1420 dict[prefix + "Text"] = textBlock.toString();
1421 }
1422 else if (adtlChunkType == chunkName ("ltxt"))
1423 {
1424 auto prefix = "CueRegion" + String (cueRegionIndex++);
1425 auto identifier = (uint32) input->readInt();
1426 auto sampleLength = (uint32) input->readInt();
1427 auto purpose = (uint32) input->readInt();
1428 auto country = (uint16) input->readShort();
1429 auto language = (uint16) input->readShort();
1430 auto dialect = (uint16) input->readShort();
1431 auto codePage = (uint16) input->readShort();
1432 auto stringLength = adtlLength - 20;
1433
1434 MemoryBlock textBlock;
1435 input->readIntoMemoryBlock (textBlock, (int) stringLength);
1436
1437 dict[prefix + "Identifier"] = String (identifier);
1438 dict[prefix + "SampleLength"] = String (sampleLength);
1439 dict[prefix + "Purpose"] = String (purpose);
1440 dict[prefix + "Country"] = String (country);
1441 dict[prefix + "Language"] = String (language);
1442 dict[prefix + "Dialect"] = String (dialect);
1443 dict[prefix + "CodePage"] = String (codePage);
1444 dict[prefix + "Text"] = textBlock.toString();
1445 }
1446
1447 input->setPosition (adtlChunkEnd);
1448 }
1449 }
1450 }
1451 else if (chunkType == chunkName ("acid"))
1452 {
1453 AcidChunk (*input, length).addToMetadata (dict);
1454 }
1455 else if (chunkType == chunkName ("Trkn"))
1456 {
1457 MemoryBlock tracktion;
1458 input->readIntoMemoryBlock (tracktion, (ssize_t) length);
1459 dict[WavAudioFormat::tracktionLoopInfo] = tracktion.toString();
1460 }
1461 else if (chunkEnd <= input->getPosition())
1462 {
1463 break;
1464 }
1465
1466 input->setPosition (chunkEnd);
1467 }
1468 }
1469
1470 if (cueLabelIndex > 0) dict["NumCueLabels"] = String (cueLabelIndex);
1471 if (cueNoteIndex > 0) dict["NumCueNotes"] = String (cueNoteIndex);
1472 if (cueRegionIndex > 0) dict["NumCueRegions"] = String (cueRegionIndex);
1473 if (dict.size() > 0) dict["MetaDataSource"] = "WAV";
1474
1475 metadataValues.addUnorderedMap (dict);
1476 }
1477
1478 //==============================================================================
1479 bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer,
1480 int64 startSampleInFile, int numSamples) override
1481 {
1482 clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
1483 startSampleInFile, numSamples, lengthInSamples);
1484
1485 if (numSamples <= 0)
1486 return true;
1487
1488 input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame);
1489
1490 while (numSamples > 0)
1491 {
1492 const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
1493 char tempBuffer[tempBufSize];
1494
1495 auto numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples);
1496 auto bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);
1497
1498 if (bytesRead < numThisTime * bytesPerFrame)
1499 {
1500 jassert (bytesRead >= 0);
1501 zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead));
1502 }
1503
1504 copySampleData (bitsPerSample, usesFloatingPointData,
1505 destSamples, startOffsetInDestBuffer, numDestChannels,
1506 tempBuffer, (int) numChannels, numThisTime);
1507
1508 startOffsetInDestBuffer += numThisTime;
1509 numSamples -= numThisTime;
1510 }
1511
1512 return true;
1513 }
1514
1515 static void copySampleData (unsigned int numBitsPerSample, const bool floatingPointData,
1516 int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels,
1517 const void* sourceData, int numberOfChannels, int numSamples) noexcept
1518 {
1519 switch (numBitsPerSample)
1520 {
1521 case 8: ReadHelper<AudioData::Int32, AudioData::UInt8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break;
1522 case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break;
1523 case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break;
1524 case 32: if (floatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
1525 else ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples);
1526 break;
1527 default: jassertfalse; break;
1528 }
1529 }
1530
1531 //==============================================================================
1532 AudioChannelSet getChannelLayout() override
1533 {
1534 if (channelLayout.size() == static_cast<int> (numChannels))
1535 return channelLayout;
1536
1537 return WavFileHelpers::canonicalWavChannelSet (static_cast<int> (numChannels));
1538 }
1539
1540 static AudioChannelSet getChannelLayoutFromMask (int dwChannelMask, size_t totalNumChannels)
1541 {
1542 AudioChannelSet wavFileChannelLayout;
1543
1544 // AudioChannelSet and wav's dwChannelMask are compatible
1545 BigInteger channelBits (dwChannelMask);
1546
1547 for (auto bit = channelBits.findNextSetBit (0); bit >= 0; bit = channelBits.findNextSetBit (bit + 1))
1548 wavFileChannelLayout.addChannel (static_cast<AudioChannelSet::ChannelType> (bit + 1));
1549
1550 // channel layout and number of channels do not match
1551 if (wavFileChannelLayout.size() != static_cast<int> (totalNumChannels))
1552 {
1553 // for backward compatibility with old wav files, assume 1 or 2
1554 // channel wav files are mono/stereo respectively
1555 if (totalNumChannels <= 2 && dwChannelMask == 0)
1556 wavFileChannelLayout = AudioChannelSet::canonicalChannelSet (static_cast<int> (totalNumChannels));
1557 else
1558 {
1559 auto discreteSpeaker = static_cast<int> (AudioChannelSet::discreteChannel0);
1560
1561 while (wavFileChannelLayout.size() < static_cast<int> (totalNumChannels))
1562 wavFileChannelLayout.addChannel (static_cast<AudioChannelSet::ChannelType> (discreteSpeaker++));
1563 }
1564 }
1565
1566 return wavFileChannelLayout;
1567 }
1568
1569 int64 bwavChunkStart = 0, bwavSize = 0;
1570 int64 dataChunkStart = 0, dataLength = 0;
1571 int bytesPerFrame = 0;
1572 bool isRF64 = false;
1573 bool isSubformatOggVorbis = false;
1574
1575 AudioChannelSet channelLayout;
1576
1577private:
1578 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader)
1579};
1580
1581//==============================================================================
1582class WavAudioFormatWriter final : public AudioFormatWriter
1583{
1584public:
1585 WavAudioFormatWriter (OutputStream* const out, const double rate,
1586 const AudioChannelSet& channelLayoutToUse, const unsigned int bits,
1587 const StringPairArray& metadataValues)
1588 : AudioFormatWriter (out, wavFormatName, rate, channelLayoutToUse, bits)
1589 {
1590 using namespace WavFileHelpers;
1591
1592 if (metadataValues.size() > 0)
1593 {
1594 // The meta data should have been sanitised for the WAV format.
1595 // If it was originally sourced from an AIFF file the MetaDataSource
1596 // key should be removed (or set to "WAV") once this has been done
1597 jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF");
1598
1599 const auto map = toMap (metadataValues);
1600
1601 bwavChunk = BWAVChunk::createFrom (map);
1602 ixmlChunk = IXMLChunk::createFrom (map);
1603 axmlChunk = AXMLChunk::createFrom (map);
1604 smplChunk = SMPLChunk::createFrom (map);
1605 instChunk = InstChunk::createFrom (map);
1606 cueChunk = CueChunk ::createFrom (map);
1607 listChunk = ListChunk::createFrom (map);
1608 listInfoChunk = ListInfoChunk::createFrom (map);
1609 acidChunk = AcidChunk::createFrom (map);
1610 trckChunk = TracktionChunk::createFrom (map);
1611 }
1612
1613 headerPosition = out->getPosition();
1614 writeHeader();
1615 }
1616
1617 ~WavAudioFormatWriter() override
1618 {
1619 writeHeader();
1620 }
1621
1622 //==============================================================================
1623 bool write (const int** data, int numSamples) override
1624 {
1625 jassert (numSamples >= 0);
1626 jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel!
1627
1628 if (writeFailed)
1629 return false;
1630
1631 auto bytes = numChannels * (size_t) numSamples * bitsPerSample / 8;
1632 tempBlock.ensureSize (bytes, false);
1633
1634 switch (bitsPerSample)
1635 {
1636 case 8: WriteHelper<AudioData::UInt8, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
1637 case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
1638 case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
1639 case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break;
1640 default: jassertfalse; break;
1641 }
1642
1643 if (! output->write (tempBlock.getData(), bytes))
1644 {
1645 // failed to write to disk, so let's try writing the header.
1646 // If it's just run out of disk space, then if it does manage
1647 // to write the header, we'll still have a usable file..
1648 writeHeader();
1649 writeFailed = true;
1650 return false;
1651 }
1652
1653 bytesWritten += bytes;
1654 lengthInSamples += (uint64) numSamples;
1655 return true;
1656 }
1657
1658 bool flush() override
1659 {
1660 auto lastWritePos = output->getPosition();
1661 writeHeader();
1662
1663 if (output->setPosition (lastWritePos))
1664 return true;
1665
1666 // if this fails, you've given it an output stream that can't seek! It needs
1667 // to be able to seek back to write the header
1668 jassertfalse;
1669 return false;
1670 }
1671
1672private:
1673 MemoryBlock tempBlock, bwavChunk, ixmlChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk;
1674 uint64 lengthInSamples = 0, bytesWritten = 0;
1675 int64 headerPosition = 0;
1676 bool writeFailed = false;
1677
1678 void writeHeader()
1679 {
1680 if ((bytesWritten & 1) != 0) // pad to an even length
1681 output->writeByte (0);
1682
1683 using namespace WavFileHelpers;
1684
1685 if (headerPosition != output->getPosition() && ! output->setPosition (headerPosition))
1686 {
1687 // if this fails, you've given it an output stream that can't seek! It needs to be
1688 // able to seek back to go back and write the header after the data has been written.
1689 jassertfalse;
1690 return;
1691 }
1692
1693 const size_t bytesPerFrame = numChannels * bitsPerSample / 8;
1694 uint64 audioDataSize = bytesPerFrame * lengthInSamples;
1695 auto channelMask = getChannelMaskFromChannelLayout (channelLayout);
1696
1697 const bool isRF64 = (bytesWritten >= 0x100000000LL);
1698 const bool isWaveFmtEx = isRF64 || (channelMask != 0);
1699
1700 int64 riffChunkSize = (int64) (4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */
1701 + 8 + audioDataSize + (audioDataSize & 1)
1702 + chunkSize (bwavChunk)
1703 + chunkSize (ixmlChunk)
1704 + chunkSize (axmlChunk)
1705 + chunkSize (smplChunk)
1706 + chunkSize (instChunk)
1707 + chunkSize (cueChunk)
1708 + chunkSize (listChunk)
1709 + chunkSize (listInfoChunk)
1710 + chunkSize (acidChunk)
1711 + chunkSize (trckChunk)
1712 + (8 + 28)); // (ds64 chunk)
1713
1714 riffChunkSize += (riffChunkSize & 1);
1715
1716 if (isRF64)
1717 writeChunkHeader (chunkName ("RF64"), -1);
1718 else
1719 writeChunkHeader (chunkName ("RIFF"), (int) riffChunkSize);
1720
1721 output->writeInt (chunkName ("WAVE"));
1722
1723 if (! isRF64)
1724 {
1725 #if ! JUCE_WAV_DO_NOT_PAD_HEADER_SIZE
1726 /* NB: This junk chunk is added for padding, so that the header is a fixed size
1727 regardless of whether it's RF64 or not. That way, we can begin recording a file,
1728 and when it's finished, can go back and write either a RIFF or RF64 header,
1729 depending on whether more than 2^32 samples were written.
1730
1731 The JUCE_WAV_DO_NOT_PAD_HEADER_SIZE macro allows you to disable this feature in case
1732 you need to create files for crappy WAV players with bugs that stop them skipping chunks
1733 which they don't recognise. But DO NOT USE THIS option unless you really have no choice,
1734 because it means that if you write more than 2^32 samples to the file, you'll corrupt it.
1735 */
1736 writeChunkHeader (chunkName ("JUNK"), 28 + (isWaveFmtEx? 0 : 24));
1737 output->writeRepeatedByte (0, 28 /* ds64 */ + (isWaveFmtEx? 0 : 24));
1738 #endif
1739 }
1740 else
1741 {
1742 #if JUCE_WAV_DO_NOT_PAD_HEADER_SIZE
1743 // If you disable padding, then you MUST NOT write more than 2^32 samples to a file.
1744 jassertfalse;
1745 #endif
1746
1747 writeChunkHeader (chunkName ("ds64"), 28); // chunk size for uncompressed data (no table)
1748 output->writeInt64 (riffChunkSize);
1749 output->writeInt64 ((int64) audioDataSize);
1750 output->writeRepeatedByte (0, 12);
1751 }
1752
1753 if (isWaveFmtEx)
1754 {
1755 writeChunkHeader (chunkName ("fmt "), 40);
1756 output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE
1757 }
1758 else
1759 {
1760 writeChunkHeader (chunkName ("fmt "), 16);
1761 output->writeShort (bitsPerSample < 32 ? (short) 1 /*WAVE_FORMAT_PCM*/
1762 : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/);
1763 }
1764
1765 output->writeShort ((short) numChannels);
1766 output->writeInt ((int) sampleRate);
1767 output->writeInt ((int) ((double) bytesPerFrame * sampleRate)); // nAvgBytesPerSec
1768 output->writeShort ((short) bytesPerFrame); // nBlockAlign
1769 output->writeShort ((short) bitsPerSample); // wBitsPerSample
1770
1771 if (isWaveFmtEx)
1772 {
1773 output->writeShort (22); // cbSize (size of the extension)
1774 output->writeShort ((short) bitsPerSample); // wValidBitsPerSample
1775 output->writeInt (channelMask);
1776
1777 const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat;
1778
1779 output->writeInt ((int) subFormat.data1);
1780 output->writeShort ((short) subFormat.data2);
1781 output->writeShort ((short) subFormat.data3);
1782 output->write (subFormat.data4, sizeof (subFormat.data4));
1783 }
1784
1785 writeChunk (bwavChunk, chunkName ("bext"));
1786 writeChunk (ixmlChunk, chunkName ("iXML"));
1787 writeChunk (axmlChunk, chunkName ("axml"));
1788 writeChunk (smplChunk, chunkName ("smpl"));
1789 writeChunk (instChunk, chunkName ("inst"), 7);
1790 writeChunk (cueChunk, chunkName ("cue "));
1791 writeChunk (listChunk, chunkName ("LIST"));
1792 writeChunk (listInfoChunk, chunkName ("LIST"));
1793 writeChunk (acidChunk, chunkName ("acid"));
1794 writeChunk (trckChunk, chunkName ("Trkn"));
1795
1796 writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame));
1797
1798 usesFloatingPointData = (bitsPerSample == 32);
1799 }
1800
1801 static size_t chunkSize (const MemoryBlock& data) noexcept { return data.isEmpty() ? 0 : (8 + data.getSize()); }
1802
1803 void writeChunkHeader (int chunkType, int size) const
1804 {
1805 output->writeInt (chunkType);
1806 output->writeInt (size);
1807 }
1808
1809 void writeChunk (const MemoryBlock& data, int chunkType, int size = 0) const
1810 {
1811 if (! data.isEmpty())
1812 {
1813 writeChunkHeader (chunkType, size != 0 ? size : (int) data.getSize());
1814 *output << data;
1815 }
1816 }
1817
1818 static int getChannelMaskFromChannelLayout (const AudioChannelSet& layout)
1819 {
1820 if (layout.isDiscreteLayout())
1821 return 0;
1822
1823 // Don't add an extended format chunk for mono and stereo. Basically, all wav players
1824 // interpret a wav file with only one or two channels to be mono or stereo anyway.
1825 if (layout == AudioChannelSet::mono() || layout == AudioChannelSet::stereo())
1826 return 0;
1827
1828 auto channels = layout.getChannelTypes();
1829 auto wavChannelMask = 0;
1830
1831 for (auto channel : channels)
1832 {
1833 int wavChannelBit = static_cast<int> (channel) - 1;
1834 jassert (wavChannelBit >= 0 && wavChannelBit <= 31);
1835
1836 wavChannelMask |= (1 << wavChannelBit);
1837 }
1838
1839 return wavChannelMask;
1840 }
1841
1842 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatWriter)
1843};
1844
1845//==============================================================================
1846class MemoryMappedWavReader final : public MemoryMappedAudioFormatReader
1847{
1848public:
1849 MemoryMappedWavReader (const File& wavFile, const WavAudioFormatReader& reader)
1850 : MemoryMappedAudioFormatReader (wavFile, reader, reader.dataChunkStart,
1851 reader.dataLength, reader.bytesPerFrame)
1852 {
1853 }
1854
1855 bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer,
1856 int64 startSampleInFile, int numSamples) override
1857 {
1858 clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
1859 startSampleInFile, numSamples, lengthInSamples);
1860
1861 if (numSamples <= 0)
1862 return true;
1863
1864 if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
1865 {
1866 jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
1867 return false;
1868 }
1869
1870 WavAudioFormatReader::copySampleData (bitsPerSample, usesFloatingPointData,
1871 destSamples, startOffsetInDestBuffer, numDestChannels,
1872 sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
1873 return true;
1874 }
1875
1876 void getSample (int64 sample, float* result) const noexcept override
1877 {
1878 auto num = (int) numChannels;
1879
1880 if (map == nullptr || ! mappedSection.contains (sample))
1881 {
1882 jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
1883
1884 zeromem (result, (size_t) num * sizeof (float));
1885 return;
1886 }
1887
1888 auto dest = &result;
1889 auto source = sampleToPointer (sample);
1890
1891 switch (bitsPerSample)
1892 {
1893 case 8: ReadHelper<AudioData::Float32, AudioData::UInt8, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
1894 case 16: ReadHelper<AudioData::Float32, AudioData::Int16, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
1895 case 24: ReadHelper<AudioData::Float32, AudioData::Int24, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
1896 case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num);
1897 else ReadHelper<AudioData::Float32, AudioData::Int32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num);
1898 break;
1899 default: jassertfalse; break;
1900 }
1901 }
1902
1903 void readMaxLevels (int64 startSampleInFile, int64 numSamples, Range<float>* results, int numChannelsToRead) override
1904 {
1905 numSamples = jmin (numSamples, lengthInSamples - startSampleInFile);
1906
1907 if (map == nullptr || numSamples <= 0 || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
1908 {
1909 jassert (numSamples <= 0); // you must make sure that the window contains all the samples you're going to attempt to read.
1910
1911 for (int i = 0; i < numChannelsToRead; ++i)
1912 results[i] = {};
1913
1914 return;
1915 }
1916
1917 switch (bitsPerSample)
1918 {
1919 case 8: scanMinAndMax<AudioData::UInt8> (startSampleInFile, numSamples, results, numChannelsToRead); break;
1920 case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, results, numChannelsToRead); break;
1921 case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, results, numChannelsToRead); break;
1922 case 32: if (usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, results, numChannelsToRead);
1923 else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, results, numChannelsToRead);
1924 break;
1925 default: jassertfalse; break;
1926 }
1927 }
1928
1929 using AudioFormatReader::readMaxLevels;
1930
1931private:
1932 template <typename SampleType>
1933 void scanMinAndMax (int64 startSampleInFile, int64 numSamples, Range<float>* results, int numChannelsToRead) const noexcept
1934 {
1935 for (int i = 0; i < numChannelsToRead; ++i)
1936 results[i] = scanMinAndMaxInterleaved<SampleType, AudioData::LittleEndian> (i, startSampleInFile, numSamples);
1937 }
1938
1939 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedWavReader)
1940};
1941
1942//==============================================================================
1943WavAudioFormat::WavAudioFormat() : AudioFormat (wavFormatName, ".wav .bwf") {}
1944WavAudioFormat::~WavAudioFormat() {}
1945
1946 Array<int> WavAudioFormat::getPossibleSampleRates()
1947{
1948 return { 8000, 11025, 12000, 16000, 22050, 32000, 44100,
1949 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
1950}
1951
1952 Array<int> WavAudioFormat::getPossibleBitDepths()
1953{
1954 return { 8, 16, 24, 32 };
1955}
1956
1957 bool WavAudioFormat::canDoStereo() { return true; }
1958 bool WavAudioFormat::canDoMono() { return true; }
1959
1960 bool WavAudioFormat::isChannelLayoutSupported (const AudioChannelSet& channelSet)
1961{
1962 auto channelTypes = channelSet.getChannelTypes();
1963
1964 // When
1965 if (channelSet.isDiscreteLayout())
1966 return true;
1967
1968 // WAV supports all channel types from left ... topRearRight
1969 for (auto channel : channelTypes)
1970 if (channel < AudioChannelSet::left || channel > AudioChannelSet::topRearRight)
1971 return false;
1972
1973 return true;
1974}
1975
1976 AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails)
1977{
1978 std::unique_ptr<WavAudioFormatReader> r (new WavAudioFormatReader (sourceStream));
1979
1980 #if JUCE_USE_OGGVORBIS
1981 if (r->isSubformatOggVorbis)
1982 {
1983 r->input = nullptr;
1984 return OggVorbisAudioFormat().createReaderFor (sourceStream, deleteStreamIfOpeningFails);
1985 }
1986 #endif
1987
1988 if (r->sampleRate > 0 && r->numChannels > 0 && r->bytesPerFrame > 0 && r->bitsPerSample <= 32)
1989 return r.release();
1990
1991 if (! deleteStreamIfOpeningFails)
1992 r->input = nullptr;
1993
1994 return nullptr;
1995}
1996
1997 MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (const File& file)
1998{
1999 return createMemoryMappedReader (file.createInputStream().release());
2000}
2001
2002 MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (FileInputStream* fin)
2003{
2004 if (fin != nullptr)
2005 {
2006 WavAudioFormatReader reader (fin);
2007
2008 if (reader.lengthInSamples > 0)
2009 return new MemoryMappedWavReader (fin->getFile(), reader);
2010 }
2011
2012 return nullptr;
2013}
2014
2015 AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate,
2016 unsigned int numChannels, int bitsPerSample,
2017 const StringPairArray& metadataValues, int qualityOptionIndex)
2018{
2019 return createWriterFor (out, sampleRate, WavFileHelpers::canonicalWavChannelSet (static_cast<int> (numChannels)),
2020 bitsPerSample, metadataValues, qualityOptionIndex);
2021}
2022
2023 AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out,
2024 double sampleRate,
2025 const AudioChannelSet& channelLayout,
2026 int bitsPerSample,
2027 const StringPairArray& metadataValues,
2028 int /*qualityOptionIndex*/)
2029{
2030 if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample) && isChannelLayoutSupported (channelLayout))
2031 return new WavAudioFormatWriter (out, sampleRate, channelLayout,
2032 (unsigned int) bitsPerSample, metadataValues);
2033
2034 return nullptr;
2035}
2036
2037namespace WavFileHelpers
2038{
2039 static bool slowCopyWavFileWithNewMetadata (const File& file, const StringPairArray& metadata)
2040 {
2041 TemporaryFile tempFile (file);
2042 WavAudioFormat wav;
2043
2044 std::unique_ptr<AudioFormatReader> reader (wav.createReaderFor (file.createInputStream().release(), true));
2045
2046 if (reader != nullptr)
2047 {
2048 std::unique_ptr<OutputStream> outStream (tempFile.getFile().createOutputStream());
2049
2050 if (outStream != nullptr)
2051 {
2052 std::unique_ptr<AudioFormatWriter> writer (wav.createWriterFor (outStream.get(), reader->sampleRate,
2053 reader->numChannels, (int) reader->bitsPerSample,
2054 metadata, 0));
2055
2056 if (writer != nullptr)
2057 {
2058 outStream.release();
2059
2060 bool ok = writer->writeFromAudioReader (*reader, 0, -1);
2061 writer.reset();
2062 reader.reset();
2063
2064 return ok && tempFile.overwriteTargetFileWithTemporary();
2065 }
2066 }
2067 }
2068
2069 return false;
2070 }
2071}
2072
2073 bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata)
2074{
2075 using namespace WavFileHelpers;
2076
2077 std::unique_ptr<WavAudioFormatReader> reader (static_cast<WavAudioFormatReader*> (createReaderFor (wavFile.createInputStream().release(), true)));
2078
2079 if (reader != nullptr)
2080 {
2081 auto bwavPos = reader->bwavChunkStart;
2082 auto bwavSize = reader->bwavSize;
2083 reader.reset();
2084
2085 if (bwavSize > 0)
2086 {
2087 auto chunk = BWAVChunk::createFrom (toMap (newMetadata));
2088
2089 if (chunk.getSize() <= (size_t) bwavSize)
2090 {
2091 // the new one will fit in the space available, so write it directly..
2092 auto oldSize = wavFile.getSize();
2093
2094 {
2095 FileOutputStream out (wavFile);
2096
2097 if (out.openedOk())
2098 {
2099 out.setPosition (bwavPos);
2100 out << chunk;
2101 out.setPosition (oldSize);
2102 }
2103 }
2104
2105 jassert (wavFile.getSize() == oldSize);
2106 return true;
2107 }
2108 }
2109 }
2110
2111 return slowCopyWavFileWithNewMetadata (wavFile, newMetadata);
2112}
2113
2114
2115//==============================================================================
2116//==============================================================================
2117#if JUCE_UNIT_TESTS
2118
2119struct WaveAudioFormatTests final : public UnitTest
2120{
2121 WaveAudioFormatTests()
2122 : UnitTest ("Wave audio format tests", UnitTestCategories::audio)
2123 {}
2124
2125 void runTest() override
2126 {
2127 beginTest ("Setting up metadata");
2128
2129 auto metadataValues = toMap (WavAudioFormat::createBWAVMetadata ("description",
2130 "originator",
2131 "originatorRef",
2132 Time::getCurrentTime(),
2133 numTestAudioBufferSamples,
2134 "codingHistory"));
2135
2136 for (int i = numElementsInArray (WavFileHelpers::ListInfoChunk::types); --i >= 0;)
2137 metadataValues[WavFileHelpers::ListInfoChunk::types[i]] = WavFileHelpers::ListInfoChunk::types[i];
2138
2139 metadataValues[WavAudioFormat::internationalStandardRecordingCode] = WavAudioFormat::internationalStandardRecordingCode;
2140
2141 if (metadataValues.size() > 0)
2142 metadataValues["MetaDataSource"] = "WAV";
2143
2144 const auto smplMetadata = createDefaultSMPLMetadata();
2145 metadataValues.insert (smplMetadata.cbegin(), smplMetadata.cend());
2146
2147 WavAudioFormat format;
2148 MemoryBlock memoryBlock;
2149
2150 StringPairArray metadataArray;
2151 metadataArray.addUnorderedMap (metadataValues);
2152
2153 {
2154 beginTest ("Metadata can be written and read");
2155
2156 const auto newMetadata = getMetadataAfterReading (format, writeToBlock (format, metadataArray));
2157 expect (newMetadata == metadataArray, "Somehow, the metadata is different!");
2158 }
2159
2160 {
2161 beginTest ("Files containing a riff info source and an empty ISRC associate the source with the riffInfoSource key");
2162 StringPairArray meta;
2163 meta.addMap ({ { WavAudioFormat::riffInfoSource, "customsource" },
2164 { WavAudioFormat::internationalStandardRecordingCode, "" } });
2165 const auto mb = writeToBlock (format, meta);
2166 checkPatternsPresent (mb, { "INFOISRC" });
2167 checkPatternsNotPresent (mb, { "ISRC:", "<ebucore" });
2168 const auto a = getMetadataAfterReading (format, mb);
2169 expect (a[WavAudioFormat::riffInfoSource] == "customsource");
2170 expect (a[WavAudioFormat::internationalStandardRecordingCode] == "");
2171 }
2172
2173 {
2174 beginTest ("Files containing a riff info source and no ISRC associate the source with both keys "
2175 "for backwards compatibility");
2176 StringPairArray meta;
2177 meta.addMap ({ { WavAudioFormat::riffInfoSource, "customsource" } });
2178 const auto mb = writeToBlock (format, meta);
2179 checkPatternsPresent (mb, { "INFOISRC", "ISRC:customsource", "<ebucore" });
2180 const auto a = getMetadataAfterReading (format, mb);
2181 expect (a[WavAudioFormat::riffInfoSource] == "customsource");
2182 expect (a[WavAudioFormat::internationalStandardRecordingCode] == "customsource");
2183 }
2184
2185 {
2186 beginTest ("Files containing an ISRC associate the value with the internationalStandardRecordingCode key "
2187 "and the riffInfoSource key for backwards compatibility");
2188 StringPairArray meta;
2189 meta.addMap ({ { WavAudioFormat::internationalStandardRecordingCode, "AABBBCCDDDDD" } });
2190 const auto mb = writeToBlock (format, meta);
2191 checkPatternsPresent (mb, { "ISRC:AABBBCCDDDDD", "<ebucore" });
2192 checkPatternsNotPresent (mb, { "INFOISRC" });
2193 const auto a = getMetadataAfterReading (format, mb);
2194 expect (a[WavAudioFormat::riffInfoSource] == "AABBBCCDDDDD");
2195 expect (a[WavAudioFormat::internationalStandardRecordingCode] == "AABBBCCDDDDD");
2196 }
2197
2198 {
2199 beginTest ("Files containing an ISRC and a riff info source associate the values with the appropriate keys");
2200 StringPairArray meta;
2201 meta.addMap ({ { WavAudioFormat::riffInfoSource, "source" } });
2202 meta.addMap ({ { WavAudioFormat::internationalStandardRecordingCode, "UUVVVXXYYYYY" } });
2203 const auto mb = writeToBlock (format, meta);
2204 checkPatternsPresent (mb, { "INFOISRC", "ISRC:UUVVVXXYYYYY", "<ebucore" });
2205 const auto a = getMetadataAfterReading (format, mb);
2206 expect (a[WavAudioFormat::riffInfoSource] == "source");
2207 expect (a[WavAudioFormat::internationalStandardRecordingCode] == "UUVVVXXYYYYY");
2208 }
2209
2210 {
2211 beginTest ("Files containing ASWG metadata read and write correctly");
2212 MemoryBlock block;
2213 StringPairArray meta;
2214
2215 for (const auto& key : WavFileHelpers::IXMLChunk::aswgMetadataKeys)
2216 meta.set (key, "Test123&<>");
2217
2218 {
2219 auto writer = rawToUniquePtr (WavAudioFormat().createWriterFor (new MemoryOutputStream (block, false), 48000, 1, 32, meta, 0));
2220 expect (writer != nullptr);
2221 }
2222
2223 expect ([&]
2224 {
2225 auto input = std::make_unique<MemoryInputStream> (block, false);
2226
2227 while (! input->isExhausted())
2228 {
2229 char chunkType[4] {};
2230 auto pos = input->getPosition();
2231
2232 input->read (chunkType, 4);
2233
2234 if (memcmp (chunkType, "iXML", 4) == 0)
2235 {
2236 auto length = (uint32) input->readInt();
2237
2238 MemoryBlock xmlBlock;
2239 input->readIntoMemoryBlock (xmlBlock, (ssize_t) length);
2240
2241 return parseXML (xmlBlock.toString()) != nullptr;
2242 }
2243
2244 input->setPosition (pos + 1);
2245 }
2246
2247 return false;
2248 }());
2249
2250 {
2251 auto reader = rawToUniquePtr (WavAudioFormat().createReaderFor (new MemoryInputStream (block, false), true));
2252 expect (reader != nullptr);
2253
2254 for (const auto& key : meta.getAllKeys())
2255 {
2256 const auto oldValue = meta.getValue (key, "!");
2257 const auto newValue = reader->metadataValues.getValue (key, "");
2258 expectEquals (oldValue, newValue);
2259 }
2260
2261 expect (reader->metadataValues.getValue (WavAudioFormat::aswgVersion, "") == "3.01");
2262 }
2263 }
2264 }
2265
2266private:
2267 MemoryBlock writeToBlock (WavAudioFormat& format, StringPairArray meta)
2268 {
2269 MemoryBlock mb;
2270
2271 {
2272 // The destructor of the writer will modify the block, so make sure that we've
2273 // destroyed the writer before returning the block!
2274 auto writer = rawToUniquePtr (format.createWriterFor (new MemoryOutputStream (mb, false),
2275 44100.0,
2276 numTestAudioBufferChannels,
2277 16,
2278 meta,
2279 0));
2280 expect (writer != nullptr);
2281 AudioBuffer<float> buffer (numTestAudioBufferChannels, numTestAudioBufferSamples);
2282 expect (writer->writeFromAudioSampleBuffer (buffer, 0, numTestAudioBufferSamples));
2283 }
2284
2285 return mb;
2286 }
2287
2288 StringPairArray getMetadataAfterReading (WavAudioFormat& format, const MemoryBlock& mb)
2289 {
2290 auto reader = rawToUniquePtr (format.createReaderFor (new MemoryInputStream (mb, false), true));
2291 expect (reader != nullptr);
2292 return reader->metadataValues;
2293 }
2294
2295 template <typename Fn>
2296 void checkPatterns (const MemoryBlock& mb, const std::vector<std::string>& patterns, Fn&& fn)
2297 {
2298 for (const auto& pattern : patterns)
2299 {
2300 const auto begin = static_cast<const char*> (mb.getData());
2301 const auto end = begin + mb.getSize();
2302 expect (fn (std::search (begin, end, pattern.begin(), pattern.end()), end));
2303 }
2304 }
2305
2306 void checkPatternsPresent (const MemoryBlock& mb, const std::vector<std::string>& patterns)
2307 {
2308 checkPatterns (mb, patterns, std::not_equal_to<>{});
2309 }
2310
2311 void checkPatternsNotPresent (const MemoryBlock& mb, const std::vector<std::string>& patterns)
2312 {
2313 checkPatterns (mb, patterns, std::equal_to<>{});
2314 }
2315
2316 enum
2317 {
2318 numTestAudioBufferChannels = 2,
2319 numTestAudioBufferSamples = 256
2320 };
2321
2322 static StringMap createDefaultSMPLMetadata()
2323 {
2324 StringMap m;
2325
2326 m["Manufacturer"] = "0";
2327 m["Product"] = "0";
2328 m["SamplePeriod"] = "0";
2329 m["MidiUnityNote"] = "60";
2330 m["MidiPitchFraction"] = "0";
2331 m["SmpteFormat"] = "0";
2332 m["SmpteOffset"] = "0";
2333 m["NumSampleLoops"] = "0";
2334 m["SamplerData"] = "0";
2335
2336 return m;
2337 }
2338
2339 JUCE_DECLARE_NON_COPYABLE (WaveAudioFormatTests)
2340};
2341
2342static const WaveAudioFormatTests waveAudioFormatTests;
2343
2344#endif
2345
2346} // namespace juce
juce::Array
Definition juce_Array.h:56
juce::AudioChannelSet
Definition juce_AudioChannelSet.h:47
juce::AudioChannelSet::quadraphonic
static AudioChannelSet JUCE_CALLTYPE quadraphonic()
Definition juce_AudioChannelSet.cpp:553
juce::AudioChannelSet::create5point0
static AudioChannelSet JUCE_CALLTYPE create5point0()
Definition juce_AudioChannelSet.cpp:543
juce::AudioChannelSet::size
int size() const noexcept
Definition juce_AudioChannelSet.cpp:483
juce::AudioChannelSet::isDiscreteLayout
bool isDiscreteLayout() const noexcept
Definition juce_AudioChannelSet.cpp:474
juce::AudioChannelSet::mono
static AudioChannelSet JUCE_CALLTYPE mono()
Definition juce_AudioChannelSet.cpp:538
juce::AudioChannelSet::stereo
static AudioChannelSet JUCE_CALLTYPE stereo()
Definition juce_AudioChannelSet.cpp:539
juce::AudioChannelSet::ChannelType
ChannelType
Definition juce_AudioChannelSet.h:353
juce::AudioChannelSet::topRearRight
@ topRearRight
Definition juce_AudioChannelSet.h:377
juce::AudioChannelSet::discreteChannel0
@ discreteChannel0
Definition juce_AudioChannelSet.h:493
juce::AudioChannelSet::create5point1
static AudioChannelSet JUCE_CALLTYPE create5point1()
Definition juce_AudioChannelSet.cpp:544
juce::AudioChannelSet::create7point0SDDS
static AudioChannelSet JUCE_CALLTYPE create7point0SDDS()
Definition juce_AudioChannelSet.cpp:550
juce::AudioChannelSet::create7point1SDDS
static AudioChannelSet JUCE_CALLTYPE create7point1SDDS()
Definition juce_AudioChannelSet.cpp:552
juce::AudioChannelSet::canonicalChannelSet
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
Definition juce_AudioChannelSet.cpp:616
juce::AudioChannelSet::discreteChannels
static AudioChannelSet JUCE_CALLTYPE discreteChannels(int numChannels)
Definition juce_AudioChannelSet.cpp:609
juce::AudioChannelSet::createLCR
static AudioChannelSet JUCE_CALLTYPE createLCR()
Definition juce_AudioChannelSet.cpp:540
juce::AudioChannelSet::getChannelTypes
Array< ChannelType > getChannelTypes() const
Definition juce_AudioChannelSet.cpp:513
juce::AudioFormatReader
Definition juce_AudioFormatReader.h:44
juce::AudioFormatReader::input
InputStream * input
Definition juce_AudioFormatReader.h:248
juce::AudioFormatReader::usesFloatingPointData
bool usesFloatingPointData
Definition juce_AudioFormatReader.h:237
juce::AudioFormatReader::metadataValues
StringPairArray metadataValues
Definition juce_AudioFormatReader.h:245
juce::AudioFormatReader::clearSamplesBeyondAvailableLength
static void clearSamplesBeyondAvailableLength(int *const *destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int &numSamples, int64 fileLengthInSamples)
Definition juce_AudioFormatReader.h:309
juce::AudioFormatReader::lengthInSamples
int64 lengthInSamples
Definition juce_AudioFormatReader.h:231
juce::AudioFormatReader::sampleRate
double sampleRate
Definition juce_AudioFormatReader.h:225
juce::AudioFormatReader::AudioFormatReader
AudioFormatReader(InputStream *sourceStream, const String &formatName)
Definition juce_AudioFormatReader.cpp:29
juce::AudioFormatReader::bitsPerSample
unsigned int bitsPerSample
Definition juce_AudioFormatReader.h:228
juce::AudioFormatReader::readMaxLevels
virtual void readMaxLevels(int64 startSample, int64 numSamples, Range< float > *results, int numChannelsToRead)
Definition juce_AudioFormatReader.cpp:209
juce::AudioFormatReader::numChannels
unsigned int numChannels
Definition juce_AudioFormatReader.h:234
juce::AudioFormatWriter
Definition juce_AudioFormatWriter.h:44
juce::AudioFormatWriter::numChannels
unsigned int numChannels
Definition juce_AudioFormatWriter.h:251
juce::AudioFormatWriter::sampleRate
double sampleRate
Definition juce_AudioFormatWriter.h:248
juce::AudioFormatWriter::usesFloatingPointData
bool usesFloatingPointData
Definition juce_AudioFormatWriter.h:257
juce::AudioFormatWriter::bitsPerSample
unsigned int bitsPerSample
Definition juce_AudioFormatWriter.h:254
juce::AudioFormatWriter::AudioFormatWriter
AudioFormatWriter(OutputStream *destStream, const String &formatName, double sampleRate, unsigned int numberOfChannels, unsigned int bitsPerSample)
Definition juce_AudioFormatWriter.cpp:29
juce::AudioFormatWriter::channelLayout
AudioChannelSet channelLayout
Definition juce_AudioFormatWriter.h:260
juce::AudioFormatWriter::output
OutputStream * output
Definition juce_AudioFormatWriter.h:263
juce::AudioFormat
Definition juce_AudioFormat.h:39
juce::ByteOrder::littleEndianInt
static constexpr uint32 littleEndianInt(const void *bytes) noexcept
Definition juce_ByteOrder.h:199
juce::ByteOrder::swap
static constexpr uint16 swap(uint16 value) noexcept
Definition juce_ByteOrder.h:143
juce::ByteOrder::swapIfBigEndian
static Type swapIfBigEndian(Type value) noexcept
Definition juce_ByteOrder.h:63
juce::CharacterFunctions::toUpperCase
static juce_wchar toUpperCase(juce_wchar character) noexcept
Definition juce_CharacterFunctions.cpp:28
juce::FileInputStream
Definition juce_FileInputStream.h:35
juce::FileInputStream::getFile
const File & getFile() const noexcept
Definition juce_FileInputStream.h:51
juce::FileOutputStream
Definition juce_FileOutputStream.h:35
juce::FileOutputStream::setPosition
bool setPosition(int64) override
Definition juce_FileOutputStream.cpp:46
juce::FileOutputStream::openedOk
bool openedOk() const noexcept
Definition juce_FileOutputStream.h:91
juce::File
Definition juce_File.h:45
juce::File::getSize
int64 getSize() const
juce::File::createInputStream
std::unique_ptr< FileInputStream > createInputStream() const
Definition juce_File.cpp:732
juce::InputStream
Definition juce_InputStream.h:37
juce::InputStream::getPosition
virtual int64 getPosition()=0
juce::InputStream::readInt64
virtual int64 readInt64()
Definition juce_InputStream.cpp:133
juce::InputStream::setPosition
virtual bool setPosition(int64 newPosition)=0
juce::InputStream::isExhausted
virtual bool isExhausted()=0
juce::InputStream::readShort
virtual short readShort()
Definition juce_InputStream.cpp:68
juce::InputStream::skipNextBytes
virtual void skipNextBytes(int64 numBytesToSkip)
Definition juce_InputStream.cpp:237
juce::InputStream::readIntoMemoryBlock
virtual size_t readIntoMemoryBlock(MemoryBlock &destBlock, ssize_t maxNumBytesToRead=-1)
Definition juce_InputStream.cpp:223
juce::InputStream::read
virtual int read(void *destBuffer, int maxBytesToRead)=0
juce::InputStream::readInt
virtual int readInt()
Definition juce_InputStream.cpp:88
juce::MemoryBlock
Definition juce_MemoryBlock.h:33
juce::MemoryBlock::getData
void * getData() noexcept
Definition juce_MemoryBlock.h:91
juce::MemoryBlock::getSize
size_t getSize() const noexcept
Definition juce_MemoryBlock.h:127
juce::MemoryBlock::ensureSize
void ensureSize(size_t minimumSize, bool initialiseNewSpaceToZero=false)
Definition juce_MemoryBlock.cpp:148
juce::MemoryMappedAudioFormatReader
Definition juce_MemoryMappedAudioFormatReader.h:47
juce::MemoryMappedAudioFormatReader::MemoryMappedAudioFormatReader
MemoryMappedAudioFormatReader(const File &file, const AudioFormatReader &details, int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame)
Definition juce_AudioFormatReader.cpp:404
juce::MemoryMappedAudioFormatReader::sampleToPointer
const void * sampleToPointer(int64 sample) const noexcept
Definition juce_MemoryMappedAudioFormatReader.h:98
juce::MemoryMappedAudioFormatReader::scanMinAndMaxInterleaved
Range< float > scanMinAndMaxInterleaved(int channel, int64 startSampleInFile, int64 numSamples) const noexcept
Definition juce_MemoryMappedAudioFormatReader.h:102
juce::MemoryOutputStream
Definition juce_MemoryOutputStream.h:36
juce::MemoryOutputStream::getDataSize
size_t getDataSize() const noexcept
Definition juce_MemoryOutputStream.h:80
juce::MemoryOutputStream::write
bool write(const void *, size_t) override
Definition juce_MemoryOutputStream.cpp:102
juce::MemoryOutputStream::getMemoryBlock
MemoryBlock getMemoryBlock() const
Definition juce_MemoryOutputStream.cpp:143
juce::OutputStream
Definition juce_OutputStream.h:38
juce::OutputStream::write
virtual bool write(const void *dataToWrite, size_t numberOfBytes)=0
juce::OutputStream::writeRepeatedByte
virtual bool writeRepeatedByte(uint8 byte, size_t numTimesToRepeat)
Definition juce_OutputStream.cpp:88
juce::OutputStream::getPosition
virtual int64 getPosition()=0
juce::OutputStream::writeByte
virtual bool writeByte(char byte)
Definition juce_OutputStream.cpp:83
juce::OutputStream::writeShort
virtual bool writeShort(short value)
Definition juce_OutputStream.cpp:97
juce::OutputStream::writeInt64
virtual bool writeInt64(int64 value)
Definition juce_OutputStream.cpp:143
juce::OutputStream::setPosition
virtual bool setPosition(int64 newPosition)=0
juce::OutputStream::writeInt
virtual bool writeInt(int value)
Definition juce_OutputStream.cpp:109
juce::Range::contains
constexpr bool contains(const ValueType position) const noexcept
Definition juce_Range.h:214
juce::StringPairArray
Definition juce_StringPairArray.h:35
juce::StringPairArray::set
void set(const String &key, const String &value)
Definition juce_StringPairArray.cpp:101
juce::StringPairArray::addUnorderedMap
void addUnorderedMap(const std::unordered_map< String, String > &mapToAdd)
Definition juce_StringPairArray.cpp:205
juce::String
Definition juce_String.h:53
juce::String::createStringFromData
static String createStringFromData(const void *data, int size)
Definition juce_String.cpp:1968
juce::String::fromUTF8
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Definition juce_String.cpp:2125
juce::TemporaryFile
Definition juce_TemporaryFile.h:65
juce::Time
Definition juce_Time.h:37
juce::Time::formatted
String formatted(const String &format) const
Definition juce_Time.cpp:341
juce::UnitTest
Definition juce_UnitTest.h:70
juce::WavAudioFormat
Definition juce_WavAudioFormat.h:38
juce::WavAudioFormat::aswgRecStudio
static const char *const aswgRecStudio
Definition juce_WavAudioFormat.h:201
juce::WavAudioFormat::aswgBillingCode
static const char *const aswgBillingCode
Definition juce_WavAudioFormat.h:263
juce::WavAudioFormat::aswgMicDistance
static const char *const aswgMicDistance
Definition juce_WavAudioFormat.h:197
juce::WavAudioFormat::riffInfoCopyright
static const char *const riffInfoCopyright
Definition juce_WavAudioFormat.h:104
juce::WavAudioFormat::acidRootSet
static const char *const acidRootSet
Definition juce_WavAudioFormat.h:83
juce::WavAudioFormat::aswgUserData
static const char *const aswgUserData
Definition juce_WavAudioFormat.h:207
juce::WavAudioFormat::riffInfoDirectory
static const char *const riffInfoDirectory
Definition juce_WavAudioFormat.h:112
juce::WavAudioFormat::bwavCodingHistory
static const char *const bwavCodingHistory
Definition juce_WavAudioFormat.h:55
juce::WavAudioFormat::bwavTimeReference
static const char *const bwavTimeReference
Definition juce_WavAudioFormat.h:65
juce::WavAudioFormat::acidDiskBased
static const char *const acidDiskBased
Definition juce_WavAudioFormat.h:85
juce::WavAudioFormat::aswgEfforts
static const char *const aswgEfforts
Definition juce_WavAudioFormat.h:221
juce::WavAudioFormat::aswgMusicSup
static const char *const aswgMusicSup
Definition juce_WavAudioFormat.h:245
juce::WavAudioFormat::aswgTimeSig
static const char *const aswgTimeSig
Definition juce_WavAudioFormat.h:261
juce::WavAudioFormat::acidOneShot
static const char *const acidOneShot
Definition juce_WavAudioFormat.h:82
juce::WavAudioFormat::aswgIsDesigned
static const char *const aswgIsDesigned
Definition juce_WavAudioFormat.h:199
juce::WavAudioFormat::riffInfoOrganisation
static const char *const riffInfoOrganisation
Definition juce_WavAudioFormat.h:139
juce::WavAudioFormat::aswgCharacterName
static const char *const aswgCharacterName
Definition juce_WavAudioFormat.h:226
juce::WavAudioFormat::aswgIsUnion
static const char *const aswgIsUnion
Definition juce_WavAudioFormat.h:236
juce::WavAudioFormat::aswgLibrary
static const char *const aswgLibrary
Definition juce_WavAudioFormat.h:210
juce::WavAudioFormat::riffInfoFirstLanguage
static const char *const riffInfoFirstLanguage
Definition juce_WavAudioFormat.h:121
juce::WavAudioFormat::aswgAmbisonicChnOrder
static const char *const aswgAmbisonicChnOrder
Definition juce_WavAudioFormat.h:193
juce::WavAudioFormat::aswgRecordingLoc
static const char *const aswgRecordingLoc
Definition juce_WavAudioFormat.h:198
juce::WavAudioFormat::aswgCreatorId
static const char *const aswgCreatorId
Definition juce_WavAudioFormat.h:211
juce::WavAudioFormat::riffInfoEncodedBy
static const char *const riffInfoEncodedBy
Definition juce_WavAudioFormat.h:117
juce::WavAudioFormat::riffInfoCommissioned
static const char *const riffInfoCommissioned
Definition juce_WavAudioFormat.h:103
juce::WavAudioFormat::riffInfoMusicBy
static const char *const riffInfoMusicBy
Definition juce_WavAudioFormat.h:136
juce::WavAudioFormat::riffInfoSharpness
static const char *const riffInfoSharpness
Definition juce_WavAudioFormat.h:152
juce::WavAudioFormat::aswgProducer
static const char *const aswgProducer
Definition juce_WavAudioFormat.h:244
juce::WavAudioFormat::aswgFxUsed
static const char *const aswgFxUsed
Definition juce_WavAudioFormat.h:234
juce::WavAudioFormat::riffInfoStatistics
static const char *const riffInfoStatistics
Definition juce_WavAudioFormat.h:161
juce::WavAudioFormat::riffInfoNinthLanguage
static const char *const riffInfoNinthLanguage
Definition juce_WavAudioFormat.h:137
juce::WavAudioFormat::riffInfoDefaultAudioStream
static const char *const riffInfoDefaultAudioStream
Definition juce_WavAudioFormat.h:110
juce::WavAudioFormat::riffInfoGenre
static const char *const riffInfoGenre
Definition juce_WavAudioFormat.h:123
juce::WavAudioFormat::canDoMono
bool canDoMono() override
Definition juce_WavAudioFormat.cpp:1958
juce::WavAudioFormat::riffInfoMoreInfoBannerImage
static const char *const riffInfoMoreInfoBannerImage
Definition juce_WavAudioFormat.h:132
juce::WavAudioFormat::aswgProjection
static const char *const aswgProjection
Definition juce_WavAudioFormat.h:223
juce::WavAudioFormat::riffInfoVegasVersionMajor
static const char *const riffInfoVegasVersionMajor
Definition juce_WavAudioFormat.h:171
juce::WavAudioFormat::aswgContentType
static const char *const aswgContentType
Definition juce_WavAudioFormat.h:181
juce::WavAudioFormat::aswgOriginator
static const char *const aswgOriginator
Definition juce_WavAudioFormat.h:183
juce::WavAudioFormat::riffInfoLocation
static const char *const riffInfoLocation
Definition juce_WavAudioFormat.h:128
juce::WavAudioFormat::aswgIsOst
static const char *const aswgIsOst
Definition juce_WavAudioFormat.h:254
juce::WavAudioFormat::createReaderFor
AudioFormatReader * createReaderFor(InputStream *sourceStream, bool deleteStreamIfOpeningFails) override
Definition juce_WavAudioFormat.cpp:1976
juce::WavAudioFormat::aswgRmsPower
static const char *const aswgRmsPower
Definition juce_WavAudioFormat.h:213
juce::WavAudioFormat::riffInfoRate
static const char *const riffInfoRate
Definition juce_WavAudioFormat.h:145
juce::WavAudioFormat::riffInfoCostumeDesigner
static const char *const riffInfoCostumeDesigner
Definition juce_WavAudioFormat.h:105
juce::WavAudioFormat::riffInfoVersion
static const char *const riffInfoVersion
Definition juce_WavAudioFormat.h:173
juce::WavAudioFormat::aswgActorGender
static const char *const aswgActorGender
Definition juce_WavAudioFormat.h:231
juce::WavAudioFormat::riffInfoLightness
static const char *const riffInfoLightness
Definition juce_WavAudioFormat.h:127
juce::WavAudioFormat::riffInfoProductionStudio
static const char *const riffInfoProductionStudio
Definition juce_WavAudioFormat.h:144
juce::WavAudioFormat::aswgIsLoop
static const char *const aswgIsLoop
Definition juce_WavAudioFormat.h:250
juce::WavAudioFormat::aswgState
static const char *const aswgState
Definition juce_WavAudioFormat.h:187
juce::WavAudioFormat::aswgSongTitle
static const char *const aswgSongTitle
Definition juce_WavAudioFormat.h:241
juce::WavAudioFormat::riffInfoProducedBy
static const char *const riffInfoProducedBy
Definition juce_WavAudioFormat.h:141
juce::WavAudioFormat::aswgSpecDensity
static const char *const aswgSpecDensity
Definition juce_WavAudioFormat.h:217
juce::WavAudioFormat::riffInfoEighthLanguage
static const char *const riffInfoEighthLanguage
Definition juce_WavAudioFormat.h:116
juce::WavAudioFormat::riffInfoCropped
static const char *const riffInfoCropped
Definition juce_WavAudioFormat.h:107
juce::WavAudioFormat::aswgCharacterAge
static const char *const aswgCharacterAge
Definition juce_WavAudioFormat.h:228
juce::WavAudioFormat::canDoStereo
bool canDoStereo() override
Definition juce_WavAudioFormat.cpp:1957
juce::WavAudioFormat::riffInfoRating
static const char *const riffInfoRating
Definition juce_WavAudioFormat.h:147
juce::WavAudioFormat::aswgMicType
static const char *const aswgMicType
Definition juce_WavAudioFormat.h:195
juce::WavAudioFormat::aswgIsDiegetic
static const char *const aswgIsDiegetic
Definition juce_WavAudioFormat.h:257
juce::WavAudioFormat::riffInfoURL
static const char *const riffInfoURL
Definition juce_WavAudioFormat.h:170
juce::WavAudioFormat::aswgVersion
static const char *const aswgVersion
Definition juce_WavAudioFormat.h:264
juce::WavAudioFormat::ISRC
static const char *const ISRC
Definition juce_WavAudioFormat.h:269
juce::WavAudioFormat::riffInfoMoreInfoBannerURL
static const char *const riffInfoMoreInfoBannerURL
Definition juce_WavAudioFormat.h:133
juce::WavAudioFormat::riffInfoStartTimecode
static const char *const riffInfoStartTimecode
Definition juce_WavAudioFormat.h:160
juce::WavAudioFormat::aswgLoudnessRange
static const char *const aswgLoudnessRange
Definition juce_WavAudioFormat.h:215
juce::WavAudioFormat::bwavOriginatorRef
static const char *const bwavOriginatorRef
Definition juce_WavAudioFormat.h:52
juce::WavAudioFormat::aswgChannelConfig
static const char *const aswgChannelConfig
Definition juce_WavAudioFormat.h:191
juce::WavAudioFormat::riffInfoTitle
static const char *const riffInfoTitle
Definition juce_WavAudioFormat.h:167
juce::WavAudioFormat::aswgIsFinal
static const char *const aswgIsFinal
Definition juce_WavAudioFormat.h:252
juce::WavAudioFormat::riffInfoArtist
static const char *const riffInfoArtist
Definition juce_WavAudioFormat.h:97
juce::WavAudioFormat::aswgMusicPublisher
static const char *const aswgMusicPublisher
Definition juce_WavAudioFormat.h:247
juce::WavAudioFormat::riffInfoSixthLanguage
static const char *const riffInfoSixthLanguage
Definition juce_WavAudioFormat.h:153
juce::WavAudioFormat::riffInfoSecondaryGenre
static const char *const riffInfoSecondaryGenre
Definition juce_WavAudioFormat.h:149
juce::WavAudioFormat::riffInfoFifthLanguage
static const char *const riffInfoFifthLanguage
Definition juce_WavAudioFormat.h:120
juce::WavAudioFormat::riffInfoDotsPerInch
static const char *const riffInfoDotsPerInch
Definition juce_WavAudioFormat.h:114
juce::WavAudioFormat::riffInfoDistributedBy
static const char *const riffInfoDistributedBy
Definition juce_WavAudioFormat.h:113
juce::WavAudioFormat::riffInfoStarring_ISTR
static const char *const riffInfoStarring_ISTR
Definition juce_WavAudioFormat.h:158
juce::WavAudioFormat::aswgAccent
static const char *const aswgAccent
Definition juce_WavAudioFormat.h:237
juce::WavAudioFormat::riffInfoProductName
static const char *const riffInfoProductName
Definition juce_WavAudioFormat.h:142
juce::WavAudioFormat::riffInfoKeywords
static const char *const riffInfoKeywords
Definition juce_WavAudioFormat.h:124
juce::WavAudioFormat::aswgInstrument
static const char *const aswgInstrument
Definition juce_WavAudioFormat.h:246
juce::WavAudioFormat::aswgSession
static const char *const aswgSession
Definition juce_WavAudioFormat.h:186
juce::WavAudioFormat::riffInfoRippedBy
static const char *const riffInfoRippedBy
Definition juce_WavAudioFormat.h:148
juce::WavAudioFormat::riffInfoLanguage
static const char *const riffInfoLanguage
Definition juce_WavAudioFormat.h:125
juce::WavAudioFormat::riffInfoDateTimeOriginal
static const char *const riffInfoDateTimeOriginal
Definition juce_WavAudioFormat.h:109
juce::WavAudioFormat::acidizerFlag
static const char *const acidizerFlag
Definition juce_WavAudioFormat.h:86
juce::WavAudioFormat::riffInfoBaseURL
static const char *const riffInfoBaseURL
Definition juce_WavAudioFormat.h:98
juce::WavAudioFormat::createMemoryMappedReader
MemoryMappedAudioFormatReader * createMemoryMappedReader(const File &) override
Definition juce_WavAudioFormat.cpp:1997
juce::WavAudioFormat::isChannelLayoutSupported
bool isChannelLayoutSupported(const AudioChannelSet &channelSet) override
Definition juce_WavAudioFormat.cpp:1960
juce::WavAudioFormat::aswgArtist
static const char *const aswgArtist
Definition juce_WavAudioFormat.h:240
juce::WavAudioFormat::riffInfoProductionDesigner
static const char *const riffInfoProductionDesigner
Definition juce_WavAudioFormat.h:143
juce::WavAudioFormat::acidDenominator
static const char *const acidDenominator
Definition juce_WavAudioFormat.h:89
juce::WavAudioFormat::aswgCharacterRole
static const char *const aswgCharacterRole
Definition juce_WavAudioFormat.h:229
juce::WavAudioFormat::aswgDirection
static const char *const aswgDirection
Definition juce_WavAudioFormat.h:233
juce::WavAudioFormat::aswgIsSource
static const char *const aswgIsSource
Definition juce_WavAudioFormat.h:249
juce::WavAudioFormat::aswgFxChainName
static const char *const aswgFxChainName
Definition juce_WavAudioFormat.h:190
juce::WavAudioFormat::aswgFxName
static const char *const aswgFxName
Definition juce_WavAudioFormat.h:209
juce::WavAudioFormat::aswgOriginatorStudio
static const char *const aswgOriginatorStudio
Definition juce_WavAudioFormat.h:184
juce::WavAudioFormat::riffInfoVegasVersionMinor
static const char *const riffInfoVegasVersionMinor
Definition juce_WavAudioFormat.h:172
juce::WavAudioFormat::riffInfoLength
static const char *const riffInfoLength
Definition juce_WavAudioFormat.h:126
juce::WavAudioFormat::replaceMetadataInFile
bool replaceMetadataInFile(const File &wavFile, const StringPairArray &newMetadata)
Definition juce_WavAudioFormat.cpp:2073
juce::WavAudioFormat::aswgUsageRights
static const char *const aswgUsageRights
Definition juce_WavAudioFormat.h:235
juce::WavAudioFormat::~WavAudioFormat
~WavAudioFormat() override
Definition juce_WavAudioFormat.cpp:1944
juce::WavAudioFormat::riffInfoTechnician
static const char *const riffInfoTechnician
Definition juce_WavAudioFormat.h:164
juce::WavAudioFormat::riffInfoSoftware
static const char *const riffInfoSoftware
Definition juce_WavAudioFormat.h:154
juce::WavAudioFormat::riffInfoStarring_STAR
static const char *const riffInfoStarring_STAR
Definition juce_WavAudioFormat.h:159
juce::WavAudioFormat::riffInfoDateCreated
static const char *const riffInfoDateCreated
Definition juce_WavAudioFormat.h:108
juce::WavAudioFormat::riffInfoSeventhLanguage
static const char *const riffInfoSeventhLanguage
Definition juce_WavAudioFormat.h:151
juce::WavAudioFormat::aswgActorName
static const char *const aswgActorName
Definition juce_WavAudioFormat.h:230
juce::WavAudioFormat::aswgAmbisonicFormat
static const char *const aswgAmbisonicFormat
Definition juce_WavAudioFormat.h:192
juce::WavAudioFormat::acidBeats
static const char *const acidBeats
Definition juce_WavAudioFormat.h:88
juce::WavAudioFormat::aswgMusicVersion
static const char *const aswgMusicVersion
Definition juce_WavAudioFormat.h:258
juce::WavAudioFormat::createWriterFor
AudioFormatWriter * createWriterFor(OutputStream *streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray &metadataValues, int qualityOptionIndex) override
Definition juce_WavAudioFormat.cpp:2015
juce::WavAudioFormat::aswgMixer
static const char *const aswgMixer
Definition juce_WavAudioFormat.h:189
juce::WavAudioFormat::riffInfoLogoIconURL
static const char *const riffInfoLogoIconURL
Definition juce_WavAudioFormat.h:129
juce::WavAudioFormat::tracktionLoopInfo
static const char *const tracktionLoopInfo
Definition juce_WavAudioFormat.h:275
juce::WavAudioFormat::acidNumerator
static const char *const acidNumerator
Definition juce_WavAudioFormat.h:90
juce::WavAudioFormat::bwavOriginationDate
static const char *const bwavOriginationDate
Definition juce_WavAudioFormat.h:53
juce::WavAudioFormat::internationalStandardRecordingCode
static const char *const internationalStandardRecordingCode
Definition juce_WavAudioFormat.h:272
juce::WavAudioFormat::aswgSourceId
static const char *const aswgSourceId
Definition juce_WavAudioFormat.h:212
juce::WavAudioFormat::riffInfoComments
static const char *const riffInfoComments
Definition juce_WavAudioFormat.h:102
juce::WavAudioFormat::riffInfoNumberOfParts
static const char *const riffInfoNumberOfParts
Definition juce_WavAudioFormat.h:138
juce::WavAudioFormat::aswgVendorCategory
static const char *const aswgVendorCategory
Definition juce_WavAudioFormat.h:208
juce::WavAudioFormat::bwavDescription
static const char *const bwavDescription
Definition juce_WavAudioFormat.h:50
juce::WavAudioFormat::riffInfoSoundSchemeTitle
static const char *const riffInfoSoundSchemeTitle
Definition juce_WavAudioFormat.h:155
juce::WavAudioFormat::aswgPapr
static const char *const aswgPapr
Definition juce_WavAudioFormat.h:219
juce::WavAudioFormat::getPossibleSampleRates
Array< int > getPossibleSampleRates() override
Definition juce_WavAudioFormat.cpp:1946
juce::WavAudioFormat::aswgEditor
static const char *const aswgEditor
Definition juce_WavAudioFormat.h:188
juce::WavAudioFormat::aswgComposor
static const char *const aswgComposor
Definition juce_WavAudioFormat.h:239
juce::WavAudioFormat::aswgSubGenre
static const char *const aswgSubGenre
Definition juce_WavAudioFormat.h:243
juce::WavAudioFormat::aswgEffortType
static const char *const aswgEffortType
Definition juce_WavAudioFormat.h:222
juce::WavAudioFormat::riffInfoWatermarkURL
static const char *const riffInfoWatermarkURL
Definition juce_WavAudioFormat.h:174
juce::WavAudioFormat::aswgLoudness
static const char *const aswgLoudness
Definition juce_WavAudioFormat.h:214
juce::WavAudioFormat::riffInfoTrackNo
static const char *const riffInfoTrackNo
Definition juce_WavAudioFormat.h:168
juce::WavAudioFormat::riffInfoMedium
static const char *const riffInfoMedium
Definition juce_WavAudioFormat.h:131
juce::WavAudioFormat::acidStretch
static const char *const acidStretch
Definition juce_WavAudioFormat.h:84
juce::WavAudioFormat::getPossibleBitDepths
Array< int > getPossibleBitDepths() override
Definition juce_WavAudioFormat.cpp:1952
juce::WavAudioFormat::aswgRightsOwner
static const char *const aswgRightsOwner
Definition juce_WavAudioFormat.h:248
juce::WavAudioFormat::aswgImpulseLocation
static const char *const aswgImpulseLocation
Definition juce_WavAudioFormat.h:202
juce::WavAudioFormat::riffInfoThirdLanguage
static const char *const riffInfoThirdLanguage
Definition juce_WavAudioFormat.h:165
juce::WavAudioFormat::bwavOriginationTime
static const char *const bwavOriginationTime
Definition juce_WavAudioFormat.h:54
juce::WavAudioFormat::aswgLanguage
static const char *const aswgLanguage
Definition juce_WavAudioFormat.h:224
juce::WavAudioFormat::riffInfoArchivalLocation
static const char *const riffInfoArchivalLocation
Definition juce_WavAudioFormat.h:96
juce::WavAudioFormat::aswgTimingRestriction
static const char *const aswgTimingRestriction
Definition juce_WavAudioFormat.h:225
juce::WavAudioFormat::aswgIsrcId
static const char *const aswgIsrcId
Definition juce_WavAudioFormat.h:259
juce::WavAudioFormat::aswgIsLicensed
static const char *const aswgIsLicensed
Definition juce_WavAudioFormat.h:256
juce::WavAudioFormat::aswgCategory
static const char *const aswgCategory
Definition juce_WavAudioFormat.h:203
juce::WavAudioFormat::aswgZeroCrossRate
static const char *const aswgZeroCrossRate
Definition juce_WavAudioFormat.h:218
juce::WavAudioFormat::aswgGenre
static const char *const aswgGenre
Definition juce_WavAudioFormat.h:242
juce::WavAudioFormat::aswgMaxPeak
static const char *const aswgMaxPeak
Definition juce_WavAudioFormat.h:216
juce::WavAudioFormat::riffInfoMoreInfoText
static const char *const riffInfoMoreInfoText
Definition juce_WavAudioFormat.h:134
juce::WavAudioFormat::aswgCharacterGender
static const char *const aswgCharacterGender
Definition juce_WavAudioFormat.h:227
juce::WavAudioFormat::riffInfoCinematographer
static const char *const riffInfoCinematographer
Definition juce_WavAudioFormat.h:99
juce::WavAudioFormat::riffInfoFourthLanguage
static const char *const riffInfoFourthLanguage
Definition juce_WavAudioFormat.h:122
juce::WavAudioFormat::riffInfoSubject
static const char *const riffInfoSubject
Definition juce_WavAudioFormat.h:162
juce::WavAudioFormat::aswgEmotion
static const char *const aswgEmotion
Definition juce_WavAudioFormat.h:238
juce::WavAudioFormat::aswgInKey
static const char *const aswgInKey
Definition juce_WavAudioFormat.h:262
juce::WavAudioFormat::aswgMicConfig
static const char *const aswgMicConfig
Definition juce_WavAudioFormat.h:196
juce::WavAudioFormat::riffInfoRated
static const char *const riffInfoRated
Definition juce_WavAudioFormat.h:146
juce::WavAudioFormat::riffInfoDimension
static const char *const riffInfoDimension
Definition juce_WavAudioFormat.h:111
juce::WavAudioFormat::aswgProject
static const char *const aswgProject
Definition juce_WavAudioFormat.h:182
juce::WavAudioFormat::aswgIntensity
static const char *const aswgIntensity
Definition juce_WavAudioFormat.h:251
juce::WavAudioFormat::riffInfoEditedBy
static const char *const riffInfoEditedBy
Definition juce_WavAudioFormat.h:115
juce::WavAudioFormat::riffInfoYear
static const char *const riffInfoYear
Definition juce_WavAudioFormat.h:176
juce::WavAudioFormat::riffInfoComment2
static const char *const riffInfoComment2
Definition juce_WavAudioFormat.h:101
juce::WavAudioFormat::createBWAVMetadata
static StringPairArray createBWAVMetadata(const String &description, const String &originator, const String &originatorRef, Time dateAndTime, int64 timeReferenceSamples, const String &codingHistory)
Definition juce_WavAudioFormat.cpp:58
juce::WavAudioFormat::riffInfoTrackNumber
static const char *const riffInfoTrackNumber
Definition juce_WavAudioFormat.h:169
juce::WavAudioFormat::riffInfoEngineer
static const char *const riffInfoEngineer
Definition juce_WavAudioFormat.h:119
juce::WavAudioFormat::riffInfoWrittenBy
static const char *const riffInfoWrittenBy
Definition juce_WavAudioFormat.h:175
juce::WavAudioFormat::aswgSubCategory
static const char *const aswgSubCategory
Definition juce_WavAudioFormat.h:204
juce::WavAudioFormat::aswgText
static const char *const aswgText
Definition juce_WavAudioFormat.h:220
juce::WavAudioFormat::riffInfoTimeCode
static const char *const riffInfoTimeCode
Definition juce_WavAudioFormat.h:166
juce::WavAudioFormat::riffInfoSourceFrom
static const char *const riffInfoSourceFrom
Definition juce_WavAudioFormat.h:157
juce::WavAudioFormat::riffInfoSource
static const char *const riffInfoSource
Definition juce_WavAudioFormat.h:156
juce::WavAudioFormat::riffInfoLogoURL
static const char *const riffInfoLogoURL
Definition juce_WavAudioFormat.h:130
juce::WavAudioFormat::aswgOrderRef
static const char *const aswgOrderRef
Definition juce_WavAudioFormat.h:253
juce::WavAudioFormat::riffInfoCountry
static const char *const riffInfoCountry
Definition juce_WavAudioFormat.h:106
juce::WavAudioFormat::aswgAmbisonicNorm
static const char *const aswgAmbisonicNorm
Definition juce_WavAudioFormat.h:194
juce::WavAudioFormat::aswgUserCategory
static const char *const aswgUserCategory
Definition juce_WavAudioFormat.h:206
juce::WavAudioFormat::aswgNotes
static const char *const aswgNotes
Definition juce_WavAudioFormat.h:185
juce::WavAudioFormat::riffInfoSecondLanguage
static const char *const riffInfoSecondLanguage
Definition juce_WavAudioFormat.h:150
juce::WavAudioFormat::aswgTempo
static const char *const aswgTempo
Definition juce_WavAudioFormat.h:260
juce::WavAudioFormat::riffInfoComment
static const char *const riffInfoComment
Definition juce_WavAudioFormat.h:100
juce::WavAudioFormat::riffInfoTapeName
static const char *const riffInfoTapeName
Definition juce_WavAudioFormat.h:163
juce::WavAudioFormat::aswgDirector
static const char *const aswgDirector
Definition juce_WavAudioFormat.h:232
juce::WavAudioFormat::aswgRecEngineer
static const char *const aswgRecEngineer
Definition juce_WavAudioFormat.h:200
juce::WavAudioFormat::aswgCatId
static const char *const aswgCatId
Definition juce_WavAudioFormat.h:205
juce::WavAudioFormat::riffInfoEndTimecode
static const char *const riffInfoEndTimecode
Definition juce_WavAudioFormat.h:118
juce::WavAudioFormat::aswgIsCinematic
static const char *const aswgIsCinematic
Definition juce_WavAudioFormat.h:255
juce::WavAudioFormat::WavAudioFormat
WavAudioFormat()
Definition juce_WavAudioFormat.cpp:1943
juce::WavAudioFormat::riffInfoPart
static const char *const riffInfoPart
Definition juce_WavAudioFormat.h:140
juce::WavAudioFormat::bwavOriginator
static const char *const bwavOriginator
Definition juce_WavAudioFormat.h:51
juce::WavAudioFormat::acidTempo
static const char *const acidTempo
Definition juce_WavAudioFormat.h:91
juce::WavAudioFormat::acidRootNote
static const char *const acidRootNote
Definition juce_WavAudioFormat.h:87
juce::WavAudioFormat::riffInfoMoreInfoURL
static const char *const riffInfoMoreInfoURL
Definition juce_WavAudioFormat.h:135