JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_WavAudioFormat.cpp
Go to the documentation of this file.
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
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
59 const String& originator,
60 const String& originatorRef,
61 Time date,
63 const String& codingHistory)
64{
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"));
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
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
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 //==============================================================================
353 {
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 //==============================================================================
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
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
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 {
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 {
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 {
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
834 }
835 }
836
837 //==============================================================================
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 //==============================================================================
933 {
934 static MemoryBlock createFrom (const StringMap& values)
935 {
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 {
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
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.
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,
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 //==============================================================================
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//==============================================================================
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
1219
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
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();
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
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
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 {
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 {
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 {
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 {
1380 AXMLChunk::addToMetadata (dict, axml.toString());
1381 }
1382 else if (chunkType == chunkName ("iXML"))
1383 {
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
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
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
1448 }
1449 }
1450 }
1451 else if (chunkType == chunkName ("acid"))
1452 {
1453 AcidChunk (*input, length).addToMetadata (dict);
1454 }
1455 else if (chunkType == chunkName ("Trkn"))
1456 {
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
1476 }
1477
1478 //==============================================================================
1480 int64 startSampleInFile, int numSamples) override
1481 {
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,
1506 tempBuffer, (int) numChannels, numThisTime);
1507
1509 numSamples -= numThisTime;
1510 }
1511
1512 return true;
1513 }
1514
1515 static void copySampleData (unsigned int numBitsPerSample, const bool floatingPointData,
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 //==============================================================================
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 {
1543
1544 // AudioChannelSet and wav's dwChannelMask are compatible
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)
1557 else
1558 {
1559 auto discreteSpeaker = static_cast<int> (AudioChannelSet::discreteChannel0);
1560
1561 while (wavFileChannelLayout.size() < static_cast<int> (totalNumChannels))
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:
1579};
1580
1581//==============================================================================
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 {
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 {
1661 writeHeader();
1662
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
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.
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
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.
1745 #endif
1746
1747 writeChunkHeader (chunkName ("ds64"), 28); // chunk size for uncompressed data (no table)
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
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
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 {
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
1843};
1844
1845//==============================================================================
1847{
1848public:
1850 : MemoryMappedAudioFormatReader (wavFile, reader, reader.dataChunkStart,
1851 reader.dataLength, reader.bytesPerFrame)
1852 {
1853 }
1854
1856 int64 startSampleInFile, int numSamples) override
1857 {
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,
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 {
1898 break;
1899 default: jassertfalse; break;
1900 }
1901 }
1902
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;
1924 break;
1925 default: jassertfalse; break;
1926 }
1927 }
1928
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)
1937 }
1938
1939 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedWavReader)
1940};
1941
1942//==============================================================================
1943WavAudioFormat::WavAudioFormat() : AudioFormat (wavFormatName, ".wav .bwf") {}
1945
1947{
1948 return { 8000, 11025, 12000, 16000, 22050, 32000, 44100,
1949 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
1950}
1951
1953{
1954 return { 8, 16, 24, 32 };
1955}
1956
1957bool WavAudioFormat::canDoStereo() { return true; }
1958bool WavAudioFormat::canDoMono() { return true; }
1959
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)
1971 return false;
1972
1973 return true;
1974}
1975
1977{
1979
1980 #if JUCE_USE_OGGVORBIS
1981 if (r->isSubformatOggVorbis)
1982 {
1983 r->input = nullptr;
1985 }
1986 #endif
1987
1988 if (r->sampleRate > 0 && r->numChannels > 0 && r->bytesPerFrame > 0 && r->bitsPerSample <= 32)
1989 return r.release();
1990
1992 r->input = nullptr;
1993
1994 return nullptr;
1995}
1996
2001
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
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
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);
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
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 {
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{
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",
2134 "codingHistory"));
2135
2136 for (int i = numElementsInArray (WavFileHelpers::ListInfoChunk::types); --i >= 0;)
2137 metadataValues[WavFileHelpers::ListInfoChunk::types[i]] = WavFileHelpers::ListInfoChunk::types[i];
2138
2140
2141 if (metadataValues.size() > 0)
2142 metadataValues["MetaDataSource"] = "WAV";
2143
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" },
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");
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,
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 {
2309 }
2310
2311 void checkPatternsNotPresent (const MemoryBlock& mb, const std::vector<std::string>& patterns)
2312 {
2314 }
2315
2316 enum
2317 {
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
2340};
2341
2343
2344#endif
2345
2346} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
Represents a set of audio channel types.
static AudioChannelSet JUCE_CALLTYPE quadraphonic()
Creates a set for quadraphonic surround setup (left, right, leftSurround, rightSurround)
static AudioChannelSet JUCE_CALLTYPE create5point0()
Creates a set for a 5.0 surround setup (left, right, centre, leftSurround, rightSurround).
int size() const noexcept
Returns the number of channels in the set.
static AudioChannelSet JUCE_CALLTYPE mono()
Creates a one-channel mono set (centre).
static AudioChannelSet JUCE_CALLTYPE stereo()
Creates a set containing a stereo set (left, right).
ChannelType
Represents different audio channel types.
@ topRearRight
Top Rear Right channel.
@ discreteChannel0
Non-typed individual channels are indexed upwards from this value.
static AudioChannelSet JUCE_CALLTYPE create5point1()
Creates a set for a 5.1 surround setup (left, right, centre, leftSurround, rightSurround,...
static AudioChannelSet JUCE_CALLTYPE create7point0SDDS()
Creates a set for a SDDS 7.0 surround setup (left, right, centre, leftSurround, rightSurround,...
static AudioChannelSet JUCE_CALLTYPE create7point1SDDS()
Creates a set for a 7.1 surround setup (left, right, centre, leftSurround, rightSurround,...
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
Create a canonical channel set for a given number of channels.
static AudioChannelSet JUCE_CALLTYPE discreteChannels(int numChannels)
Creates a set of untyped discrete channels.
static AudioChannelSet JUCE_CALLTYPE createLCR()
Creates a set containing an LCR set (left, right, centre).
Reads samples from an audio file stream.
InputStream * input
The input stream, for use by subclasses.
bool usesFloatingPointData
Indicates whether the data is floating-point or fixed.
StringPairArray metadataValues
A set of metadata values that the reader has pulled out of the stream.
static void clearSamplesBeyondAvailableLength(int *const *destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int &numSamples, int64 fileLengthInSamples)
Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie beyond the end of...
int64 lengthInSamples
The total number of samples in the audio stream.
double sampleRate
The sample-rate of the stream.
unsigned int bitsPerSample
The number of bits per sample, e.g.
virtual void readMaxLevels(int64 startSample, int64 numSamples, Range< float > *results, int numChannelsToRead)
Finds the highest and lowest sample levels from a section of the audio stream.
unsigned int numChannels
The total number of channels in the audio stream.
Writes samples to an audio file stream.
unsigned int numChannels
The number of channels being written to the stream.
double sampleRate
The sample rate of the stream.
bool usesFloatingPointData
True if it's a floating-point format, false if it's fixed-point.
unsigned int bitsPerSample
The bit depth of the file.
AudioChannelSet channelLayout
The audio channel layout that the writer should use.
OutputStream * output
The output stream for use by subclasses.
Subclasses of AudioFormat are used to read and write different audio file formats.
An arbitrarily large integer class.
static constexpr uint32 littleEndianInt(const void *bytes) noexcept
Turns 4 bytes into a little-endian integer.
static constexpr uint16 swap(uint16 value) noexcept
Swaps the upper and lower bytes of a 16-bit integer.
static Type swapIfBigEndian(Type value) noexcept
Swaps the byte order of a signed or unsigned integer if the CPU is big-endian.
static juce_wchar toUpperCase(juce_wchar character) noexcept
Converts a character to upper-case.
An input stream that reads from a local file.
An output stream that writes into a local file.
bool setPosition(int64) override
Tries to move the stream's output position.
bool openedOk() const noexcept
Returns true if the stream opened without problems.
Represents a local file or directory.
Definition juce_File.h:45
std::unique_ptr< FileInputStream > createInputStream() const
Creates a stream to read from this file.
Very simple container class to hold a pointer to some data on the heap.
void calloc(SizeType newNumElements, const size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory and clears it.
The base class for streams that read data.
virtual int64 getPosition()=0
Returns the offset of the next byte that will be read from the stream.
virtual int64 readInt64()
Reads eight bytes from the stream as a little-endian 64-bit value.
virtual bool setPosition(int64 newPosition)=0
Tries to move the current read position of the stream.
virtual bool isExhausted()=0
Returns true if the stream has no more data to read.
virtual short readShort()
Reads two bytes from the stream as a little-endian 16-bit value.
virtual void skipNextBytes(int64 numBytesToSkip)
Reads and discards a number of bytes from the stream.
virtual size_t readIntoMemoryBlock(MemoryBlock &destBlock, ssize_t maxNumBytesToRead=-1)
Reads from the stream and appends the data to a MemoryBlock.
virtual int read(void *destBuffer, int maxBytesToRead)=0
Reads some data from the stream into a memory buffer.
virtual int readInt()
Reads four bytes from the stream as a little-endian 32-bit value.
A class to hold a resizable block of raw data.
void * getData() noexcept
Returns a void pointer to the data.
void ensureSize(size_t minimumSize, bool initialiseNewSpaceToZero=false)
Increases the block's size only if it's smaller than a given size.
A specialised type of AudioFormatReader that uses a MemoryMappedFile to read directly from an audio f...
const void * sampleToPointer(int64 sample) const noexcept
Converts a sample index to a pointer to the mapped file memory.
bool readSamples(int *const *destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override
Subclasses must implement this method to perform the low-level read operation.
void readMaxLevels(int64 startSampleInFile, int64 numSamples, Range< float > *results, int numChannelsToRead) override
Finds the highest and lowest sample levels from a section of the audio stream.
void getSample(int64 sample, float *result) const noexcept override
Returns the samples for all channels at a given sample position.
Writes data to an internal memory buffer, which grows as required.
size_t getDataSize() const noexcept
Returns the number of bytes of data that have been written to the stream.
bool write(const void *, size_t) override
Writes a block of data to the stream.
MemoryBlock getMemoryBlock() const
Returns a copy of the stream's data as a memory block.
Reads and writes the Ogg-Vorbis audio format.
AudioFormatReader * createReaderFor(InputStream *sourceStream, bool deleteStreamIfOpeningFails) override
Tries to create an object that can read from a stream containing audio data in this format.
The base class for streams that write data to some kind of destination.
virtual bool write(const void *dataToWrite, size_t numberOfBytes)=0
Writes a block of data to the stream.
virtual bool writeRepeatedByte(uint8 byte, size_t numTimesToRepeat)
Writes a byte to the output stream a given number of times.
virtual int64 getPosition()=0
Returns the stream's current position.
virtual bool writeByte(char byte)
Writes a single byte to the stream.
virtual bool writeShort(short value)
Writes a 16-bit integer to the stream in a little-endian byte order.
virtual bool writeInt64(int64 value)
Writes a 64-bit integer to the stream in a little-endian byte order.
virtual bool setPosition(int64 newPosition)=0
Tries to move the stream's output position.
virtual bool writeInt(int value)
Writes a 32-bit integer to the stream in a little-endian byte order.
virtual bool writeString(const String &text)
Stores a string in the stream in a binary format.
A general-purpose range object, that simply represents any linear range with a start and end point.
Definition juce_Range.h:40
constexpr bool contains(const ValueType position) const noexcept
Returns true if the given position lies inside this range.
Definition juce_Range.h:214
A container for holding a set of strings which are keyed by another string.
String getValue(StringRef, const String &defaultReturnValue) const
Finds the value corresponding to a key string.
void set(const String &key, const String &value)
Adds or amends a key/value pair.
void addUnorderedMap(const std::unordered_map< String, String > &mapToAdd)
Adds the contents of an unordered map to this StringPairArray.
int size() const noexcept
Returns the number of strings in the array.
The JUCE String class!
Definition juce_String.h:53
static String createStringFromData(const void *data, int size)
Creates a string from data in an unknown format.
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
Manages a temporary file, which will be deleted when this object is deleted.
Holds an absolute date and time.
Definition juce_Time.h:37
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Returns a Time object that is set to the current system time.
String formatted(const String &format) const
Converts this date/time to a string with a user-defined format.
This is a base class for classes that perform a unit test.
bool readSamples(int *const *destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override
Subclasses must implement this method to perform the low-level read operation.
AudioChannelSet getChannelLayout() override
Get the channel layout of the audio stream.
bool flush() override
Some formats may support a flush operation that makes sure the file is in a valid state before carryi...
bool write(const int **data, int numSamples) override
Writes a set of samples to the audio stream.
Reads and Writes WAV format audio files.
static const char *const aswgRecStudio
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgBillingCode
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgMicDistance
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoCopyright
Metadata property name used in INFO chunks.
static const char *const acidRootSet
Metadata property name used in acid chunks.
static const char *const aswgUserData
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoDirectory
Metadata property name used in INFO chunks.
static const char *const bwavCodingHistory
Metadata property name used in BWAV chunks.
static const char *const bwavTimeReference
Metadata property name used in BWAV chunks.
static const char *const acidDiskBased
Metadata property name used in acid chunks.
static const char *const aswgEfforts
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgMusicSup
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgTimeSig
Metadata property name used in ASWG/iXML chunks.
static const char *const acidOneShot
Metadata property name used in acid chunks.
static const char *const aswgIsDesigned
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoOrganisation
Metadata property name used in INFO chunks.
static const char *const aswgCharacterName
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgIsUnion
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgLibrary
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoFirstLanguage
Metadata property name used in INFO chunks.
static const char *const aswgAmbisonicChnOrder
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgRecordingLoc
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgCreatorId
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoEncodedBy
Metadata property name used in INFO chunks.
static const char *const riffInfoCommissioned
Metadata property name used in INFO chunks.
static const char *const riffInfoMusicBy
Metadata property name used in INFO chunks.
static const char *const riffInfoSharpness
Metadata property name used in INFO chunks.
static const char *const aswgProducer
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgFxUsed
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoStatistics
Metadata property name used in INFO chunks.
static const char *const riffInfoNinthLanguage
Metadata property name used in INFO chunks.
static const char *const riffInfoDefaultAudioStream
Metadata property name used in INFO chunks.
static const char *const riffInfoGenre
Metadata property name used in INFO chunks.
bool canDoMono() override
Returns true if the format can do 1-channel audio.
static const char *const riffInfoMoreInfoBannerImage
Metadata property name used in INFO chunks.
static const char *const aswgProjection
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoVegasVersionMajor
Metadata property name used in INFO chunks.
static const char *const aswgContentType
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgOriginator
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoLocation
Metadata property name used in INFO chunks.
static const char *const aswgIsOst
Metadata property name used in ASWG/iXML chunks.
AudioFormatReader * createReaderFor(InputStream *sourceStream, bool deleteStreamIfOpeningFails) override
Tries to create an object that can read from a stream containing audio data in this format.
static const char *const aswgRmsPower
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoRate
Metadata property name used in INFO chunks.
static const char *const riffInfoCostumeDesigner
Metadata property name used in INFO chunks.
static const char *const riffInfoVersion
Metadata property name used in INFO chunks.
static const char *const aswgActorGender
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoLightness
Metadata property name used in INFO chunks.
static const char *const riffInfoProductionStudio
Metadata property name used in INFO chunks.
static const char *const aswgIsLoop
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgState
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgSongTitle
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoProducedBy
Metadata property name used in INFO chunks.
static const char *const aswgSpecDensity
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoEighthLanguage
Metadata property name used in INFO chunks.
static const char *const riffInfoCropped
Metadata property name used in INFO chunks.
static const char *const aswgCharacterAge
Metadata property name used in ASWG/iXML chunks.
bool canDoStereo() override
Returns true if the format can do 2-channel audio.
static const char *const riffInfoRating
Metadata property name used in INFO chunks.
static const char *const aswgMicType
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgIsDiegetic
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoURL
Metadata property name used in INFO chunks.
static const char *const aswgVersion
Metadata property name used in ASWG/iXML chunks.
static const char *const ISRC
Metadata property name used when reading an ISRC code from an AXML chunk.
static const char *const riffInfoMoreInfoBannerURL
Metadata property name used in INFO chunks.
static const char *const riffInfoStartTimecode
Metadata property name used in INFO chunks.
static const char *const aswgLoudnessRange
Metadata property name used in ASWG/iXML chunks.
static const char *const bwavOriginatorRef
Metadata property name used in BWAV chunks.
static const char *const aswgChannelConfig
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoTitle
Metadata property name used in INFO chunks.
static const char *const aswgIsFinal
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoArtist
Metadata property name used in INFO chunks.
static const char *const aswgMusicPublisher
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoSixthLanguage
Metadata property name used in INFO chunks.
static const char *const riffInfoSecondaryGenre
Metadata property name used in INFO chunks.
static const char *const riffInfoFifthLanguage
Metadata property name used in INFO chunks.
static const char *const riffInfoDotsPerInch
Metadata property name used in INFO chunks.
static const char *const riffInfoDistributedBy
Metadata property name used in INFO chunks.
static const char *const riffInfoStarring_ISTR
Metadata property name used in INFO chunks.
static const char *const aswgAccent
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoProductName
Metadata property name used in INFO chunks.
static const char *const riffInfoKeywords
Metadata property name used in INFO chunks.
static const char *const aswgInstrument
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgSession
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoRippedBy
Metadata property name used in INFO chunks.
static const char *const riffInfoLanguage
Metadata property name used in INFO chunks.
static const char *const riffInfoDateTimeOriginal
Metadata property name used in INFO chunks.
static const char *const acidizerFlag
Metadata property name used in acid chunks.
static const char *const riffInfoBaseURL
Metadata property name used in INFO chunks.
MemoryMappedAudioFormatReader * createMemoryMappedReader(const File &) override
Attempts to create a MemoryMappedAudioFormatReader, if possible for this format.
bool isChannelLayoutSupported(const AudioChannelSet &channelSet) override
Returns true if the channel layout is supported by this format.
static const char *const aswgArtist
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoProductionDesigner
Metadata property name used in INFO chunks.
static const char *const acidDenominator
Metadata property name used in acid chunks.
static const char *const aswgCharacterRole
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgDirection
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgIsSource
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgFxChainName
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgFxName
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgOriginatorStudio
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoVegasVersionMinor
Metadata property name used in INFO chunks.
static const char *const riffInfoLength
Metadata property name used in INFO chunks.
bool replaceMetadataInFile(const File &wavFile, const StringPairArray &newMetadata)
Utility function to replace the metadata in a wav file with a new set of values.
static const char *const aswgUsageRights
Metadata property name used in ASWG/iXML chunks.
~WavAudioFormat() override
Destructor.
static const char *const riffInfoTechnician
Metadata property name used in INFO chunks.
static const char *const riffInfoSoftware
Metadata property name used in INFO chunks.
static const char *const riffInfoStarring_STAR
Metadata property name used in INFO chunks.
static const char *const riffInfoDateCreated
Metadata property name used in INFO chunks.
static const char *const riffInfoSeventhLanguage
Metadata property name used in INFO chunks.
static const char *const aswgActorName
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgAmbisonicFormat
Metadata property name used in ASWG/iXML chunks.
static const char *const acidBeats
Metadata property name used in acid chunks.
static const char *const aswgMusicVersion
Metadata property name used in ASWG/iXML chunks.
AudioFormatWriter * createWriterFor(OutputStream *streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray &metadataValues, int qualityOptionIndex) override
Tries to create an object that can write to a stream with this audio format.
static const char *const aswgMixer
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoLogoIconURL
Metadata property name used in INFO chunks.
static const char *const tracktionLoopInfo
Metadata property name used when reading a WAV file with a Tracktion chunk.
static const char *const acidNumerator
Metadata property name used in acid chunks.
static const char *const bwavOriginationDate
Metadata property name used in BWAV chunks.
static const char *const internationalStandardRecordingCode
Metadata property name used when reading and writing ISRC codes to/from AXML chunks.
static const char *const aswgSourceId
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoComments
Metadata property name used in INFO chunks.
static const char *const riffInfoNumberOfParts
Metadata property name used in INFO chunks.
static const char *const aswgVendorCategory
Metadata property name used in ASWG/iXML chunks.
static const char *const bwavDescription
Metadata property name used in BWAV chunks.
static const char *const riffInfoSoundSchemeTitle
Metadata property name used in INFO chunks.
static const char *const aswgPapr
Metadata property name used in ASWG/iXML chunks.
Array< int > getPossibleSampleRates() override
Returns a set of sample rates that the format can read and write.
static const char *const aswgEditor
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgComposor
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgSubGenre
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgEffortType
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoWatermarkURL
Metadata property name used in INFO chunks.
static const char *const aswgLoudness
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoTrackNo
Metadata property name used in INFO chunks.
static const char *const riffInfoMedium
Metadata property name used in INFO chunks.
static const char *const acidStretch
Metadata property name used in acid chunks.
Array< int > getPossibleBitDepths() override
Returns a set of bit depths that the format can read and write.
static const char *const aswgRightsOwner
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgImpulseLocation
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoThirdLanguage
Metadata property name used in INFO chunks.
static const char *const bwavOriginationTime
Metadata property name used in BWAV chunks.
static const char *const aswgLanguage
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoArchivalLocation
Metadata property name used in INFO chunks.
static const char *const aswgTimingRestriction
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgIsrcId
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgIsLicensed
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgCategory
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgZeroCrossRate
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgGenre
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgMaxPeak
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoMoreInfoText
Metadata property name used in INFO chunks.
static const char *const aswgCharacterGender
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoCinematographer
Metadata property name used in INFO chunks.
static const char *const riffInfoFourthLanguage
Metadata property name used in INFO chunks.
static const char *const riffInfoSubject
Metadata property name used in INFO chunks.
static const char *const aswgEmotion
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgInKey
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgMicConfig
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoRated
Metadata property name used in INFO chunks.
static const char *const riffInfoDimension
Metadata property name used in INFO chunks.
static const char *const aswgProject
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgIntensity
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoEditedBy
Metadata property name used in INFO chunks.
static const char *const riffInfoYear
Metadata property name used in INFO chunks.
static const char *const riffInfoComment2
Metadata property name used in INFO chunks.
static StringPairArray createBWAVMetadata(const String &description, const String &originator, const String &originatorRef, Time dateAndTime, int64 timeReferenceSamples, const String &codingHistory)
Utility function to fill out the appropriate metadata for a BWAV file.
static const char *const riffInfoTrackNumber
Metadata property name used in INFO chunks.
static const char *const riffInfoEngineer
Metadata property name used in INFO chunks.
static const char *const riffInfoWrittenBy
Metadata property name used in INFO chunks.
static const char *const aswgSubCategory
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgText
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoTimeCode
Metadata property name used in INFO chunks.
static const char *const riffInfoSourceFrom
Metadata property name used in INFO chunks.
static const char *const riffInfoSource
Metadata property name used in INFO chunks.
static const char *const riffInfoLogoURL
Metadata property name used in INFO chunks.
static const char *const aswgOrderRef
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoCountry
Metadata property name used in INFO chunks.
static const char *const aswgAmbisonicNorm
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgUserCategory
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgNotes
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoSecondLanguage
Metadata property name used in INFO chunks.
static const char *const aswgTempo
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoComment
Metadata property name used in INFO chunks.
static const char *const riffInfoTapeName
Metadata property name used in INFO chunks.
static const char *const aswgDirector
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgRecEngineer
Metadata property name used in ASWG/iXML chunks.
static const char *const aswgCatId
Metadata property name used in ASWG/iXML chunks.
static const char *const riffInfoEndTimecode
Metadata property name used in INFO chunks.
static const char *const aswgIsCinematic
Metadata property name used in ASWG/iXML chunks.
WavAudioFormat()
Creates a format object.
static const char *const riffInfoPart
Metadata property name used in INFO chunks.
static const char *const bwavOriginator
Metadata property name used in BWAV chunks.
static const char *const acidTempo
Metadata property name used in acid chunks.
static const char *const acidRootNote
Metadata property name used in acid chunks.
static const char *const riffInfoMoreInfoURL
Metadata property name used in INFO chunks.
T data(T... args)
T cend(T... args)
T find(T... args)
T format(T... args)
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE(className)
This is a shorthand macro for deleting a class's copy constructor and copy assignment operator.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
typedef int
memcmp
JUCE Namespace.
void zerostruct(Type &structure) noexcept
Overwrites a structure or object with zeros.
Definition juce_Memory.h:32
unsigned short uint16
A platform-independent 16-bit unsigned integer type.
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
std::unique_ptr< XmlElement > parseXML(const String &textToParse)
Attempts to parse some XML text, returning a new XmlElement if it was valid.
signed char int8
A platform-independent 8-bit signed integer type.
unsigned long long uint64
A platform-independent 64-bit unsigned integer type.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
RangedDirectoryIterator begin(const RangedDirectoryIterator &it)
Returns the iterator that was passed in.
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.
long long int64
A platform-independent 64-bit integer type.
std::unique_ptr< T > rawToUniquePtr(T *ptr)
Converts an owning raw pointer into a unique_ptr, deriving the type of the unique_ptr automatically.
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.
Definition juce_Memory.h:28
T release(T... args)
T reset(T... args)
T search(T... args)
Used by AudioFormatReader subclasses to copy data to different formats.
Used by AudioFormatWriter subclasses to copy data to different formats.
AcidChunk(InputStream &input, size_t length)
Reads an acid RIFF chunk from a stream positioned just after the size byte.
typedef size_t
time