Loading...
Searching...
No Matches
juce_AudioFormatWriter.cpp
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
AudioFormatWriter::AudioFormatWriter (OutputStream* const out,
const String& formatName_,
const double rate,
const unsigned int numChannels_,
const unsigned int bitsPerSample_)
: sampleRate (rate),
numChannels (numChannels_),
bitsPerSample (bitsPerSample_),
usesFloatingPointData (false),
channelLayout (AudioChannelSet::canonicalChannelSet (static_cast<int> (numChannels_))),
output (out),
formatName (formatName_)
{
}
AudioFormatWriter::AudioFormatWriter (OutputStream* const out,
const String& formatName_,
const double rate,
const AudioChannelSet& channelLayout_,
const unsigned int bitsPerSample_)
: sampleRate (rate),
numChannels (static_cast<unsigned int> (channelLayout_.size())),
bitsPerSample (bitsPerSample_),
usesFloatingPointData (false),
channelLayout (channelLayout_),
output (out),
formatName (formatName_)
{
}
AudioFormatWriter::~AudioFormatWriter()
{
delete output;
}
static void convertFloatsToInts (int* dest, const float* src, int numSamples) noexcept
{
while (--numSamples >= 0)
{
const double samp = *src++;
if (samp <= -1.0)
*dest = std::numeric_limits<int>::min();
else if (samp >= 1.0)
*dest = std::numeric_limits<int>::max();
else
*dest = roundToInt (std::numeric_limits<int>::max() * samp);
++dest;
}
}
bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader,
int64 startSample,
int64 numSamplesToRead)
{
const int bufferSize = 16384;
AudioBuffer<float> tempBuffer ((int) numChannels, bufferSize);
int* buffers[128] = { nullptr };
for (int i = tempBuffer.getNumChannels(); --i >= 0;)
buffers[i] = reinterpret_cast<int*> (tempBuffer.getWritePointer (i, 0));
if (numSamplesToRead < 0)
numSamplesToRead = reader.lengthInSamples;
while (numSamplesToRead > 0)
{
const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize);
if (! reader.read (buffers, (int) numChannels, startSample, numToDo, false))
return false;
if (reader.usesFloatingPointData != isFloatingPoint())
{
int** bufferChan = buffers;
while (*bufferChan != nullptr)
{
void* const b = *bufferChan++;
constexpr auto scaleFactor = 1.0f / static_cast<float> (0x7fffffff);
if (isFloatingPoint())
FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, scaleFactor, numToDo);
else
convertFloatsToInts ((int*) b, (float*) b, numToDo);
}
}
if (! write (const_cast<const int**> (buffers), numToDo))
return false;
numSamplesToRead -= numToDo;
startSample += numToDo;
}
return true;
}
bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock)
{
AudioBuffer<float> tempBuffer (getNumChannels(), samplesPerBlock);
while (numSamplesToRead > 0)
{
auto numToDo = jmin (numSamplesToRead, samplesPerBlock);
AudioSourceChannelInfo info (&tempBuffer, 0, numToDo);
info.clearActiveBufferRegion();
source.getNextAudioBlock (info);
if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo))
return false;
numSamplesToRead -= numToDo;
}
return true;
}
bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int numSourceChannels, int numSamples)
{
if (numSamples <= 0)
return true;
if (isFloatingPoint())
return write ((const int**) channels, numSamples);
std::vector<int*> chans (256);
std::vector<int> scratch (4096);
jassert (numSourceChannels < (int) chans.size());
const int maxSamples = (int) scratch.size() / numSourceChannels;
for (int i = 0; i < numSourceChannels; ++i)
chans[(size_t) i] = scratch.data() + (i * maxSamples);
chans[(size_t) numSourceChannels] = nullptr;
int startSample = 0;
while (numSamples > 0)
{
auto numToDo = jmin (numSamples, maxSamples);
for (int i = 0; i < numSourceChannels; ++i)
convertFloatsToInts (chans[(size_t) i], channels[(size_t) i] + startSample, numToDo);
if (! write ((const int**) chans.data(), numToDo))
return false;
startSample += numToDo;
numSamples -= numToDo;
}
return true;
}
bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioBuffer<float>& source, int startSample, int numSamples)
{
auto numSourceChannels = source.getNumChannels();
jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0);
if (startSample == 0)
return writeFromFloatArrays (source.getArrayOfReadPointers(), numSourceChannels, numSamples);
const float* chans[256];
jassert ((int) numChannels < numElementsInArray (chans));
for (int i = 0; i < numSourceChannels; ++i)
chans[i] = source.getReadPointer (i, startSample);
chans[numSourceChannels] = nullptr;
return writeFromFloatArrays (chans, numSourceChannels, numSamples);
}
bool AudioFormatWriter::flush()
{
return false;
}
//==============================================================================
class AudioFormatWriter::ThreadedWriter::Buffer final : private TimeSliceClient
{
public:
Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples)
: fifo (numSamples),
buffer (channels, numSamples),
timeSliceThread (tst),
writer (w)
{
timeSliceThread.addTimeSliceClient (this);
}
~Buffer() override
{
isRunning = false;
timeSliceThread.removeTimeSliceClient (this);
while (writePendingData() == 0)
{}
}
bool write (const float* const* data, int numSamples)
{
if (numSamples <= 0 || ! isRunning)
return true;
jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this!
int start1, size1, start2, size2;
fifo.prepareToWrite (numSamples, start1, size1, start2, size2);
if (size1 + size2 < numSamples)
return false;
for (int i = buffer.getNumChannels(); --i >= 0;)
{
buffer.copyFrom (i, start1, data[i], size1);
buffer.copyFrom (i, start2, data[i] + size1, size2);
}
fifo.finishedWrite (size1 + size2);
timeSliceThread.notify();
return true;
}
int useTimeSlice() override
{
return writePendingData();
}
int writePendingData()
{
auto numToDo = fifo.getTotalSize() / 4;
int start1, size1, start2, size2;
fifo.prepareToRead (numToDo, start1, size1, start2, size2);
if (size1 <= 0)
return 10;
writer->writeFromAudioSampleBuffer (buffer, start1, size1);
const ScopedLock sl (thumbnailLock);
if (receiver != nullptr)
receiver->addBlock (samplesWritten, buffer, start1, size1);
samplesWritten += size1;
if (size2 > 0)
{
writer->writeFromAudioSampleBuffer (buffer, start2, size2);
if (receiver != nullptr)
receiver->addBlock (samplesWritten, buffer, start2, size2);
samplesWritten += size2;
}
fifo.finishedRead (size1 + size2);
if (samplesPerFlush > 0)
{
flushSampleCounter -= size1 + size2;
if (flushSampleCounter <= 0)
{
flushSampleCounter = samplesPerFlush;
writer->flush();
}
}
return 0;
}
void setDataReceiver (IncomingDataReceiver* newReceiver)
{
if (newReceiver != nullptr)
newReceiver->reset (buffer.getNumChannels(), writer->getSampleRate(), 0);
const ScopedLock sl (thumbnailLock);
receiver = newReceiver;
samplesWritten = 0;
}
void setFlushInterval (int numSamples) noexcept
{
samplesPerFlush = numSamples;
}
private:
AbstractFifo fifo;
AudioBuffer<float> buffer;
TimeSliceThread& timeSliceThread;
std::unique_ptr<AudioFormatWriter> writer;
CriticalSection thumbnailLock;
IncomingDataReceiver* receiver = {};
int64 samplesWritten = 0;
int samplesPerFlush = 0, flushSampleCounter = 0;
std::atomic<bool> isRunning { true };
JUCE_DECLARE_NON_COPYABLE (Buffer)
};
AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer)
: buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, (int) writer->numChannels, numSamplesToBuffer))
{
}
AudioFormatWriter::ThreadedWriter::~ThreadedWriter()
{
}
bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples)
{
return buffer->write (data, numSamples);
}
void AudioFormatWriter::ThreadedWriter::setDataReceiver (AudioFormatWriter::ThreadedWriter::IncomingDataReceiver* receiver)
{
buffer->setDataReceiver (receiver);
}
void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept
{
buffer->setFlushInterval (numSamplesPerFlush);
}
} // namespace juce
juce::AudioBuffer
Definition juce_AudioSampleBuffer.h:34
juce::AudioBuffer::getWritePointer
Type * getWritePointer(int channelNumber) noexcept
Definition juce_AudioSampleBuffer.h:291
juce::AudioBuffer::getNumChannels
int getNumChannels() const noexcept
Definition juce_AudioSampleBuffer.h:236
juce::AudioBuffer::getNumSamples
int getNumSamples() const noexcept
Definition juce_AudioSampleBuffer.h:242
juce::AudioBuffer::getReadPointer
const Type * getReadPointer(int channelNumber) const noexcept
Definition juce_AudioSampleBuffer.h:253
juce::AudioBuffer::getArrayOfReadPointers
const Type *const * getArrayOfReadPointers() const noexcept
Definition juce_AudioSampleBuffer.h:327
juce::AudioChannelSet
Definition juce_AudioChannelSet.h:47
juce::AudioFormatReader
Definition juce_AudioFormatReader.h:44
juce::AudioFormatReader::usesFloatingPointData
bool usesFloatingPointData
Definition juce_AudioFormatReader.h:237
juce::AudioFormatReader::read
bool read(float *const *destChannels, int numDestChannels, int64 startSampleInSource, int numSamplesToRead)
Definition juce_AudioFormatReader.cpp:48
juce::AudioFormatReader::lengthInSamples
int64 lengthInSamples
Definition juce_AudioFormatReader.h:231
juce::AudioFormatWriter::ThreadedWriter::IncomingDataReceiver
Definition juce_AudioFormatWriter.h:216
juce::AudioFormatWriter::ThreadedWriter::~ThreadedWriter
~ThreadedWriter()
Definition juce_AudioFormatWriter.cpp:343
juce::AudioFormatWriter::ThreadedWriter::write
bool write(const float *const *data, int numSamples)
Definition juce_AudioFormatWriter.cpp:347
juce::AudioFormatWriter::ThreadedWriter::setDataReceiver
void setDataReceiver(IncomingDataReceiver *)
Definition juce_AudioFormatWriter.cpp:352
juce::AudioFormatWriter::ThreadedWriter::ThreadedWriter
ThreadedWriter(AudioFormatWriter *writer, TimeSliceThread &backgroundThread, int numSamplesToBuffer)
Definition juce_AudioFormatWriter.cpp:338
juce::AudioFormatWriter::ThreadedWriter::setFlushInterval
void setFlushInterval(int numSamplesPerFlush) noexcept
Definition juce_AudioFormatWriter.cpp:357
juce::AudioFormatWriter
Definition juce_AudioFormatWriter.h:44
juce::AudioFormatWriter::writeFromAudioReader
bool writeFromAudioReader(AudioFormatReader &reader, int64 startSample, int64 numSamplesToRead)
Definition juce_AudioFormatWriter.cpp:81
juce::AudioFormatWriter::numChannels
unsigned int numChannels
Definition juce_AudioFormatWriter.h:251
juce::AudioFormatWriter::writeFromFloatArrays
bool writeFromFloatArrays(const float *const *channels, int numChannels, int numSamples)
Definition juce_AudioFormatWriter.cpp:152
juce::AudioFormatWriter::writeFromAudioSource
bool writeFromAudioSource(AudioSource &source, int numSamplesToRead, int samplesPerBlock=2048)
Definition juce_AudioFormatWriter.cpp:130
juce::AudioFormatWriter::sampleRate
double sampleRate
Definition juce_AudioFormatWriter.h:248
juce::AudioFormatWriter::usesFloatingPointData
bool usesFloatingPointData
Definition juce_AudioFormatWriter.h:257
juce::AudioFormatWriter::flush
virtual bool flush()
Definition juce_AudioFormatWriter.cpp:208
juce::AudioFormatWriter::bitsPerSample
unsigned int bitsPerSample
Definition juce_AudioFormatWriter.h:254
juce::AudioFormatWriter::write
virtual bool write(const int **samplesToWrite, int numSamples)=0
juce::AudioFormatWriter::getNumChannels
int getNumChannels() const noexcept
Definition juce_AudioFormatWriter.h:170
juce::AudioFormatWriter::AudioFormatWriter
AudioFormatWriter(OutputStream *destStream, const String &formatName, double sampleRate, unsigned int numberOfChannels, unsigned int bitsPerSample)
Definition juce_AudioFormatWriter.cpp:29
juce::AudioFormatWriter::writeFromAudioSampleBuffer
bool writeFromAudioSampleBuffer(const AudioBuffer< float > &source, int startSample, int numSamples)
Definition juce_AudioFormatWriter.cpp:189
juce::AudioFormatWriter::~AudioFormatWriter
virtual ~AudioFormatWriter()
Definition juce_AudioFormatWriter.cpp:59
juce::AudioFormatWriter::isFloatingPoint
bool isFloatingPoint() const noexcept
Definition juce_AudioFormatWriter.h:176
juce::AudioFormatWriter::channelLayout
AudioChannelSet channelLayout
Definition juce_AudioFormatWriter.h:260
juce::AudioFormatWriter::output
OutputStream * output
Definition juce_AudioFormatWriter.h:263
juce::AudioSource
Definition juce_AudioSource.h:110
juce::AudioSource::getNextAudioBlock
virtual void getNextAudioBlock(const AudioSourceChannelInfo &bufferToFill)=0
juce::OutputStream
Definition juce_OutputStream.h:38
juce::String
Definition juce_String.h:53
juce::TimeSliceClient
Definition juce_TimeSliceThread.h:44
juce::TimeSliceClient::useTimeSlice
virtual int useTimeSlice()=0
juce::TimeSliceThread
Definition juce_TimeSliceThread.h:83
juce::TimeSliceThread::removeTimeSliceClient
void removeTimeSliceClient(TimeSliceClient *clientToRemove)
Definition juce_TimeSliceThread.cpp:47
juce::TimeSliceThread::addTimeSliceClient
void addTimeSliceClient(TimeSliceClient *clientToAdd, int millisecondsBeforeStarting=0)
Definition juce_TimeSliceThread.cpp:36
juce::AudioSourceChannelInfo
Definition juce_AudioSource.h:33
juce::AudioSourceChannelInfo::clearActiveBufferRegion
void clearActiveBufferRegion() const
Definition juce_AudioSource.h:84