|
|
@@ -220,7 +220,7 @@ namespace WavFileHelpers |
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MemoryBlock();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
} JUCE_PACKED;
|
|
|
@@ -366,12 +366,12 @@ namespace WavFileHelpers |
|
|
|
static MemoryBlock createFrom (const StringPairArray& values)
|
|
|
|
{
|
|
|
|
MemoryBlock data;
|
|
|
|
const StringArray& keys = values.getAllKeys();
|
|
|
|
auto& keys = values.getAllKeys();
|
|
|
|
|
|
|
|
if (keys.contains ("LowNote", true) && keys.contains ("HighNote", true))
|
|
|
|
{
|
|
|
|
data.setSize (8, true);
|
|
|
|
InstChunk* const inst = static_cast<InstChunk*> (data.getData());
|
|
|
|
auto* inst = static_cast<InstChunk*> (data.getData());
|
|
|
|
|
|
|
|
inst->baseNote = getValue (values, "MidiUnityNote", "60");
|
|
|
|
inst->detune = getValue (values, "Detune", "0");
|
|
|
@@ -434,7 +434,7 @@ namespace WavFileHelpers |
|
|
|
{
|
|
|
|
data.setSize (roundUpSize (sizeof (CueChunk) + (size_t) (numCues - 1) * sizeof (Cue)), true);
|
|
|
|
|
|
|
|
CueChunk* const c = static_cast<CueChunk*> (data.getData());
|
|
|
|
auto c = static_cast<CueChunk*> (data.getData());
|
|
|
|
|
|
|
|
c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues);
|
|
|
|
|
|
|
@@ -447,18 +447,18 @@ namespace WavFileHelpers |
|
|
|
|
|
|
|
for (int i = 0; i < numCues; ++i)
|
|
|
|
{
|
|
|
|
const String prefix ("Cue" + String (i));
|
|
|
|
const uint32 identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue();
|
|
|
|
auto prefix = "Cue" + String (i);
|
|
|
|
auto identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue();
|
|
|
|
|
|
|
|
#if JUCE_DEBUG
|
|
|
|
jassert (! identifiers.contains (identifier));
|
|
|
|
identifiers.add (identifier);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const int order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue();
|
|
|
|
auto order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue();
|
|
|
|
nextOrder = jmax (nextOrder, order) + 1;
|
|
|
|
|
|
|
|
Cue& cue = c->cues[i];
|
|
|
|
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());
|
|
|
@@ -489,9 +489,9 @@ namespace WavFileHelpers |
|
|
|
static void appendLabelOrNoteChunk (const StringPairArray& values, const String& prefix,
|
|
|
|
const int chunkType, MemoryOutputStream& out)
|
|
|
|
{
|
|
|
|
const String label (values.getValue (prefix + "Text", prefix));
|
|
|
|
const int labelLength = (int) label.getNumBytesAsUTF8() + 1;
|
|
|
|
const int chunkLength = 4 + labelLength + (labelLength & 1);
|
|
|
|
auto label = values.getValue (prefix + "Text", prefix);
|
|
|
|
auto labelLength = (int) label.getNumBytesAsUTF8() + 1;
|
|
|
|
auto chunkLength = 4 + labelLength + (labelLength & 1);
|
|
|
|
|
|
|
|
out.writeInt (chunkType);
|
|
|
|
out.writeInt (chunkLength);
|
|
|
@@ -504,9 +504,9 @@ namespace WavFileHelpers |
|
|
|
|
|
|
|
static void appendExtraChunk (const StringPairArray& values, const String& prefix, MemoryOutputStream& out)
|
|
|
|
{
|
|
|
|
const String text (values.getValue (prefix + "Text", prefix));
|
|
|
|
auto text = values.getValue (prefix + "Text", prefix);
|
|
|
|
|
|
|
|
const int textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator
|
|
|
|
auto textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator
|
|
|
|
int chunkLength = textLength + 20 + (textLength & 1);
|
|
|
|
|
|
|
|
out.writeInt (chunkName ("ltxt"));
|
|
|
@@ -526,9 +526,9 @@ namespace WavFileHelpers |
|
|
|
|
|
|
|
static MemoryBlock createFrom (const StringPairArray& values)
|
|
|
|
{
|
|
|
|
const int numCueLabels = getValue (values, "NumCueLabels");
|
|
|
|
const int numCueNotes = getValue (values, "NumCueNotes");
|
|
|
|
const int numCueRegions = getValue (values, "NumCueRegions");
|
|
|
|
auto numCueLabels = getValue (values, "NumCueLabels");
|
|
|
|
auto numCueNotes = getValue (values, "NumCueNotes");
|
|
|
|
auto numCueRegions = getValue (values, "NumCueRegions");
|
|
|
|
|
|
|
|
MemoryOutputStream out;
|
|
|
|
|
|
|
@@ -677,7 +677,7 @@ namespace WavFileHelpers |
|
|
|
|
|
|
|
static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName)
|
|
|
|
{
|
|
|
|
const String value (values.getValue (paramName, String()));
|
|
|
|
auto value = values.getValue (paramName, {});
|
|
|
|
|
|
|
|
if (value.isEmpty())
|
|
|
|
return false;
|
|
|
@@ -830,13 +830,13 @@ namespace WavFileHelpers |
|
|
|
|
|
|
|
if (xml != nullptr && xml->hasTagName ("ebucore:ebuCoreMain"))
|
|
|
|
{
|
|
|
|
if (XmlElement* xml2 = xml->getChildByName ("ebucore:coreMetadata"))
|
|
|
|
if (auto* xml2 = xml->getChildByName ("ebucore:coreMetadata"))
|
|
|
|
{
|
|
|
|
if (XmlElement* xml3 = xml2->getChildByName ("ebucore:identifier"))
|
|
|
|
if (auto* xml3 = xml2->getChildByName ("ebucore:identifier"))
|
|
|
|
{
|
|
|
|
if (XmlElement* xml4 = xml3->getChildByName ("dc:identifier"))
|
|
|
|
if (auto* xml4 = xml3->getChildByName ("dc:identifier"))
|
|
|
|
{
|
|
|
|
const String ISRCCode (xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true));
|
|
|
|
auto ISRCCode = xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true);
|
|
|
|
|
|
|
|
if (ISRCCode.isNotEmpty())
|
|
|
|
destValues.set (WavAudioFormat::ISRC, ISRCCode);
|
|
|
@@ -848,7 +848,7 @@ namespace WavFileHelpers |
|
|
|
|
|
|
|
static MemoryBlock createFrom (const StringPairArray& values)
|
|
|
|
{
|
|
|
|
const String ISRC (values.getValue (WavAudioFormat::ISRC, String()));
|
|
|
|
auto ISRC = values.getValue (WavAudioFormat::ISRC, {});
|
|
|
|
MemoryOutputStream xml;
|
|
|
|
|
|
|
|
if (ISRC.isNotEmpty())
|
|
|
@@ -911,11 +911,7 @@ class WavAudioFormatReader : public AudioFormatReader |
|
|
|
{
|
|
|
|
public:
|
|
|
|
WavAudioFormatReader (InputStream* const in)
|
|
|
|
: AudioFormatReader (in, wavFormatName),
|
|
|
|
bwavChunkStart (0),
|
|
|
|
bwavSize (0),
|
|
|
|
dataLength (0),
|
|
|
|
isRF64 (false)
|
|
|
|
: AudioFormatReader (in, wavFormatName)
|
|
|
|
{
|
|
|
|
using namespace WavFileHelpers;
|
|
|
|
uint64 len = 0;
|
|
|
@@ -941,7 +937,7 @@ public: |
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int64 startOfRIFFChunk = input->getPosition();
|
|
|
|
auto startOfRIFFChunk = input->getPosition();
|
|
|
|
|
|
|
|
if (input->readInt() == chunkName ("WAVE"))
|
|
|
|
{
|
|
|
@@ -968,10 +964,10 @@ public: |
|
|
|
if (chunkType == chunkName ("fmt "))
|
|
|
|
{
|
|
|
|
// read the format chunk
|
|
|
|
const unsigned short format = (unsigned short) input->readShort();
|
|
|
|
auto format = (unsigned short) input->readShort();
|
|
|
|
numChannels = (unsigned int) input->readShort();
|
|
|
|
sampleRate = input->readInt();
|
|
|
|
const int bytesPerSec = input->readInt();
|
|
|
|
auto bytesPerSec = input->readInt();
|
|
|
|
input->skipNextBytes (2);
|
|
|
|
bitsPerSample = (unsigned int) (int) input->readShort();
|
|
|
|
|
|
|
@@ -1197,15 +1193,16 @@ public: |
|
|
|
case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
|
|
|
case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
|
|
|
case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
|
|
|
|
else ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
|
|
|
else ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
|
|
|
|
break;
|
|
|
|
default: jassertfalse; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int64 bwavChunkStart, bwavSize;
|
|
|
|
int64 dataChunkStart, dataLength;
|
|
|
|
int bytesPerFrame;
|
|
|
|
bool isRF64;
|
|
|
|
int64 bwavChunkStart = 0, bwavSize = 0;
|
|
|
|
int64 dataChunkStart = 0, dataLength = 0;
|
|
|
|
int bytesPerFrame = 0;
|
|
|
|
bool isRF64 = false;
|
|
|
|
|
|
|
|
private:
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader)
|
|
|
@@ -1215,13 +1212,9 @@ private: |
|
|
|
class WavAudioFormatWriter : public AudioFormatWriter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
WavAudioFormatWriter (OutputStream* const out, const double rate,
|
|
|
|
const unsigned int numChans, const unsigned int bits,
|
|
|
|
const StringPairArray& metadataValues)
|
|
|
|
: AudioFormatWriter (out, wavFormatName, rate, numChans, bits),
|
|
|
|
lengthInSamples (0),
|
|
|
|
bytesWritten (0),
|
|
|
|
writeFailed (false)
|
|
|
|
WavAudioFormatWriter (OutputStream* out, double rate, unsigned int numChans,
|
|
|
|
unsigned int bits, const StringPairArray& metadataValues)
|
|
|
|
: AudioFormatWriter (out, wavFormatName, rate, numChans, bits)
|
|
|
|
{
|
|
|
|
using namespace WavFileHelpers;
|
|
|
|
|
|
|
@@ -1290,7 +1283,7 @@ public: |
|
|
|
|
|
|
|
bool flush() override
|
|
|
|
{
|
|
|
|
const int64 lastWritePos = output->getPosition();
|
|
|
|
auto lastWritePos = output->getPosition();
|
|
|
|
writeHeader();
|
|
|
|
|
|
|
|
if (output->setPosition (lastWritePos))
|
|
|
@@ -1304,9 +1297,9 @@ public: |
|
|
|
|
|
|
|
private:
|
|
|
|
MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk;
|
|
|
|
uint64 lengthInSamples, bytesWritten;
|
|
|
|
int64 headerPosition;
|
|
|
|
bool writeFailed;
|
|
|
|
uint64 lengthInSamples = 0, bytesWritten = 0;
|
|
|
|
int64 headerPosition = 0;
|
|
|
|
bool writeFailed = false;
|
|
|
|
|
|
|
|
static int getChannelMask (const int numChannels) noexcept
|
|
|
|
{
|
|
|
@@ -1496,7 +1489,7 @@ public: |
|
|
|
|
|
|
|
void getSample (int64 sample, float* result) const noexcept override
|
|
|
|
{
|
|
|
|
const int num = (int) numChannels;
|
|
|
|
auto num = (int) numChannels;
|
|
|
|
|
|
|
|
if (map == nullptr || ! mappedSection.contains (sample))
|
|
|
|
{
|
|
|
@@ -1506,8 +1499,8 @@ public: |
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
float** dest = &result;
|
|
|
|
const void* source = sampleToPointer (sample);
|
|
|
|
auto dest = &result;
|
|
|
|
auto source = sampleToPointer (sample);
|
|
|
|
|
|
|
|
switch (bitsPerSample)
|
|
|
|
{
|
|
|
@@ -1515,7 +1508,8 @@ public: |
|
|
|
case 16: ReadHelper<AudioData::Float32, AudioData::Int16, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
|
|
|
|
case 24: ReadHelper<AudioData::Float32, AudioData::Int24, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
|
|
|
|
case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num);
|
|
|
|
else ReadHelper<AudioData::Float32, AudioData::Int32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num); break;
|
|
|
|
else ReadHelper<AudioData::Float32, AudioData::Int32, AudioData::LittleEndian>::read (dest, 0, 1, source, 1, num);
|
|
|
|
break;
|
|
|
|
default: jassertfalse; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -1540,7 +1534,8 @@ public: |
|
|
|
case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, results, numChannelsToRead); break;
|
|
|
|
case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, results, numChannelsToRead); break;
|
|
|
|
case 32: if (usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, results, numChannelsToRead);
|
|
|
|
else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, results, numChannelsToRead); break;
|
|
|
|
else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, results, numChannelsToRead);
|
|
|
|
break;
|
|
|
|
default: jassertfalse; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -1626,21 +1621,15 @@ namespace WavFileHelpers |
|
|
|
static bool slowCopyWavFileWithNewMetadata (const File& file, const StringPairArray& metadata)
|
|
|
|
{
|
|
|
|
TemporaryFile tempFile (file);
|
|
|
|
|
|
|
|
WavAudioFormat wav;
|
|
|
|
ScopedPointer<AudioFormatReader> reader (wav.createReaderFor (file.createInputStream(), true));
|
|
|
|
|
|
|
|
if (reader != nullptr)
|
|
|
|
if (ScopedPointer<AudioFormatReader> reader = wav.createReaderFor (file.createInputStream(), true))
|
|
|
|
{
|
|
|
|
ScopedPointer<OutputStream> outStream (tempFile.getFile().createOutputStream());
|
|
|
|
|
|
|
|
if (outStream != nullptr)
|
|
|
|
if (ScopedPointer<OutputStream> outStream = tempFile.getFile().createOutputStream())
|
|
|
|
{
|
|
|
|
ScopedPointer<AudioFormatWriter> writer (wav.createWriterFor (outStream, reader->sampleRate,
|
|
|
|
reader->numChannels, (int) reader->bitsPerSample,
|
|
|
|
metadata, 0));
|
|
|
|
|
|
|
|
if (writer != nullptr)
|
|
|
|
if (ScopedPointer<AudioFormatWriter> writer = wav.createWriterFor (outStream, reader->sampleRate,
|
|
|
|
reader->numChannels, (int) reader->bitsPerSample,
|
|
|
|
metadata, 0))
|
|
|
|
{
|
|
|
|
outStream.release();
|
|
|
|
|
|
|
@@ -1660,12 +1649,11 @@ namespace WavFileHelpers |
|
|
|
bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata)
|
|
|
|
{
|
|
|
|
using namespace WavFileHelpers;
|
|
|
|
ScopedPointer<WavAudioFormatReader> reader (static_cast<WavAudioFormatReader*> (createReaderFor (wavFile.createInputStream(), true)));
|
|
|
|
|
|
|
|
if (reader != nullptr)
|
|
|
|
if (ScopedPointer<WavAudioFormatReader> reader = static_cast<WavAudioFormatReader*> (createReaderFor (wavFile.createInputStream(), true)))
|
|
|
|
{
|
|
|
|
const int64 bwavPos = reader->bwavChunkStart;
|
|
|
|
const int64 bwavSize = reader->bwavSize;
|
|
|
|
auto bwavPos = reader->bwavChunkStart;
|
|
|
|
auto bwavSize = reader->bwavSize;
|
|
|
|
reader = nullptr;
|
|
|
|
|
|
|
|
if (bwavSize > 0)
|
|
|
@@ -1675,7 +1663,7 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai |
|
|
|
if (chunk.getSize() <= (size_t) bwavSize)
|
|
|
|
{
|
|
|
|
// the new one will fit in the space available, so write it directly..
|
|
|
|
const int64 oldSize = wavFile.getSize();
|
|
|
|
auto oldSize = wavFile.getSize();
|
|
|
|
|
|
|
|
{
|
|
|
|
FileOutputStream out (wavFile);
|
|
|
@@ -1701,9 +1689,8 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai |
|
|
|
//==============================================================================
|
|
|
|
#if JUCE_UNIT_TESTS
|
|
|
|
|
|
|
|
class WaveAudioFormatTests : public UnitTest
|
|
|
|
struct WaveAudioFormatTests : public UnitTest
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
WaveAudioFormatTests() : UnitTest ("Wave audio format tests") {}
|
|
|
|
|
|
|
|
void runTest() override
|
|
|
|