diff --git a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 1da4e0379e..c90af056f3 100644 --- a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -65,6 +65,7 @@ const char* const WavAudioFormat::acidNumerator = "acid numerator"; const char* const WavAudioFormat::acidTempo = "acid tempo"; const char* const WavAudioFormat::ISRC = "ISRC"; +const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; //============================================================================== namespace WavFileHelpers @@ -490,30 +491,100 @@ namespace WavFileHelpers setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10); if (flags & 0x02) // root note set - values.set (WavAudioFormat::acidRootNote, String (rootNote)); + values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote))); - values.set (WavAudioFormat::acidBeats, String (numBeats)); - values.set (WavAudioFormat::acidDenominator, String (meterDenominator)); - values.set (WavAudioFormat::acidNumerator, String (meterNumerator)); - values.set (WavAudioFormat::acidTempo, String (tempo)); + 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))); } - void setBoolFlag (StringPairArray& values, const char* name, int32 mask) const + void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const { - values.set (name, (flags & mask) ? "1" : "0"); + values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0"); } - int32 flags; - int16 rootNote; - int16 reserved1; + template + static void setIntFlagIfPresent (IntType& flag, const StringPairArray& values, const char* name) + { + if (values.containsKey (name)) + flag = ByteOrder::swapIfBigEndian ((IntType) values[name].getIntValue()); + } + + static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag) + { + return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0; + } + + static float swapFloatByteOrder (const float x) noexcept + { + #ifdef JUCE_BIG_ENDIAN + union { uint32 asInt; float asFloat; } n; + n.asFloat = x; + n.asInt = ByteOrder::swap (n.asInt); + return n.asFloat; + #else + return x; + #endif + } + + static MemoryBlock createFrom (const StringPairArray& values) + { + MemoryBlock data (sizeof (AcidChunk), true); + AcidChunk* const acid = static_cast (data.getData()); + + acid->flags = getFlagIfPresent (values, WavAudioFormat::acidOneShot, 0x01) + | getFlagIfPresent (values, WavAudioFormat::acidRootSet, 0x02) + | getFlagIfPresent (values, WavAudioFormat::acidStretch, 0x04) + | getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08) + | getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10); + + if (values[WavAudioFormat::acidRootSet].getIntValue() != 0) + setIntFlagIfPresent (acid->rootNote, values, WavAudioFormat::acidRootNote); + + setIntFlagIfPresent (acid->numBeats, values, WavAudioFormat::acidBeats); + setIntFlagIfPresent (acid->meterDenominator, values, WavAudioFormat::acidDenominator); + setIntFlagIfPresent (acid->meterNumerator, values, WavAudioFormat::acidNumerator); + + if (values.containsKey (WavAudioFormat::acidTempo)) + acid->tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue()); + + if (acid->flags == 0 && acid->rootNote == 0 && acid->numBeats == 0 + && acid->meterDenominator == 0 && acid->meterNumerator == 0) + return MemoryBlock(); + + return data; + } + + uint32 flags; + uint16 rootNote; + uint16 reserved1; float reserved2; - int32 numBeats; - int16 meterDenominator; - int16 meterNumerator; + uint32 numBeats; + uint16 meterDenominator; + uint16 meterNumerator; float tempo; } JUCE_PACKED; + //============================================================================== + struct TracktionChunk + { + static MemoryBlock createFrom (const StringPairArray& values) + { + const String s = values[WavAudioFormat::tracktionLoopInfo]; + MemoryBlock data; + + if (s.isNotEmpty()) + { + MemoryOutputStream os (data, false); + os.writeString (s); + } + + return data; + } + }; + //============================================================================== namespace AXMLChunk { @@ -816,6 +887,12 @@ public: { AcidChunk (*input, length).addToMetadata (metadataValues); } + else if (chunkType == chunkName ("Trkn")) + { + MemoryBlock tracktion; + input->readIntoMemoryBlock (tracktion, (ssize_t) length); + metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString()); + } else if (chunkEnd <= input->getPosition()) { break; @@ -919,6 +996,8 @@ public: instChunk = InstChunk::createFrom (metadataValues); cueChunk = CueChunk ::createFrom (metadataValues); listChunk = ListChunk::createFrom (metadataValues); + acidChunk = AcidChunk::createFrom (metadataValues); + trckChunk = TracktionChunk::createFrom (metadataValues); } headerPosition = out->getPosition(); @@ -981,7 +1060,7 @@ public: } private: - MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk; + MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, acidChunk, trckChunk; uint64 lengthInSamples, bytesWritten; int64 headerPosition; bool writeFailed; @@ -1033,6 +1112,8 @@ private: + chunkSize (instChunk) + chunkSize (cueChunk) + chunkSize (listChunk) + + chunkSize (acidChunk) + + chunkSize (trckChunk) + (8 + 28)); // (ds64 chunk) riffChunkSize += (riffChunkSize & 1); @@ -1112,6 +1193,8 @@ private: writeChunk (instChunk, chunkName ("inst"), 7); writeChunk (cueChunk, chunkName ("cue ")); writeChunk (listChunk, chunkName ("LIST")); + writeChunk (acidChunk, chunkName ("acid")); + writeChunk (trckChunk, chunkName ("Trkn")); writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); diff --git a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h index f80695c0a1..62de05a897 100644 --- a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h +++ b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h @@ -135,6 +135,9 @@ public: /** Metadata property name used when reading an ISRC code from an AXML chunk. */ static const char* const ISRC; + /** Metadata property name used when reading a WAV file with a Tracktion chunk. */ + static const char* const tracktionLoopInfo; + //============================================================================== Array getPossibleSampleRates() override; Array getPossibleBitDepths() override; diff --git a/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index 2c644e947b..5db3149eff 100644 --- a/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -176,7 +176,7 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer, void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, Range* const results, const int channelsToRead) { - jassert (channelsToRead > 0 && channelsToRead <= numChannels); + jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels); if (numSamples <= 0) {