mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added RF64 support to WavAudioFormat. Extended some of the audio source classes to support 64-bit sample indices.
This commit is contained in:
parent
9770806e09
commit
8ade855f56
15 changed files with 562 additions and 350 deletions
|
|
@ -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 "))
|
||||
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;
|
||||
|
||||
if (format == 3)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
usesFloatingPointData = true;
|
||||
}
|
||||
else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
usesFloatingPointData = true;
|
||||
}
|
||||
else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/)
|
||||
{
|
||||
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));
|
||||
|
||||
hasGotType = true;
|
||||
}
|
||||
else if (chunkType == chunkName ("data"))
|
||||
{
|
||||
// get the data chunk's position
|
||||
dataLength = length;
|
||||
dataChunkStart = input->getPosition();
|
||||
lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0;
|
||||
const ExtensibleWavSubFormat pcmFormat
|
||||
= { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
|
||||
hasGotData = true;
|
||||
}
|
||||
else if (chunkType == chunkName ("bext"))
|
||||
{
|
||||
bwavChunkStart = input->getPosition();
|
||||
bwavSize = length;
|
||||
if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0)
|
||||
{
|
||||
const ExtensibleWavSubFormat ambisonicFormat
|
||||
= { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } };
|
||||
|
||||
// Broadcast-wav extension chunk..
|
||||
HeapBlock <BWAVChunk> bwav;
|
||||
bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1);
|
||||
input->read (bwav, length);
|
||||
bwav->copyTo (metadataValues);
|
||||
if (memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0)
|
||||
bytesPerFrame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (chunkType == chunkName ("smpl"))
|
||||
else if (format != 1)
|
||||
{
|
||||
HeapBlock <SMPLChunk> 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;
|
||||
bytesPerFrame = 0;
|
||||
}
|
||||
|
||||
input->setPosition (chunkEnd);
|
||||
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 <BWAVChunk> bwav;
|
||||
bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1);
|
||||
input->read (bwav, length);
|
||||
bwav->copyTo (metadataValues);
|
||||
}
|
||||
else if (chunkType == chunkName ("smpl"))
|
||||
{
|
||||
HeapBlock <SMPLChunk> 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<AudioData::Converter> 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<AudioData::Converter> 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 <WavAudioFormatReader> reader ((WavAudioFormatReader*) createReaderFor (wavFile.createInputStream(), true));
|
||||
using namespace WavFileHelpers;
|
||||
ScopedPointer <WavAudioFormatReader> reader (static_cast <WavAudioFormatReader*> (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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -144,10 +144,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
~AiffAudioFormatReader()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
|
||||
int64 startSampleInFile, int numSamples)
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
{
|
||||
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 "))
|
||||
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;
|
||||
|
||||
if (format == 3)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
usesFloatingPointData = true;
|
||||
}
|
||||
else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
usesFloatingPointData = true;
|
||||
}
|
||||
else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/)
|
||||
{
|
||||
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));
|
||||
|
||||
hasGotType = true;
|
||||
}
|
||||
else if (chunkType == chunkName ("data"))
|
||||
{
|
||||
// get the data chunk's position
|
||||
dataLength = length;
|
||||
dataChunkStart = input->getPosition();
|
||||
lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0;
|
||||
const ExtensibleWavSubFormat pcmFormat
|
||||
= { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
|
||||
hasGotData = true;
|
||||
}
|
||||
else if (chunkType == chunkName ("bext"))
|
||||
{
|
||||
bwavChunkStart = input->getPosition();
|
||||
bwavSize = length;
|
||||
if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0)
|
||||
{
|
||||
const ExtensibleWavSubFormat ambisonicFormat
|
||||
= { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } };
|
||||
|
||||
// Broadcast-wav extension chunk..
|
||||
HeapBlock <BWAVChunk> bwav;
|
||||
bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1);
|
||||
input->read (bwav, length);
|
||||
bwav->copyTo (metadataValues);
|
||||
if (memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0)
|
||||
bytesPerFrame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (chunkType == chunkName ("smpl"))
|
||||
else if (format != 1)
|
||||
{
|
||||
HeapBlock <SMPLChunk> 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;
|
||||
bytesPerFrame = 0;
|
||||
}
|
||||
|
||||
input->setPosition (chunkEnd);
|
||||
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 <BWAVChunk> bwav;
|
||||
bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1);
|
||||
input->read (bwav, length);
|
||||
bwav->copyTo (metadataValues);
|
||||
}
|
||||
else if (chunkType == chunkName ("smpl"))
|
||||
{
|
||||
HeapBlock <SMPLChunk> 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<AudioData::Converter> 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<AudioData::Converter> 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;
|
||||
|
||||
|
|
@ -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 <WavAudioFormatReader> reader ((WavAudioFormatReader*) createReaderFor (wavFile.createInputStream(), true));
|
||||
using namespace WavFileHelpers;
|
||||
ScopedPointer <WavAudioFormatReader> reader (static_cast <WavAudioFormatReader*> (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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ public:
|
|||
void readFromAudioReader (AudioFormatReader* reader,
|
||||
int startSample,
|
||||
int numSamples,
|
||||
int readerStartSample,
|
||||
int64 readerStartSample,
|
||||
bool useReaderLeftChan,
|
||||
bool useReaderRightChan);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue