From 8ade855f5680a7182c47385ec745b9629d2fac6e Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Thu, 20 Jan 2011 22:25:15 +0000 Subject: [PATCH] Added RF64 support to WavAudioFormat. Extended some of the audio source classes to support 64-bit sample indices. --- juce_amalgamated.cpp | 413 +++++++++++------- juce_amalgamated.h | 35 +- .../juce_AiffAudioFormat.cpp | 4 - .../juce_WavAudioFormat.cpp | 355 +++++++++------ .../juce_AudioFormatReaderSource.cpp | 14 +- .../juce_AudioFormatReaderSource.h | 8 +- .../juce_AudioTransportSource.cpp | 14 +- .../audio_sources/juce_AudioTransportSource.h | 6 +- .../juce_BufferingAudioSource.cpp | 22 +- .../audio_sources/juce_BufferingAudioSource.h | 10 +- .../juce_PositionableAudioSource.h | 6 +- src/audio/dsp/juce_AudioSampleBuffer.cpp | 2 +- src/audio/dsp/juce_AudioSampleBuffer.h | 2 +- src/io/streams/juce_OutputStream.cpp | 6 + src/io/streams/juce_OutputStream.h | 3 + 15 files changed, 556 insertions(+), 344 deletions(-) diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index d5e7f2e9df..355c5cd6e1 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -6927,6 +6927,12 @@ void OutputStream::writeByte (char byte) write (&byte, 1); } +void OutputStream::writeRepeatedByte (uint8 byte, int numTimesToRepeat) +{ + while (--numTimesToRepeat >= 0) + writeByte (byte); +} + void OutputStream::writeShort (short value) { const unsigned short v = ByteOrder::swapIfBigEndian ((unsigned short) value); @@ -20729,10 +20735,6 @@ public: } } - ~AiffAudioFormatReader() - { - } - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { @@ -23091,6 +23093,9 @@ const StringPairArray WavAudioFormat::createBWAVMetadata (const String& descript return m; } +namespace WavFileHelpers +{ + #if JUCE_MSVC #pragma pack (push, 1) #define PACKED @@ -23265,12 +23270,26 @@ struct ExtensibleWavSubFormat uint8 data4[8]; } PACKED; +struct DataSize64Chunk // chunk ID = 'ds64' if data size > 0xffffffff, 'JUNK' otherwise +{ + uint32 riffSizeLow; // low 4 byte size of RF64 block + uint32 riffSizeHigh; // high 4 byte size of RF64 block + uint32 dataSizeLow; // low 4 byte size of data chunk + uint32 dataSizeHigh; // high 4 byte size of data chunk + uint32 sampleCountLow; // low 4 byte sample count of fact chunk + uint32 sampleCountHigh; // high 4 byte sample count of fact chunk + uint32 tableLength; // number of valid entries in array 'table' +} PACKED; + #if JUCE_MSVC #pragma pack (pop) #endif #undef PACKED + inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } +} + class WavAudioFormatReader : public AudioFormatReader { public: @@ -23279,109 +23298,146 @@ public: : AudioFormatReader (in, TRANS (wavFormatName)), bwavChunkStart (0), bwavSize (0), - dataLength (0) + dataLength (0), + isRF64 (false) { - if (input->readInt() == chunkName ("RIFF")) + using namespace WavFileHelpers; + uint64 len = 0; + int64 end = 0; + bool hasGotType = false; + bool hasGotData = false; + + const int firstChunkType = input->readInt(); + + if (firstChunkType == chunkName ("RF64")) { - const uint32 len = (uint32) input->readInt(); - const int64 end = input->getPosition() + len; - bool hasGotType = false; - bool hasGotData = false; + input->skipNextBytes (4); // size is -1 for RF64 + isRF64 = true; + } + else if (firstChunkType == chunkName ("RIFF")) + { + len = (uint64) input->readInt(); + end = input->getPosition() + len; + } + else + { + return; + } - if (input->readInt() == chunkName ("WAVE")) + const int64 startOfRIFFChunk = input->getPosition(); + + if (input->readInt() == chunkName ("WAVE")) + { + if (isRF64 && input->readInt() == chunkName ("ds64")) { - while (input->getPosition() < end - && ! input->isExhausted()) + uint32 length = (uint32) input->readInt(); + + if (length < 28) + { + return; + } + else { - const int chunkType = input->readInt(); - uint32 length = (uint32) input->readInt(); const int64 chunkEnd = input->getPosition() + length + (length & 1); + len = input->readInt64(); + end = startOfRIFFChunk + len; + dataLength = input->readInt64(); + input->setPosition (chunkEnd); + } + } - if (chunkType == chunkName ("fmt ")) - { - // read the format chunk - const unsigned short format = input->readShort(); - const short numChans = input->readShort(); - sampleRate = input->readInt(); - const int bytesPerSec = input->readInt(); + while (input->getPosition() < end && ! input->isExhausted()) + { + const int chunkType = input->readInt(); + uint32 length = (uint32) input->readInt(); + const int64 chunkEnd = input->getPosition() + length + (length & 1); - numChannels = numChans; - bytesPerFrame = bytesPerSec / (int)sampleRate; - bitsPerSample = 8 * bytesPerFrame / numChans; + if (chunkType == chunkName ("fmt ")) + { + // read the format chunk + const unsigned short format = input->readShort(); + const short numChans = input->readShort(); + sampleRate = input->readInt(); + const int bytesPerSec = input->readInt(); + + numChannels = numChans; + bytesPerFrame = bytesPerSec / (int)sampleRate; + bitsPerSample = 8 * bytesPerFrame / numChans; - if (format == 3) + if (format == 3) + { + usesFloatingPointData = true; + } + else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/) + { + if (length < 40) // too short { - usesFloatingPointData = true; + bytesPerFrame = 0; } - else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/) + else { - if (length < 40) // too short - { - bytesPerFrame = 0; - } - else - { - input->skipNextBytes (12); // skip over blockAlign, bitsPerSample and speakerPosition mask - ExtensibleWavSubFormat subFormat; - subFormat.data1 = input->readInt(); - subFormat.data2 = input->readShort(); - subFormat.data3 = input->readShort(); - input->read (subFormat.data4, sizeof (subFormat.data4)); + input->skipNextBytes (12); // skip over blockAlign, bitsPerSample and speakerPosition mask + ExtensibleWavSubFormat subFormat; + subFormat.data1 = input->readInt(); + subFormat.data2 = input->readShort(); + subFormat.data3 = input->readShort(); + input->read (subFormat.data4, sizeof (subFormat.data4)); - const ExtensibleWavSubFormat pcmFormat - = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + const ExtensibleWavSubFormat pcmFormat + = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0) - { - const ExtensibleWavSubFormat ambisonicFormat - = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } }; + if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0) + { + const ExtensibleWavSubFormat ambisonicFormat + = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } }; - if (memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0) - bytesPerFrame = 0; - } + if (memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0) + bytesPerFrame = 0; } } - else if (format != 1) - { - bytesPerFrame = 0; - } - - hasGotType = true; } - else if (chunkType == chunkName ("data")) + else if (format != 1) { - // get the data chunk's position + bytesPerFrame = 0; + } + + hasGotType = true; + } + else if (chunkType == chunkName ("data")) + { + // get the data chunk's position + if (! isRF64) // data size is expected to be -1, actual data size is in ds64 chunk dataLength = length; - dataChunkStart = input->getPosition(); - lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0; - hasGotData = true; - } - else if (chunkType == chunkName ("bext")) - { - bwavChunkStart = input->getPosition(); - bwavSize = length; - - // Broadcast-wav extension chunk.. - HeapBlock bwav; - bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); - input->read (bwav, length); - bwav->copyTo (metadataValues); - } - else if (chunkType == chunkName ("smpl")) - { - HeapBlock smpl; - smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); - input->read (smpl, length); - smpl->copyTo (metadataValues, length); - } - else if (chunkEnd <= input->getPosition()) - { - break; - } + dataChunkStart = input->getPosition(); + lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0; - input->setPosition (chunkEnd); + hasGotData = true; + } + else if (chunkType == chunkName ("bext")) + { + bwavChunkStart = input->getPosition(); + bwavSize = length; + + // Broadcast-wav extension chunk.. + HeapBlock bwav; + bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); + input->read (bwav, length); + bwav->copyTo (metadataValues); } + else if (chunkType == chunkName ("smpl")) + { + HeapBlock smpl; + smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); + input->read (smpl, length); + smpl->copyTo (metadataValues, length); + } + else if (chunkEnd <= input->getPosition()) + { + break; + } + + input->setPosition (chunkEnd); } } } @@ -23443,8 +23499,7 @@ private: ScopedPointer converter; int bytesPerFrame; int64 dataChunkStart, dataLength; - - static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } + bool isRF64; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader); }; @@ -23453,20 +23508,17 @@ class WavAudioFormatWriter : public AudioFormatWriter { public: - WavAudioFormatWriter (OutputStream* const out, - const double sampleRate_, - const unsigned int numChannels_, - const int bits, + WavAudioFormatWriter (OutputStream* const out, const double sampleRate_, + const unsigned int numChannels_, const int bits, const StringPairArray& metadataValues) - : AudioFormatWriter (out, - TRANS (wavFormatName), - sampleRate_, - numChannels_, - bits), + : AudioFormatWriter (out, TRANS (wavFormatName), sampleRate_, numChannels_, bits), lengthInSamples (0), bytesWritten (0), - writeFailed (false) + writeFailed (false), + isRF64 (false) { + using namespace WavFileHelpers; + if (metadataValues.size() > 0) { bwavChunk = BWAVChunk::createFrom (metadataValues); @@ -23479,6 +23531,12 @@ public: ~WavAudioFormatWriter() { + if ((bytesWritten & 1) != 0) // pad to an even length + { + ++bytesWritten; + output->writeByte (0); + } + writeHeader(); } @@ -23501,8 +23559,7 @@ public: default: jassertfalse; break; } - if (bytesWritten + bytes >= (uint32) 0xfff00000 - || ! output->write (tempBlock.getData(), bytes)) + if (! output->write (tempBlock.getData(), bytes)) { // failed to write to disk, so let's try writing the header. // If it's just run out of disk space, then if it does manage @@ -23523,14 +23580,27 @@ public: private: ScopedPointer converter; MemoryBlock tempBlock, bwavChunk, smplChunk; - uint32 lengthInSamples, bytesWritten; + uint64 lengthInSamples, bytesWritten; int64 headerPosition; - bool writeFailed; + bool writeFailed, isRF64; - static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } + static int getChannelMask (const int numChannels) throw() + { + switch (numChannels) + { + case 1: return 0; + case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT + case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + case 6: return 1 + 2 + 4 + 8 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + default: break; + } + + return 0; + } void writeHeader() { + using namespace WavFileHelpers; const bool seekedOk = output->setPosition (headerPosition); (void) seekedOk; @@ -23539,21 +23609,62 @@ private: jassert (seekedOk); const int bytesPerFrame = numChannels * bitsPerSample / 8; - output->writeInt (chunkName ("RIFF")); - output->writeInt ((int) (lengthInSamples * bytesPerFrame - + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36) - + (smplChunk.getSize() > 0 ? smplChunk.getSize() + 8 : 0))); + int64 audioDataSize = bytesPerFrame * lengthInSamples; + + int64 riffChunkSize = 4 /* 'WAVE' */ + 8 + 40 /* WAVEFORMATEX */ + + 8 + audioDataSize + (audioDataSize & 1) + + (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0) + + (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0) + + (8 + 28); // (JUNK chunk) + + riffChunkSize += (riffChunkSize & 0x1); + isRF64 = (riffChunkSize > 0xffffffff); + output->writeInt (chunkName (isRF64 ? "RF64" : "RIFF")); + output->writeInt (isRF64 ? -1 : (int) riffChunkSize); output->writeInt (chunkName ("WAVE")); + + if (! isRF64) + { + // write Junk chunk + output->writeInt (chunkName ("JUNK")); + output->writeInt (28); + output->writeRepeatedByte (0, 28); + } + else + { + // write ds64 chunk + output->writeInt (chunkName ("ds64")); + output->writeInt (28); // chunk size for uncompressed data (no table) + output->writeInt64 (riffChunkSize); + output->writeInt64 (audioDataSize); + output->writeRepeatedByte (0, 12); + } + output->writeInt (chunkName ("fmt ")); - output->writeInt (16); - output->writeShort ((bitsPerSample < 32) ? (short) 1 /*WAVE_FORMAT_PCM*/ - : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); + output->writeInt (40); // WAVEFORMATEX chunk size + output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE output->writeShort ((short) numChannels); output->writeInt ((int) sampleRate); - output->writeInt (bytesPerFrame * (int) sampleRate); - output->writeShort ((short) bytesPerFrame); - output->writeShort ((short) bitsPerSample); + output->writeInt ((int) (bytesPerFrame * sampleRate)); // nAvgBytesPerSec + output->writeShort ((short) bytesPerFrame); // nBlockAlign + output->writeShort ((short) bitsPerSample); // wBitsPerSample + output->writeShort (22); // cbSize (size of the extension) + output->writeShort ((short) bitsPerSample); // wValidBitsPerSample + output->writeInt (getChannelMask (numChannels)); + + const ExtensibleWavSubFormat pcmFormat + = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + + const ExtensibleWavSubFormat IEEEFloatFormat + = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + + const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat; + + output->writeInt ((int) subFormat.data1); + output->writeShort ((short) subFormat.data2); + output->writeShort ((short) subFormat.data3); + output->write (subFormat.data4, sizeof (subFormat.data4)); if (bwavChunk.getSize() > 0) { @@ -23570,7 +23681,7 @@ private: } output->writeInt (chunkName ("data")); - output->writeInt (lengthInSamples * bytesPerFrame); + output->writeInt (isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); usesFloatingPointData = (bitsPerSample == 32); } @@ -23616,28 +23727,19 @@ AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, return 0; } -AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, - double sampleRate, - unsigned int numChannels, - int bitsPerSample, - const StringPairArray& metadataValues, - int /*qualityOptionIndex*/) +AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate, + unsigned int numChannels, int bitsPerSample, + const StringPairArray& metadataValues, int /*qualityOptionIndex*/) { if (getPossibleBitDepths().contains (bitsPerSample)) - { - return new WavAudioFormatWriter (out, - sampleRate, - numChannels, - bitsPerSample, - metadataValues); - } + return new WavAudioFormatWriter (out, sampleRate, numChannels, bitsPerSample, metadataValues); return 0; } -namespace +namespace WavFileHelpers { - bool juce_slowCopyOfWavFileWithNewMetadata (const File& file, const StringPairArray& metadata) + bool slowCopyWavFileWithNewMetadata (const File& file, const StringPairArray& metadata) { TemporaryFile tempFile (file); @@ -23673,7 +23775,8 @@ namespace bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata) { - ScopedPointer reader ((WavAudioFormatReader*) createReaderFor (wavFile.createInputStream(), true)); + using namespace WavFileHelpers; + ScopedPointer reader (static_cast (createReaderFor (wavFile.createInputStream(), true))); if (reader != 0) { @@ -23704,7 +23807,7 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai } } - return juce_slowCopyOfWavFileWithNewMetadata (wavFile, newMetadata); + return slowCopyWavFileWithNewMetadata (wavFile, newMetadata); } END_JUCE_NAMESPACE @@ -23773,7 +23876,7 @@ AudioFormatReaderSource::~AudioFormatReaderSource() delete reader; } -void AudioFormatReaderSource::setNextReadPosition (int newPosition) +void AudioFormatReaderSource::setNextReadPosition (int64 newPosition) { nextPlayPos = newPosition; } @@ -23783,15 +23886,15 @@ void AudioFormatReaderSource::setLooping (bool shouldLoop) looping = shouldLoop; } -int AudioFormatReaderSource::getNextReadPosition() const +int64 AudioFormatReaderSource::getNextReadPosition() const { - return (looping) ? (nextPlayPos % (int) reader->lengthInSamples) - : nextPlayPos; + return looping ? nextPlayPos % reader->lengthInSamples + : nextPlayPos; } -int AudioFormatReaderSource::getTotalLength() const +int64 AudioFormatReaderSource::getTotalLength() const { - return (int) reader->lengthInSamples; + return reader->lengthInSamples; } void AudioFormatReaderSource::prepareToPlay (int /*samplesPerBlockExpected*/, @@ -23807,7 +23910,7 @@ void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& i { if (info.numSamples > 0) { - const int start = nextPlayPos; + const int64 start = nextPlayPos; if (looping) { @@ -24152,7 +24255,7 @@ void AudioTransportSource::stop() void AudioTransportSource::setPosition (double newPosition) { if (sampleRate > 0.0) - setNextReadPosition (roundToInt (newPosition * sampleRate)); + setNextReadPosition ((int64) (newPosition * sampleRate)); } double AudioTransportSource::getCurrentPosition() const @@ -24168,30 +24271,30 @@ double AudioTransportSource::getLengthInSeconds() const return getTotalLength() / sampleRate; } -void AudioTransportSource::setNextReadPosition (int newPosition) +void AudioTransportSource::setNextReadPosition (int64 newPosition) { if (positionableSource != 0) { if (sampleRate > 0 && sourceSampleRate > 0) - newPosition = roundToInt (newPosition * sourceSampleRate / sampleRate); + newPosition = (int64) (newPosition * sourceSampleRate / sampleRate); positionableSource->setNextReadPosition (newPosition); } } -int AudioTransportSource::getNextReadPosition() const +int64 AudioTransportSource::getNextReadPosition() const { if (positionableSource != 0) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; - return roundToInt (positionableSource->getNextReadPosition() * ratio); + return (int64) (positionableSource->getNextReadPosition() * ratio); } return 0; } -int AudioTransportSource::getTotalLength() const +int64 AudioTransportSource::getTotalLength() const { const ScopedLock sl (callbackLock); @@ -24199,7 +24302,7 @@ int AudioTransportSource::getTotalLength() const { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; - return roundToInt (positionableSource->getTotalLength() * ratio); + return (int64) (positionableSource->getTotalLength() * ratio); } return 0; @@ -24446,8 +24549,8 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info { const ScopedLock sl (bufferStartPosLock); - const int validStart = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos; - const int validEnd = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos; + const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos); + const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos); if (validStart == validEnd) { @@ -24506,14 +24609,14 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info thread->notify(); } -int BufferingAudioSource::getNextReadPosition() const +int64 BufferingAudioSource::getNextReadPosition() const { return (source->isLooping() && nextPlayPos > 0) ? nextPlayPos % source->getTotalLength() : nextPlayPos; } -void BufferingAudioSource::setNextReadPosition (int newPosition) +void BufferingAudioSource::setNextReadPosition (int64 newPosition) { const ScopedLock sl (bufferStartPosLock); @@ -24527,7 +24630,7 @@ void BufferingAudioSource::setNextReadPosition (int newPosition) bool BufferingAudioSource::readNextBufferChunk() { - int newBVS, newBVE, sectionToReadStart, sectionToReadEnd; + int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd; { const ScopedLock sl (bufferStartPosLock); @@ -24539,7 +24642,7 @@ bool BufferingAudioSource::readNextBufferChunk() bufferValidEnd = 0; } - newBVS = jmax (0, nextPlayPos); + newBVS = jmax ((int64) 0, nextPlayPos); newBVE = newBVS + buffer.getNumSamples() - 4; sectionToReadStart = 0; sectionToReadEnd = 0; @@ -24556,8 +24659,8 @@ bool BufferingAudioSource::readNextBufferChunk() bufferValidStart = 0; bufferValidEnd = 0; } - else if (abs (newBVS - bufferValidStart) > 512 - || abs (newBVE - bufferValidEnd) > 512) + else if (std::abs ((int) (newBVS - bufferValidStart)) > 512 + || std::abs ((int) (newBVE - bufferValidEnd)) > 512) { newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize); @@ -24577,7 +24680,7 @@ bool BufferingAudioSource::readNextBufferChunk() if (bufferIndexStart < bufferIndexEnd) { readBufferSection (sectionToReadStart, - sectionToReadEnd - sectionToReadStart, + (int) (sectionToReadEnd - sectionToReadStart), bufferIndexStart); } else @@ -24589,7 +24692,7 @@ bool BufferingAudioSource::readNextBufferChunk() bufferIndexStart); readBufferSection (sectionToReadStart + initialSize, - (sectionToReadEnd - sectionToReadStart) - initialSize, + (int) (sectionToReadEnd - sectionToReadStart) - initialSize, 0); } @@ -24606,7 +24709,7 @@ bool BufferingAudioSource::readNextBufferChunk() } } -void BufferingAudioSource::readBufferSection (int start, int length, int bufferOffset) +void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset) { if (source->getNextReadPosition() != start) source->setNextReadPosition (start); @@ -27565,7 +27668,7 @@ float AudioSampleBuffer::getRMSLevel (const int channel, void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader, const int startSample, const int numSamples, - const int readerStartSample, + const int64 readerStartSample, const bool useLeftChan, const bool useRightChan) { diff --git a/juce_amalgamated.h b/juce_amalgamated.h index c46147a1bf..5576dd18ad 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -5962,6 +5962,9 @@ public: */ virtual void writeDoubleBigEndian (double value); + /** Writes a byte to the output stream a given number of times. */ + virtual void writeRepeatedByte (uint8 byte, int numTimesToRepeat); + /** Writes a condensed binary encoding of a 32-bit integer. If you're storing a lot of integers which are unlikely to have very large values, @@ -32536,7 +32539,7 @@ public: void readFromAudioReader (AudioFormatReader* reader, int startSample, int numSamples, - int readerStartSample, + int64 readerStartSample, bool useReaderLeftChan, bool useReaderRightChan); @@ -34174,16 +34177,16 @@ public: Note that this may be called on a different thread to getNextAudioBlock(), so the subclass should make sure it's synchronised. */ - virtual void setNextReadPosition (int newPosition) = 0; + virtual void setNextReadPosition (int64 newPosition) = 0; /** Returns the position from which the next block will be returned. @see setNextReadPosition */ - virtual int getNextReadPosition() const = 0; + virtual int64 getNextReadPosition() const = 0; /** Returns the total length of the stream (in samples). */ - virtual int getTotalLength() const = 0; + virtual int64 getTotalLength() const = 0; /** Returns true if this source is actually playing in a loop. */ virtual bool isLooping() const = 0; @@ -34242,20 +34245,20 @@ public: void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int newPosition); + void setNextReadPosition (int64 newPosition); /** Implements the PositionableAudioSource method. */ - int getNextReadPosition() const; + int64 getNextReadPosition() const; /** Implements the PositionableAudioSource method. */ - int getTotalLength() const; + int64 getTotalLength() const; private: AudioFormatReader* reader; bool deleteReader; - int volatile nextPlayPos; + int64 volatile nextPlayPos; bool volatile looping; void readBufferSection (int start, int length, AudioSampleBuffer& buffer, int startSample); @@ -34707,13 +34710,13 @@ public: void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int newPosition); + void setNextReadPosition (int64 newPosition); /** Implements the PositionableAudioSource method. */ - int getNextReadPosition() const; + int64 getNextReadPosition() const; /** Implements the PositionableAudioSource method. */ - int getTotalLength() const { return source->getTotalLength(); } + int64 getTotalLength() const { return source->getTotalLength(); } /** Implements the PositionableAudioSource method. */ bool isLooping() const { return source->isLooping(); } @@ -34725,13 +34728,13 @@ private: int numberOfSamplesToBuffer; AudioSampleBuffer buffer; CriticalSection bufferStartPosLock; - int volatile bufferValidStart, bufferValidEnd, nextPlayPos; + int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos; bool wasSourceLooping; double volatile sampleRate; friend class SharedBufferingAudioSourceThread; bool readNextBufferChunk(); - void readBufferSection (int start, int length, int bufferOffset); + void readBufferSection (int64 start, int length, int bufferOffset); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource); }; @@ -34928,13 +34931,13 @@ public: void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int newPosition); + void setNextReadPosition (int64 newPosition); /** Implements the PositionableAudioSource method. */ - int getNextReadPosition() const; + int64 getNextReadPosition() const; /** Implements the PositionableAudioSource method. */ - int getTotalLength() const; + int64 getTotalLength() const; /** Implements the PositionableAudioSource method. */ bool isLooping() const; diff --git a/src/audio/audio_file_formats/juce_AiffAudioFormat.cpp b/src/audio/audio_file_formats/juce_AiffAudioFormat.cpp index 2c3af91da4..403ab66942 100644 --- a/src/audio/audio_file_formats/juce_AiffAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_AiffAudioFormat.cpp @@ -144,10 +144,6 @@ public: } } - ~AiffAudioFormatReader() - { - } - //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) diff --git a/src/audio/audio_file_formats/juce_WavAudioFormat.cpp b/src/audio/audio_file_formats/juce_WavAudioFormat.cpp index f1aafce43a..12c077e151 100644 --- a/src/audio/audio_file_formats/juce_WavAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_WavAudioFormat.cpp @@ -69,6 +69,9 @@ const StringPairArray WavAudioFormat::createBWAVMetadata (const String& descript //============================================================================== +namespace WavFileHelpers +{ + #if JUCE_MSVC #pragma pack (push, 1) #define PACKED @@ -246,6 +249,17 @@ struct ExtensibleWavSubFormat uint8 data4[8]; } PACKED; +struct DataSize64Chunk // chunk ID = 'ds64' if data size > 0xffffffff, 'JUNK' otherwise +{ + uint32 riffSizeLow; // low 4 byte size of RF64 block + uint32 riffSizeHigh; // high 4 byte size of RF64 block + uint32 dataSizeLow; // low 4 byte size of data chunk + uint32 dataSizeHigh; // high 4 byte size of data chunk + uint32 sampleCountLow; // low 4 byte sample count of fact chunk + uint32 sampleCountHigh; // high 4 byte sample count of fact chunk + uint32 tableLength; // number of valid entries in array 'table' +} PACKED; + #if JUCE_MSVC #pragma pack (pop) @@ -253,6 +267,9 @@ struct ExtensibleWavSubFormat #undef PACKED + inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } +} + //============================================================================== class WavAudioFormatReader : public AudioFormatReader @@ -263,109 +280,146 @@ public: : AudioFormatReader (in, TRANS (wavFormatName)), bwavChunkStart (0), bwavSize (0), - dataLength (0) + dataLength (0), + isRF64 (false) { - if (input->readInt() == chunkName ("RIFF")) + using namespace WavFileHelpers; + uint64 len = 0; + int64 end = 0; + bool hasGotType = false; + bool hasGotData = false; + + const int firstChunkType = input->readInt(); + + if (firstChunkType == chunkName ("RF64")) + { + input->skipNextBytes (4); // size is -1 for RF64 + isRF64 = true; + } + else if (firstChunkType == chunkName ("RIFF")) { - const uint32 len = (uint32) input->readInt(); - const int64 end = input->getPosition() + len; - bool hasGotType = false; - bool hasGotData = false; + len = (uint64) input->readInt(); + end = input->getPosition() + len; + } + else + { + return; + } + + const int64 startOfRIFFChunk = input->getPosition(); - if (input->readInt() == chunkName ("WAVE")) + if (input->readInt() == chunkName ("WAVE")) + { + if (isRF64 && input->readInt() == chunkName ("ds64")) { - while (input->getPosition() < end - && ! input->isExhausted()) + uint32 length = (uint32) input->readInt(); + + if (length < 28) + { + return; + } + else { - const int chunkType = input->readInt(); - uint32 length = (uint32) input->readInt(); const int64 chunkEnd = input->getPosition() + length + (length & 1); + len = input->readInt64(); + end = startOfRIFFChunk + len; + dataLength = input->readInt64(); + input->setPosition (chunkEnd); + } + } - if (chunkType == chunkName ("fmt ")) - { - // read the format chunk - const unsigned short format = input->readShort(); - const short numChans = input->readShort(); - sampleRate = input->readInt(); - const int bytesPerSec = input->readInt(); + while (input->getPosition() < end && ! input->isExhausted()) + { + const int chunkType = input->readInt(); + uint32 length = (uint32) input->readInt(); + const int64 chunkEnd = input->getPosition() + length + (length & 1); + + if (chunkType == chunkName ("fmt ")) + { + // read the format chunk + const unsigned short format = input->readShort(); + const short numChans = input->readShort(); + sampleRate = input->readInt(); + const int bytesPerSec = input->readInt(); - numChannels = numChans; - bytesPerFrame = bytesPerSec / (int)sampleRate; - bitsPerSample = 8 * bytesPerFrame / numChans; + numChannels = numChans; + bytesPerFrame = bytesPerSec / (int)sampleRate; + bitsPerSample = 8 * bytesPerFrame / numChans; - if (format == 3) + if (format == 3) + { + usesFloatingPointData = true; + } + else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/) + { + if (length < 40) // too short { - usesFloatingPointData = true; + bytesPerFrame = 0; } - else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/) + else { - if (length < 40) // too short - { - bytesPerFrame = 0; - } - else + input->skipNextBytes (12); // skip over blockAlign, bitsPerSample and speakerPosition mask + ExtensibleWavSubFormat subFormat; + subFormat.data1 = input->readInt(); + subFormat.data2 = input->readShort(); + subFormat.data3 = input->readShort(); + input->read (subFormat.data4, sizeof (subFormat.data4)); + + const ExtensibleWavSubFormat pcmFormat + = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + + if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0) { - input->skipNextBytes (12); // skip over blockAlign, bitsPerSample and speakerPosition mask - ExtensibleWavSubFormat subFormat; - subFormat.data1 = input->readInt(); - subFormat.data2 = input->readShort(); - subFormat.data3 = input->readShort(); - input->read (subFormat.data4, sizeof (subFormat.data4)); - - const ExtensibleWavSubFormat pcmFormat - = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - - if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0) - { - const ExtensibleWavSubFormat ambisonicFormat - = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } }; - - if (memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0) - bytesPerFrame = 0; - } + const ExtensibleWavSubFormat ambisonicFormat + = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } }; + + if (memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0) + bytesPerFrame = 0; } } - else if (format != 1) - { - bytesPerFrame = 0; - } - - hasGotType = true; } - else if (chunkType == chunkName ("data")) + else if (format != 1) { - // get the data chunk's position + bytesPerFrame = 0; + } + + hasGotType = true; + } + else if (chunkType == chunkName ("data")) + { + // get the data chunk's position + if (! isRF64) // data size is expected to be -1, actual data size is in ds64 chunk dataLength = length; - dataChunkStart = input->getPosition(); - lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0; - hasGotData = true; - } - else if (chunkType == chunkName ("bext")) - { - bwavChunkStart = input->getPosition(); - bwavSize = length; - - // Broadcast-wav extension chunk.. - HeapBlock bwav; - bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); - input->read (bwav, length); - bwav->copyTo (metadataValues); - } - else if (chunkType == chunkName ("smpl")) - { - HeapBlock smpl; - smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); - input->read (smpl, length); - smpl->copyTo (metadataValues, length); - } - else if (chunkEnd <= input->getPosition()) - { - break; - } + dataChunkStart = input->getPosition(); + lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0; - input->setPosition (chunkEnd); + hasGotData = true; } + else if (chunkType == chunkName ("bext")) + { + bwavChunkStart = input->getPosition(); + bwavSize = length; + + // Broadcast-wav extension chunk.. + HeapBlock bwav; + bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); + input->read (bwav, length); + bwav->copyTo (metadataValues); + } + else if (chunkType == chunkName ("smpl")) + { + HeapBlock smpl; + smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); + input->read (smpl, length); + smpl->copyTo (metadataValues, length); + } + else if (chunkEnd <= input->getPosition()) + { + break; + } + + input->setPosition (chunkEnd); } } } @@ -428,8 +482,7 @@ private: ScopedPointer converter; int bytesPerFrame; int64 dataChunkStart, dataLength; - - static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } + bool isRF64; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader); }; @@ -439,20 +492,17 @@ class WavAudioFormatWriter : public AudioFormatWriter { public: //============================================================================== - WavAudioFormatWriter (OutputStream* const out, - const double sampleRate_, - const unsigned int numChannels_, - const int bits, + WavAudioFormatWriter (OutputStream* const out, const double sampleRate_, + const unsigned int numChannels_, const int bits, const StringPairArray& metadataValues) - : AudioFormatWriter (out, - TRANS (wavFormatName), - sampleRate_, - numChannels_, - bits), + : AudioFormatWriter (out, TRANS (wavFormatName), sampleRate_, numChannels_, bits), lengthInSamples (0), bytesWritten (0), - writeFailed (false) + writeFailed (false), + isRF64 (false) { + using namespace WavFileHelpers; + if (metadataValues.size() > 0) { bwavChunk = BWAVChunk::createFrom (metadataValues); @@ -465,6 +515,12 @@ public: ~WavAudioFormatWriter() { + if ((bytesWritten & 1) != 0) // pad to an even length + { + ++bytesWritten; + output->writeByte (0); + } + writeHeader(); } @@ -488,8 +544,7 @@ public: default: jassertfalse; break; } - if (bytesWritten + bytes >= (uint32) 0xfff00000 - || ! output->write (tempBlock.getData(), bytes)) + if (! output->write (tempBlock.getData(), bytes)) { // failed to write to disk, so let's try writing the header. // If it's just run out of disk space, then if it does manage @@ -510,14 +565,27 @@ public: private: ScopedPointer converter; MemoryBlock tempBlock, bwavChunk, smplChunk; - uint32 lengthInSamples, bytesWritten; + uint64 lengthInSamples, bytesWritten; int64 headerPosition; - bool writeFailed; + bool writeFailed, isRF64; + + static int getChannelMask (const int numChannels) throw() + { + switch (numChannels) + { + case 1: return 0; + case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT + case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + case 6: return 1 + 2 + 4 + 8 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + default: break; + } - static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } + return 0; + } void writeHeader() { + using namespace WavFileHelpers; const bool seekedOk = output->setPosition (headerPosition); (void) seekedOk; @@ -526,21 +594,62 @@ private: jassert (seekedOk); const int bytesPerFrame = numChannels * bitsPerSample / 8; - output->writeInt (chunkName ("RIFF")); - output->writeInt ((int) (lengthInSamples * bytesPerFrame - + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36) - + (smplChunk.getSize() > 0 ? smplChunk.getSize() + 8 : 0))); + int64 audioDataSize = bytesPerFrame * lengthInSamples; + int64 riffChunkSize = 4 /* 'WAVE' */ + 8 + 40 /* WAVEFORMATEX */ + + 8 + audioDataSize + (audioDataSize & 1) + + (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0) + + (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0) + + (8 + 28); // (JUNK chunk) + + riffChunkSize += (riffChunkSize & 0x1); + isRF64 = (riffChunkSize > 0xffffffff); + + output->writeInt (chunkName (isRF64 ? "RF64" : "RIFF")); + output->writeInt (isRF64 ? -1 : (int) riffChunkSize); output->writeInt (chunkName ("WAVE")); + + if (! isRF64) + { + // write Junk chunk + output->writeInt (chunkName ("JUNK")); + output->writeInt (28); + output->writeRepeatedByte (0, 28); + } + else + { + // write ds64 chunk + output->writeInt (chunkName ("ds64")); + output->writeInt (28); // chunk size for uncompressed data (no table) + output->writeInt64 (riffChunkSize); + output->writeInt64 (audioDataSize); + output->writeRepeatedByte (0, 12); + } + output->writeInt (chunkName ("fmt ")); - output->writeInt (16); - output->writeShort ((bitsPerSample < 32) ? (short) 1 /*WAVE_FORMAT_PCM*/ - : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); + output->writeInt (40); // WAVEFORMATEX chunk size + output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE output->writeShort ((short) numChannels); output->writeInt ((int) sampleRate); - output->writeInt (bytesPerFrame * (int) sampleRate); - output->writeShort ((short) bytesPerFrame); - output->writeShort ((short) bitsPerSample); + output->writeInt ((int) (bytesPerFrame * sampleRate)); // nAvgBytesPerSec + output->writeShort ((short) bytesPerFrame); // nBlockAlign + output->writeShort ((short) bitsPerSample); // wBitsPerSample + output->writeShort (22); // cbSize (size of the extension) + output->writeShort ((short) bitsPerSample); // wValidBitsPerSample + output->writeInt (getChannelMask (numChannels)); + + const ExtensibleWavSubFormat pcmFormat + = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + + const ExtensibleWavSubFormat IEEEFloatFormat + = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + + const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat; + + output->writeInt ((int) subFormat.data1); + output->writeShort ((short) subFormat.data2); + output->writeShort ((short) subFormat.data3); + output->write (subFormat.data4, sizeof (subFormat.data4)); if (bwavChunk.getSize() > 0) { @@ -557,7 +666,7 @@ private: } output->writeInt (chunkName ("data")); - output->writeInt (lengthInSamples * bytesPerFrame); + output->writeInt (isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); usesFloatingPointData = (bitsPerSample == 32); } @@ -604,28 +713,19 @@ AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, return 0; } -AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, - double sampleRate, - unsigned int numChannels, - int bitsPerSample, - const StringPairArray& metadataValues, - int /*qualityOptionIndex*/) +AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate, + unsigned int numChannels, int bitsPerSample, + const StringPairArray& metadataValues, int /*qualityOptionIndex*/) { if (getPossibleBitDepths().contains (bitsPerSample)) - { - return new WavAudioFormatWriter (out, - sampleRate, - numChannels, - bitsPerSample, - metadataValues); - } + return new WavAudioFormatWriter (out, sampleRate, numChannels, bitsPerSample, metadataValues); return 0; } -namespace +namespace WavFileHelpers { - bool juce_slowCopyOfWavFileWithNewMetadata (const File& file, const StringPairArray& metadata) + bool slowCopyWavFileWithNewMetadata (const File& file, const StringPairArray& metadata) { TemporaryFile tempFile (file); @@ -661,7 +761,8 @@ namespace bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata) { - ScopedPointer reader ((WavAudioFormatReader*) createReaderFor (wavFile.createInputStream(), true)); + using namespace WavFileHelpers; + ScopedPointer reader (static_cast (createReaderFor (wavFile.createInputStream(), true))); if (reader != 0) { @@ -692,7 +793,7 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai } } - return juce_slowCopyOfWavFileWithNewMetadata (wavFile, newMetadata); + return slowCopyWavFileWithNewMetadata (wavFile, newMetadata); } diff --git a/src/audio/audio_sources/juce_AudioFormatReaderSource.cpp b/src/audio/audio_sources/juce_AudioFormatReaderSource.cpp index 0152a74678..4e79a6c4df 100644 --- a/src/audio/audio_sources/juce_AudioFormatReaderSource.cpp +++ b/src/audio/audio_sources/juce_AudioFormatReaderSource.cpp @@ -50,7 +50,7 @@ AudioFormatReaderSource::~AudioFormatReaderSource() delete reader; } -void AudioFormatReaderSource::setNextReadPosition (int newPosition) +void AudioFormatReaderSource::setNextReadPosition (int64 newPosition) { nextPlayPos = newPosition; } @@ -60,15 +60,15 @@ void AudioFormatReaderSource::setLooping (bool shouldLoop) looping = shouldLoop; } -int AudioFormatReaderSource::getNextReadPosition() const +int64 AudioFormatReaderSource::getNextReadPosition() const { - return (looping) ? (nextPlayPos % (int) reader->lengthInSamples) - : nextPlayPos; + return looping ? nextPlayPos % reader->lengthInSamples + : nextPlayPos; } -int AudioFormatReaderSource::getTotalLength() const +int64 AudioFormatReaderSource::getTotalLength() const { - return (int) reader->lengthInSamples; + return reader->lengthInSamples; } void AudioFormatReaderSource::prepareToPlay (int /*samplesPerBlockExpected*/, @@ -84,7 +84,7 @@ void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& i { if (info.numSamples > 0) { - const int start = nextPlayPos; + const int64 start = nextPlayPos; if (looping) { diff --git a/src/audio/audio_sources/juce_AudioFormatReaderSource.h b/src/audio/audio_sources/juce_AudioFormatReaderSource.h index 56b1e5d363..9fa233c4e9 100644 --- a/src/audio/audio_sources/juce_AudioFormatReaderSource.h +++ b/src/audio/audio_sources/juce_AudioFormatReaderSource.h @@ -83,20 +83,20 @@ public: //============================================================================== /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int newPosition); + void setNextReadPosition (int64 newPosition); /** Implements the PositionableAudioSource method. */ - int getNextReadPosition() const; + int64 getNextReadPosition() const; /** Implements the PositionableAudioSource method. */ - int getTotalLength() const; + int64 getTotalLength() const; private: //============================================================================== AudioFormatReader* reader; bool deleteReader; - int volatile nextPlayPos; + int64 volatile nextPlayPos; bool volatile looping; void readBufferSection (int start, int length, AudioSampleBuffer& buffer, int startSample); diff --git a/src/audio/audio_sources/juce_AudioTransportSource.cpp b/src/audio/audio_sources/juce_AudioTransportSource.cpp index 7255c7d4a9..09fd30de9c 100644 --- a/src/audio/audio_sources/juce_AudioTransportSource.cpp +++ b/src/audio/audio_sources/juce_AudioTransportSource.cpp @@ -159,7 +159,7 @@ void AudioTransportSource::stop() void AudioTransportSource::setPosition (double newPosition) { if (sampleRate > 0.0) - setNextReadPosition (roundToInt (newPosition * sampleRate)); + setNextReadPosition ((int64) (newPosition * sampleRate)); } double AudioTransportSource::getCurrentPosition() const @@ -175,30 +175,30 @@ double AudioTransportSource::getLengthInSeconds() const return getTotalLength() / sampleRate; } -void AudioTransportSource::setNextReadPosition (int newPosition) +void AudioTransportSource::setNextReadPosition (int64 newPosition) { if (positionableSource != 0) { if (sampleRate > 0 && sourceSampleRate > 0) - newPosition = roundToInt (newPosition * sourceSampleRate / sampleRate); + newPosition = (int64) (newPosition * sourceSampleRate / sampleRate); positionableSource->setNextReadPosition (newPosition); } } -int AudioTransportSource::getNextReadPosition() const +int64 AudioTransportSource::getNextReadPosition() const { if (positionableSource != 0) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; - return roundToInt (positionableSource->getNextReadPosition() * ratio); + return (int64) (positionableSource->getNextReadPosition() * ratio); } return 0; } -int AudioTransportSource::getTotalLength() const +int64 AudioTransportSource::getTotalLength() const { const ScopedLock sl (callbackLock); @@ -206,7 +206,7 @@ int AudioTransportSource::getTotalLength() const { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; - return roundToInt (positionableSource->getTotalLength() * ratio); + return (int64) (positionableSource->getTotalLength() * ratio); } return 0; diff --git a/src/audio/audio_sources/juce_AudioTransportSource.h b/src/audio/audio_sources/juce_AudioTransportSource.h index 147cd8f642..3d209e32e1 100644 --- a/src/audio/audio_sources/juce_AudioTransportSource.h +++ b/src/audio/audio_sources/juce_AudioTransportSource.h @@ -148,13 +148,13 @@ public: //============================================================================== /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int newPosition); + void setNextReadPosition (int64 newPosition); /** Implements the PositionableAudioSource method. */ - int getNextReadPosition() const; + int64 getNextReadPosition() const; /** Implements the PositionableAudioSource method. */ - int getTotalLength() const; + int64 getTotalLength() const; /** Implements the PositionableAudioSource method. */ bool isLooping() const; diff --git a/src/audio/audio_sources/juce_BufferingAudioSource.cpp b/src/audio/audio_sources/juce_BufferingAudioSource.cpp index 16c50045d9..7fb040bca8 100644 --- a/src/audio/audio_sources/juce_BufferingAudioSource.cpp +++ b/src/audio/audio_sources/juce_BufferingAudioSource.cpp @@ -187,8 +187,8 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info { const ScopedLock sl (bufferStartPosLock); - const int validStart = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos; - const int validEnd = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos; + const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos); + const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos); if (validStart == validEnd) { @@ -247,14 +247,14 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info thread->notify(); } -int BufferingAudioSource::getNextReadPosition() const +int64 BufferingAudioSource::getNextReadPosition() const { return (source->isLooping() && nextPlayPos > 0) ? nextPlayPos % source->getTotalLength() : nextPlayPos; } -void BufferingAudioSource::setNextReadPosition (int newPosition) +void BufferingAudioSource::setNextReadPosition (int64 newPosition) { const ScopedLock sl (bufferStartPosLock); @@ -268,7 +268,7 @@ void BufferingAudioSource::setNextReadPosition (int newPosition) bool BufferingAudioSource::readNextBufferChunk() { - int newBVS, newBVE, sectionToReadStart, sectionToReadEnd; + int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd; { const ScopedLock sl (bufferStartPosLock); @@ -280,7 +280,7 @@ bool BufferingAudioSource::readNextBufferChunk() bufferValidEnd = 0; } - newBVS = jmax (0, nextPlayPos); + newBVS = jmax ((int64) 0, nextPlayPos); newBVE = newBVS + buffer.getNumSamples() - 4; sectionToReadStart = 0; sectionToReadEnd = 0; @@ -297,8 +297,8 @@ bool BufferingAudioSource::readNextBufferChunk() bufferValidStart = 0; bufferValidEnd = 0; } - else if (abs (newBVS - bufferValidStart) > 512 - || abs (newBVE - bufferValidEnd) > 512) + else if (std::abs ((int) (newBVS - bufferValidStart)) > 512 + || std::abs ((int) (newBVE - bufferValidEnd)) > 512) { newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize); @@ -318,7 +318,7 @@ bool BufferingAudioSource::readNextBufferChunk() if (bufferIndexStart < bufferIndexEnd) { readBufferSection (sectionToReadStart, - sectionToReadEnd - sectionToReadStart, + (int) (sectionToReadEnd - sectionToReadStart), bufferIndexStart); } else @@ -330,7 +330,7 @@ bool BufferingAudioSource::readNextBufferChunk() bufferIndexStart); readBufferSection (sectionToReadStart + initialSize, - (sectionToReadEnd - sectionToReadStart) - initialSize, + (int) (sectionToReadEnd - sectionToReadStart) - initialSize, 0); } @@ -347,7 +347,7 @@ bool BufferingAudioSource::readNextBufferChunk() } } -void BufferingAudioSource::readBufferSection (int start, int length, int bufferOffset) +void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset) { if (source->getNextReadPosition() != start) source->setNextReadPosition (start); diff --git a/src/audio/audio_sources/juce_BufferingAudioSource.h b/src/audio/audio_sources/juce_BufferingAudioSource.h index 6269ba5926..b774083f23 100644 --- a/src/audio/audio_sources/juce_BufferingAudioSource.h +++ b/src/audio/audio_sources/juce_BufferingAudioSource.h @@ -75,13 +75,13 @@ public: //============================================================================== /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int newPosition); + void setNextReadPosition (int64 newPosition); /** Implements the PositionableAudioSource method. */ - int getNextReadPosition() const; + int64 getNextReadPosition() const; /** Implements the PositionableAudioSource method. */ - int getTotalLength() const { return source->getTotalLength(); } + int64 getTotalLength() const { return source->getTotalLength(); } /** Implements the PositionableAudioSource method. */ bool isLooping() const { return source->isLooping(); } @@ -93,13 +93,13 @@ private: int numberOfSamplesToBuffer; AudioSampleBuffer buffer; CriticalSection bufferStartPosLock; - int volatile bufferValidStart, bufferValidEnd, nextPlayPos; + int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos; bool wasSourceLooping; double volatile sampleRate; friend class SharedBufferingAudioSourceThread; bool readNextBufferChunk(); - void readBufferSection (int start, int length, int bufferOffset); + void readBufferSection (int64 start, int length, int bufferOffset); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource); }; diff --git a/src/audio/audio_sources/juce_PositionableAudioSource.h b/src/audio/audio_sources/juce_PositionableAudioSource.h index 7ae9995d99..878885fa58 100644 --- a/src/audio/audio_sources/juce_PositionableAudioSource.h +++ b/src/audio/audio_sources/juce_PositionableAudioSource.h @@ -59,16 +59,16 @@ public: Note that this may be called on a different thread to getNextAudioBlock(), so the subclass should make sure it's synchronised. */ - virtual void setNextReadPosition (int newPosition) = 0; + virtual void setNextReadPosition (int64 newPosition) = 0; /** Returns the position from which the next block will be returned. @see setNextReadPosition */ - virtual int getNextReadPosition() const = 0; + virtual int64 getNextReadPosition() const = 0; /** Returns the total length of the stream (in samples). */ - virtual int getTotalLength() const = 0; + virtual int64 getTotalLength() const = 0; /** Returns true if this source is actually playing in a loop. */ virtual bool isLooping() const = 0; diff --git a/src/audio/dsp/juce_AudioSampleBuffer.cpp b/src/audio/dsp/juce_AudioSampleBuffer.cpp index 9815af2e52..93c34011ab 100644 --- a/src/audio/dsp/juce_AudioSampleBuffer.cpp +++ b/src/audio/dsp/juce_AudioSampleBuffer.cpp @@ -547,7 +547,7 @@ float AudioSampleBuffer::getRMSLevel (const int channel, void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader, const int startSample, const int numSamples, - const int readerStartSample, + const int64 readerStartSample, const bool useLeftChan, const bool useRightChan) { diff --git a/src/audio/dsp/juce_AudioSampleBuffer.h b/src/audio/dsp/juce_AudioSampleBuffer.h index 9c0419f5b2..0aada2b5f9 100644 --- a/src/audio/dsp/juce_AudioSampleBuffer.h +++ b/src/audio/dsp/juce_AudioSampleBuffer.h @@ -405,7 +405,7 @@ public: void readFromAudioReader (AudioFormatReader* reader, int startSample, int numSamples, - int readerStartSample, + int64 readerStartSample, bool useReaderLeftChan, bool useReaderRightChan); diff --git a/src/io/streams/juce_OutputStream.cpp b/src/io/streams/juce_OutputStream.cpp index 61384855f3..57a5fc4ba2 100644 --- a/src/io/streams/juce_OutputStream.cpp +++ b/src/io/streams/juce_OutputStream.cpp @@ -78,6 +78,12 @@ void OutputStream::writeByte (char byte) write (&byte, 1); } +void OutputStream::writeRepeatedByte (uint8 byte, int numTimesToRepeat) +{ + while (--numTimesToRepeat >= 0) + writeByte (byte); +} + void OutputStream::writeShort (short value) { const unsigned short v = ByteOrder::swapIfBigEndian ((unsigned short) value); diff --git a/src/io/streams/juce_OutputStream.h b/src/io/streams/juce_OutputStream.h index e64c0d039c..3f626bad2b 100644 --- a/src/io/streams/juce_OutputStream.h +++ b/src/io/streams/juce_OutputStream.h @@ -155,6 +155,9 @@ public: */ virtual void writeDoubleBigEndian (double value); + /** Writes a byte to the output stream a given number of times. */ + virtual void writeRepeatedByte (uint8 byte, int numTimesToRepeat); + /** Writes a condensed binary encoding of a 32-bit integer. If you're storing a lot of integers which are unlikely to have very large values,