diff --git a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index eddc9d3cf5..8414d5c873 100644 --- a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -26,6 +26,24 @@ namespace juce { +using StringMap = std::unordered_map; + +static auto toMap (const StringPairArray& array) +{ + StringMap result; + + for (auto i = 0; i < array.size(); ++i) + result[array.getAllKeys()[i]] = array.getAllValues()[i]; + + return result; +} + +static auto getValueWithDefault (const StringMap& m, const String& key, const String& fallback = {}) +{ + const auto iter = m.find (key); + return iter != m.cend() ? iter->second : fallback; +} + static const char* const wavFormatName = "WAV file"; //============================================================================== @@ -177,43 +195,42 @@ namespace WavFileHelpers uint8 reserved[190]; char codingHistory[1]; - void copyTo (StringPairArray& values, const int totalSize) const + void copyTo (StringMap& values, const int totalSize) const { - values.set (WavAudioFormat::bwavDescription, String::fromUTF8 (description, sizeof (description))); - values.set (WavAudioFormat::bwavOriginator, String::fromUTF8 (originator, sizeof (originator))); - values.set (WavAudioFormat::bwavOriginatorRef, String::fromUTF8 (originatorRef, sizeof (originatorRef))); - values.set (WavAudioFormat::bwavOriginationDate, String::fromUTF8 (originationDate, sizeof (originationDate))); - values.set (WavAudioFormat::bwavOriginationTime, String::fromUTF8 (originationTime, sizeof (originationTime))); + values[WavAudioFormat::bwavDescription] = String::fromUTF8 (description, sizeof (description)); + values[WavAudioFormat::bwavOriginator] = String::fromUTF8 (originator, sizeof (originator)); + values[WavAudioFormat::bwavOriginatorRef] = String::fromUTF8 (originatorRef, sizeof (originatorRef)); + values[WavAudioFormat::bwavOriginationDate] = String::fromUTF8 (originationDate, sizeof (originationDate)); + values[WavAudioFormat::bwavOriginationTime] = String::fromUTF8 (originationTime, sizeof (originationTime)); auto timeLow = ByteOrder::swapIfBigEndian (timeRefLow); auto timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh); auto time = (((int64) timeHigh) << 32) + timeLow; - values.set (WavAudioFormat::bwavTimeReference, String (time)); - values.set (WavAudioFormat::bwavCodingHistory, - String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory))); + values[WavAudioFormat::bwavTimeReference] = String (time); + values[WavAudioFormat::bwavCodingHistory] = String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory)); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { - MemoryBlock data (roundUpSize (sizeof (BWAVChunk) + values[WavAudioFormat::bwavCodingHistory].getNumBytesAsUTF8())); + MemoryBlock data (roundUpSize (sizeof (BWAVChunk) + getValueWithDefault (values, WavAudioFormat::bwavCodingHistory).getNumBytesAsUTF8())); data.fillWith (0); auto* b = (BWAVChunk*) data.getData(); // Allow these calls to overwrite an extra byte at the end, which is fine as long - // as they get called in the right order.. - values[WavAudioFormat::bwavDescription] .copyToUTF8 (b->description, 257); - values[WavAudioFormat::bwavOriginator] .copyToUTF8 (b->originator, 33); - values[WavAudioFormat::bwavOriginatorRef] .copyToUTF8 (b->originatorRef, 33); - values[WavAudioFormat::bwavOriginationDate].copyToUTF8 (b->originationDate, 11); - values[WavAudioFormat::bwavOriginationTime].copyToUTF8 (b->originationTime, 9); - - auto time = values[WavAudioFormat::bwavTimeReference].getLargeIntValue(); + // as they get called in the right order. + getValueWithDefault (values, WavAudioFormat::bwavDescription) .copyToUTF8 (b->description, 257); + getValueWithDefault (values, WavAudioFormat::bwavOriginator) .copyToUTF8 (b->originator, 33); + getValueWithDefault (values, WavAudioFormat::bwavOriginatorRef) .copyToUTF8 (b->originatorRef, 33); + getValueWithDefault (values, WavAudioFormat::bwavOriginationDate).copyToUTF8 (b->originationDate, 11); + getValueWithDefault (values, WavAudioFormat::bwavOriginationTime).copyToUTF8 (b->originationTime, 9); + + auto time = getValueWithDefault (values, WavAudioFormat::bwavTimeReference).getLargeIntValue(); b->timeRefLow = ByteOrder::swapIfBigEndian ((uint32) (time & 0xffffffff)); b->timeRefHigh = ByteOrder::swapIfBigEndian ((uint32) (time >> 32)); - values[WavAudioFormat::bwavCodingHistory].copyToUTF8 (b->codingHistory, 0x7fffffff); + getValueWithDefault (values, WavAudioFormat::bwavCodingHistory).copyToUTF8 (b->codingHistory, 0x7fffffff); if (b->description[0] != 0 || b->originator[0] != 0 @@ -270,17 +287,17 @@ namespace WavFileHelpers SampleLoop loops[1]; template - static void setValue (StringPairArray& values, NameType name, uint32 val) + static void setValue (StringMap& values, NameType name, uint32 val) { - values.set (name, String (ByteOrder::swapIfBigEndian (val))); + values[name] = String (ByteOrder::swapIfBigEndian (val)); } - static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val) + static void setValue (StringMap& values, int prefix, const char* name, uint32 val) { setValue (values, "Loop" + String (prefix) + name, val); } - void copyTo (StringPairArray& values, const int totalSize) const + void copyTo (StringMap& values, const int totalSize) const { setValue (values, "Manufacturer", manufacturer); setValue (values, "Product", product); @@ -307,20 +324,20 @@ namespace WavFileHelpers } template - static uint32 getValue (const StringPairArray& values, NameType name, const char* def) + static uint32 getValue (const StringMap& values, NameType name, const char* def) { - return ByteOrder::swapIfBigEndian ((uint32) values.getValue (name, def).getIntValue()); + return ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, name, def).getIntValue()); } - static uint32 getValue (const StringPairArray& values, int prefix, const char* name, const char* def) + static uint32 getValue (const StringMap& values, int prefix, const char* name, const char* def) { return getValue (values, "Loop" + String (prefix) + name, def); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryBlock data; - auto numLoops = jmin (64, values.getValue ("NumSampleLoops", "0").getIntValue()); + auto numLoops = jmin (64, getValueWithDefault (values, "NumSampleLoops", "0").getIntValue()); data.setSize (roundUpSize (sizeof (SMPLChunk) + (size_t) (jmax (0, numLoops - 1)) * sizeof (SampleLoop)), true); @@ -362,12 +379,12 @@ namespace WavFileHelpers int8 lowVelocity; int8 highVelocity; - static void setValue (StringPairArray& values, const char* name, int val) + static void setValue (StringMap& values, const char* name, int val) { - values.set (name, String (val)); + values[name] = String (val); } - void copyTo (StringPairArray& values) const + void copyTo (StringMap& values) const { setValue (values, "MidiUnityNote", baseNote); setValue (values, "Detune", detune); @@ -378,17 +395,17 @@ namespace WavFileHelpers setValue (values, "HighVelocity", highVelocity); } - static int8 getValue (const StringPairArray& values, const char* name, const char* def) + static int8 getValue (const StringMap& values, const char* name, const char* def) { - return (int8) values.getValue (name, def).getIntValue(); + return (int8) getValueWithDefault (values, name, def).getIntValue(); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryBlock data; - auto& keys = values.getAllKeys(); - if (keys.contains ("LowNote", true) && keys.contains ("HighNote", true)) + if ( values.find ("LowNote") != values.cend() + && values.find ("HighNote") != values.cend()) { data.setSize (8, true); auto* inst = static_cast (data.getData()); @@ -422,14 +439,14 @@ namespace WavFileHelpers uint32 numCues; Cue cues[1]; - static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val) + static void setValue (StringMap& values, int prefix, const char* name, uint32 val) { - values.set ("Cue" + String (prefix) + name, String (ByteOrder::swapIfBigEndian (val))); + values["Cue" + String (prefix) + name] = String (ByteOrder::swapIfBigEndian (val)); } - void copyTo (StringPairArray& values, const int totalSize) const + void copyTo (StringMap& values, const int totalSize) const { - values.set ("NumCuePoints", String (ByteOrder::swapIfBigEndian (numCues))); + values["NumCuePoints"] = String (ByteOrder::swapIfBigEndian (numCues)); for (int i = 0; i < (int) numCues; ++i) { @@ -445,10 +462,10 @@ namespace WavFileHelpers } } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryBlock data; - const int numCues = values.getValue ("NumCuePoints", "0").getIntValue(); + const int numCues = getValueWithDefault (values, "NumCuePoints", "0").getIntValue(); if (numCues > 0) { @@ -468,23 +485,23 @@ namespace WavFileHelpers for (int i = 0; i < numCues; ++i) { auto prefix = "Cue" + String (i); - auto identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue(); + auto identifier = (uint32) getValueWithDefault (values, prefix + "Identifier", "0").getIntValue(); #if JUCE_DEBUG jassert (! identifiers.contains (identifier)); identifiers.add (identifier); #endif - auto order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue(); + auto order = getValueWithDefault (values, prefix + "Order", String (nextOrder)).getIntValue(); nextOrder = jmax (nextOrder, order) + 1; auto& cue = c->cues[i]; cue.identifier = ByteOrder::swapIfBigEndian ((uint32) identifier); cue.order = ByteOrder::swapIfBigEndian ((uint32) order); - cue.chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue()); - cue.chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue()); - cue.blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue()); - cue.offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue()); + cue.chunkID = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "ChunkID", dataChunkID).getIntValue()); + cue.chunkStart = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "ChunkStart", "0").getIntValue()); + cue.blockStart = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "BlockStart", "0").getIntValue()); + cue.offset = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "Offset", "0").getIntValue()); } } @@ -496,20 +513,20 @@ namespace WavFileHelpers //============================================================================== namespace ListChunk { - static int getValue (const StringPairArray& values, const String& name) + static int getValue (const StringMap& values, const String& name) { - return values.getValue (name, "0").getIntValue(); + return getValueWithDefault (values, name, "0").getIntValue(); } - static int getValue (const StringPairArray& values, const String& prefix, const char* name) + static int getValue (const StringMap& values, const String& prefix, const char* name) { return getValue (values, prefix + name); } - static void appendLabelOrNoteChunk (const StringPairArray& values, const String& prefix, + static void appendLabelOrNoteChunk (const StringMap& values, const String& prefix, const int chunkType, MemoryOutputStream& out) { - auto label = values.getValue (prefix + "Text", prefix); + auto label = getValueWithDefault (values, prefix + "Text", prefix); auto labelLength = (int) label.getNumBytesAsUTF8() + 1; auto chunkLength = 4 + labelLength + (labelLength & 1); @@ -522,9 +539,9 @@ namespace WavFileHelpers out.writeByte (0); } - static void appendExtraChunk (const StringPairArray& values, const String& prefix, MemoryOutputStream& out) + static void appendExtraChunk (const StringMap& values, const String& prefix, MemoryOutputStream& out) { - auto text = values.getValue (prefix + "Text", prefix); + auto text = getValueWithDefault (values, prefix + "Text", prefix); auto textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator auto chunkLength = textLength + 20 + (textLength & 1); @@ -544,7 +561,7 @@ namespace WavFileHelpers out.writeByte (0); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { auto numCueLabels = getValue (values, "NumCueLabels"); auto numCueNotes = getValue (values, "NumCueNotes"); @@ -668,7 +685,7 @@ namespace WavFileHelpers return true; } - static void addToMetadata (StringPairArray& values, InputStream& input, int64 chunkEnd) + static void addToMetadata (StringMap& values, InputStream& input, int64 chunkEnd) { while (input.getPosition() < chunkEnd) { @@ -688,8 +705,8 @@ namespace WavFileHelpers { MemoryBlock mb; input.readIntoMemoryBlock (mb, (ssize_t) infoLength); - values.set (type, String::createStringFromData ((const char*) mb.getData(), - (int) mb.getSize())); + values[type] = String::createStringFromData ((const char*) mb.getData(), + (int) mb.getSize()); break; } } @@ -697,9 +714,9 @@ namespace WavFileHelpers } } - static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName) + static bool writeValue (const StringMap& values, MemoryOutputStream& out, const char* paramName) { - auto value = values.getValue (paramName, {}); + auto value = getValueWithDefault (values, paramName, {}); if (value.isEmpty()) return false; @@ -717,7 +734,7 @@ namespace WavFileHelpers return true; } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryOutputStream out; out.writeInt (chunkName ("INFO")); @@ -741,7 +758,7 @@ namespace WavFileHelpers input.read (this, (int) jmin (sizeof (*this), length)); } - AcidChunk (const StringPairArray& values) + AcidChunk (const StringMap& values) { zerostruct (*this); @@ -751,18 +768,20 @@ namespace WavFileHelpers | getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08) | getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10); - if (values[WavAudioFormat::acidRootSet].getIntValue() != 0) - rootNote = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidRootNote].getIntValue()); + if (getValueWithDefault (values, WavAudioFormat::acidRootSet).getIntValue() != 0) + rootNote = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidRootNote).getIntValue()); + + numBeats = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, WavAudioFormat::acidBeats).getIntValue()); + meterDenominator = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidDenominator).getIntValue()); + meterNumerator = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidNumerator).getIntValue()); - numBeats = ByteOrder::swapIfBigEndian ((uint32) values[WavAudioFormat::acidBeats].getIntValue()); - meterDenominator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidDenominator].getIntValue()); - meterNumerator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidNumerator].getIntValue()); + const auto iter = values.find (WavAudioFormat::acidTempo); - if (values.containsKey (WavAudioFormat::acidTempo)) - tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue()); + if (iter != values.cend()) + tempo = swapFloatByteOrder (iter->second.getFloatValue()); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { return AcidChunk (values).toMemoryBlock(); } @@ -773,7 +792,7 @@ namespace WavFileHelpers ? MemoryBlock (this, sizeof (*this)) : MemoryBlock(); } - void addToMetadata (StringPairArray& values) const + void addToMetadata (StringMap& values) const { setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01); setBoolFlag (values, WavAudioFormat::acidRootSet, 0x02); @@ -782,22 +801,22 @@ namespace WavFileHelpers setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10); if (flags & 0x02) // root note set - values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote))); + values[WavAudioFormat::acidRootNote] = String (ByteOrder::swapIfBigEndian (rootNote)); - values.set (WavAudioFormat::acidBeats, String (ByteOrder::swapIfBigEndian (numBeats))); - values.set (WavAudioFormat::acidDenominator, String (ByteOrder::swapIfBigEndian (meterDenominator))); - values.set (WavAudioFormat::acidNumerator, String (ByteOrder::swapIfBigEndian (meterNumerator))); - values.set (WavAudioFormat::acidTempo, String (swapFloatByteOrder (tempo))); + values[WavAudioFormat::acidBeats] = String (ByteOrder::swapIfBigEndian (numBeats)); + values[WavAudioFormat::acidDenominator] = String (ByteOrder::swapIfBigEndian (meterDenominator)); + values[WavAudioFormat::acidNumerator] = String (ByteOrder::swapIfBigEndian (meterNumerator)); + values[WavAudioFormat::acidTempo] = String (swapFloatByteOrder (tempo)); } - void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const + void setBoolFlag (StringMap& values, const char* name, uint32 mask) const { - values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0"); + values[name] = (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0"; } - static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag) + static uint32 getFlagIfPresent (const StringMap& values, const char* name, uint32 flag) { - return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0; + return getValueWithDefault (values, name).getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0; } static float swapFloatByteOrder (const float x) noexcept @@ -826,10 +845,10 @@ namespace WavFileHelpers //============================================================================== struct TracktionChunk { - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryOutputStream out; - auto s = values[WavAudioFormat::tracktionLoopInfo]; + auto s = getValueWithDefault (values, WavAudioFormat::tracktionLoopInfo); if (s.isNotEmpty()) { @@ -846,7 +865,7 @@ namespace WavFileHelpers //============================================================================== namespace AXMLChunk { - static void addToMetadata (StringPairArray& destValues, const String& source) + static void addToMetadata (StringMap& destValues, const String& source) { if (auto xml = parseXML (source)) { @@ -861,7 +880,7 @@ namespace WavFileHelpers auto ISRCCode = xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true); if (ISRCCode.isNotEmpty()) - destValues.set (WavAudioFormat::ISRC, ISRCCode); + destValues[WavAudioFormat::ISRC] = ISRCCode; } } } @@ -869,9 +888,9 @@ namespace WavFileHelpers } } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { - auto ISRC = values.getValue (WavAudioFormat::ISRC, {}); + auto ISRC = getValueWithDefault (values, WavAudioFormat::ISRC); MemoryOutputStream xml; if (ISRC.isNotEmpty()) @@ -941,6 +960,8 @@ public: int cueLabelIndex = 0; int cueRegionIndex = 0; + StringMap dict; + auto streamStartPos = input->getPosition(); auto firstChunkType = input->readInt(); @@ -1019,7 +1040,7 @@ public: { input->skipNextBytes (4); // skip over size and bitsPerSample auto channelMask = input->readInt(); - metadataValues.set ("ChannelMask", String (channelMask)); + dict["ChannelMask"] = String (channelMask); channelLayout = getChannelLayoutFromMask (channelMask, numChannels); ExtensibleWavSubFormat subFormat; @@ -1074,34 +1095,34 @@ public: HeapBlock bwav; bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); input->read (bwav, (int) length); - bwav->copyTo (metadataValues, (int) length); + bwav->copyTo (dict, (int) length); } else if (chunkType == chunkName ("smpl")) { HeapBlock smpl; smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); input->read (smpl, (int) length); - smpl->copyTo (metadataValues, (int) length); + smpl->copyTo (dict, (int) length); } else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which... { HeapBlock inst; inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); input->read (inst, (int) length); - inst->copyTo (metadataValues); + inst->copyTo (dict); } else if (chunkType == chunkName ("cue ")) { HeapBlock cue; cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1); input->read (cue, (int) length); - cue->copyTo (metadataValues, (int) length); + cue->copyTo (dict, (int) length); } else if (chunkType == chunkName ("axml")) { MemoryBlock axml; input->readIntoMemoryBlock (axml, (ssize_t) length); - AXMLChunk::addToMetadata (metadataValues, axml.toString()); + AXMLChunk::addToMetadata (dict, axml.toString()); } else if (chunkType == chunkName ("LIST")) { @@ -1109,7 +1130,7 @@ public: if (subChunkType == chunkName ("info") || subChunkType == chunkName ("INFO")) { - ListInfoChunk::addToMetadata (metadataValues, *input, chunkEnd); + ListInfoChunk::addToMetadata (dict, *input, chunkEnd); } else if (subChunkType == chunkName ("adtl")) { @@ -1134,8 +1155,8 @@ public: MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, stringLength); - metadataValues.set (prefix + "Identifier", String (identifier)); - metadataValues.set (prefix + "Text", textBlock.toString()); + dict[prefix + "Identifier"] = String (identifier); + dict[prefix + "Text"] = textBlock.toString(); } else if (adtlChunkType == chunkName ("ltxt")) { @@ -1152,14 +1173,14 @@ public: MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, (int) stringLength); - metadataValues.set (prefix + "Identifier", String (identifier)); - metadataValues.set (prefix + "SampleLength", String (sampleLength)); - metadataValues.set (prefix + "Purpose", String (purpose)); - metadataValues.set (prefix + "Country", String (country)); - metadataValues.set (prefix + "Language", String (language)); - metadataValues.set (prefix + "Dialect", String (dialect)); - metadataValues.set (prefix + "CodePage", String (codePage)); - metadataValues.set (prefix + "Text", textBlock.toString()); + dict[prefix + "Identifier"] = String (identifier); + dict[prefix + "SampleLength"] = String (sampleLength); + dict[prefix + "Purpose"] = String (purpose); + dict[prefix + "Country"] = String (country); + dict[prefix + "Language"] = String (language); + dict[prefix + "Dialect"] = String (dialect); + dict[prefix + "CodePage"] = String (codePage); + dict[prefix + "Text"] = textBlock.toString(); } input->setPosition (adtlChunkEnd); @@ -1168,13 +1189,13 @@ public: } else if (chunkType == chunkName ("acid")) { - AcidChunk (*input, length).addToMetadata (metadataValues); + AcidChunk (*input, length).addToMetadata (dict); } else if (chunkType == chunkName ("Trkn")) { MemoryBlock tracktion; input->readIntoMemoryBlock (tracktion, (ssize_t) length); - metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString()); + dict[WavAudioFormat::tracktionLoopInfo] = tracktion.toString(); } else if (chunkEnd <= input->getPosition()) { @@ -1185,10 +1206,12 @@ public: } } - if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex)); - if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex)); - if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex)); - if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV"); + if (cueLabelIndex > 0) dict["NumCueLabels"] = String (cueLabelIndex); + if (cueNoteIndex > 0) dict["NumCueNotes"] = String (cueNoteIndex); + if (cueRegionIndex > 0) dict["NumCueRegions"] = String (cueRegionIndex); + if (dict.size() > 0) dict["MetaDataSource"] = "WAV"; + + metadataValues.addUnorderedMap (dict); } //============================================================================== @@ -1312,15 +1335,17 @@ public: // key should be removed (or set to "WAV") once this has been done jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF"); - bwavChunk = BWAVChunk::createFrom (metadataValues); - axmlChunk = AXMLChunk::createFrom (metadataValues); - smplChunk = SMPLChunk::createFrom (metadataValues); - instChunk = InstChunk::createFrom (metadataValues); - cueChunk = CueChunk ::createFrom (metadataValues); - listChunk = ListChunk::createFrom (metadataValues); - listInfoChunk = ListInfoChunk::createFrom (metadataValues); - acidChunk = AcidChunk::createFrom (metadataValues); - trckChunk = TracktionChunk::createFrom (metadataValues); + const auto map = toMap (metadataValues); + + bwavChunk = BWAVChunk::createFrom (map); + axmlChunk = AXMLChunk::createFrom (map); + smplChunk = SMPLChunk::createFrom (map); + instChunk = InstChunk::createFrom (map); + cueChunk = CueChunk ::createFrom (map); + listChunk = ListChunk::createFrom (map); + listInfoChunk = ListInfoChunk::createFrom (map); + acidChunk = AcidChunk::createFrom (map); + trckChunk = TracktionChunk::createFrom (map); } headerPosition = out->getPosition(); @@ -1792,7 +1817,7 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai if (bwavSize > 0) { - auto chunk = BWAVChunk::createFrom (newMetadata); + auto chunk = BWAVChunk::createFrom (toMap (newMetadata)); if (chunk.getSize() <= (size_t) bwavSize) { @@ -1834,31 +1859,34 @@ struct WaveAudioFormatTests : public UnitTest { beginTest ("Setting up metadata"); - StringPairArray metadataValues = WavAudioFormat::createBWAVMetadata ("description", - "originator", - "originatorRef", - Time::getCurrentTime(), - numTestAudioBufferSamples, - "codingHistory"); + auto metadataValues = toMap (WavAudioFormat::createBWAVMetadata ("description", + "originator", + "originatorRef", + Time::getCurrentTime(), + numTestAudioBufferSamples, + "codingHistory")); for (int i = numElementsInArray (WavFileHelpers::ListInfoChunk::types); --i >= 0;) - metadataValues.set (WavFileHelpers::ListInfoChunk::types[i], - WavFileHelpers::ListInfoChunk::types[i]); + metadataValues[WavFileHelpers::ListInfoChunk::types[i]] = WavFileHelpers::ListInfoChunk::types[i]; if (metadataValues.size() > 0) - metadataValues.set ("MetaDataSource", "WAV"); + metadataValues["MetaDataSource"] = "WAV"; - metadataValues.addArray (createDefaultSMPLMetadata()); + const auto smplMetadata = createDefaultSMPLMetadata(); + metadataValues.insert (smplMetadata.cbegin(), smplMetadata.cend()); WavAudioFormat format; MemoryBlock memoryBlock; + StringPairArray metadataArray; + metadataArray.addUnorderedMap (metadataValues); + { beginTest ("Creating a basic wave writer"); std::unique_ptr writer (format.createWriterFor (new MemoryOutputStream (memoryBlock, false), 44100.0, numTestAudioBufferChannels, - 32, metadataValues, 0)); + 32, metadataArray, 0)); expect (writer != nullptr); AudioBuffer buffer (numTestAudioBufferChannels, numTestAudioBufferSamples); @@ -1873,7 +1901,7 @@ struct WaveAudioFormatTests : public UnitTest std::unique_ptr reader (format.createReaderFor (new MemoryInputStream (memoryBlock, false), false)); expect (reader != nullptr); - expect (reader->metadataValues == metadataValues, "Somehow, the metadata is different!"); + expect (reader->metadataValues == metadataArray, "Somehow, the metadata is different!"); } } @@ -1884,19 +1912,19 @@ private: numTestAudioBufferSamples = 256 }; - StringPairArray createDefaultSMPLMetadata() const + static StringMap createDefaultSMPLMetadata() { - StringPairArray m; - - m.set ("Manufacturer", "0"); - m.set ("Product", "0"); - m.set ("SamplePeriod", "0"); - m.set ("MidiUnityNote", "60"); - m.set ("MidiPitchFraction", "0"); - m.set ("SmpteFormat", "0"); - m.set ("SmpteOffset", "0"); - m.set ("NumSampleLoops", "0"); - m.set ("SamplerData", "0"); + StringMap m; + + m["Manufacturer"] = "0"; + m["Product"] = "0"; + m["SamplePeriod"] = "0"; + m["MidiUnityNote"] = "60"; + m["MidiPitchFraction"] = "0"; + m["SmpteFormat"] = "0"; + m["SmpteOffset"] = "0"; + m["NumSampleLoops"] = "0"; + m["SamplerData"] = "0"; return m; }