OpenShot Library | libopenshot-audio 0.2.0
juce_AudioFormatWriter.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 5 End-User License
11 Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
12 27th April 2017).
13
14 End User License Agreement: www.juce.com/juce-5-licence
15 Privacy Policy: www.juce.com/juce-5-privacy-policy
16
17 Or: You may also use this code under the terms of the GPL v3 (see
18 www.gnu.org/licenses).
19
20 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22 DISCLAIMED.
23
24 ==============================================================================
25*/
26
27namespace juce
28{
29
31 const String& formatName_,
32 const double rate,
33 const unsigned int numChannels_,
34 const unsigned int bitsPerSample_)
35 : sampleRate (rate),
36 numChannels (numChannels_),
37 bitsPerSample (bitsPerSample_),
38 usesFloatingPointData (false),
39 channelLayout (AudioChannelSet::canonicalChannelSet(static_cast<int> (numChannels_))),
40 output (out),
41 formatName (formatName_)
42{
43}
44
46 const String& formatName_,
47 const double rate,
48 const AudioChannelSet& channelLayout_,
49 const unsigned int bitsPerSample_)
50 : sampleRate (rate),
51 numChannels (static_cast<unsigned int> (channelLayout_.size())),
52 bitsPerSample (bitsPerSample_),
53 usesFloatingPointData (false),
54 channelLayout (channelLayout_),
55 output (out),
56 formatName (formatName_)
57{
58}
59
61{
62 delete output;
63}
64
65static void convertFloatsToInts (int* dest, const float* src, int numSamples) noexcept
66{
67 while (--numSamples >= 0)
68 {
69 const double samp = *src++;
70
71 if (samp <= -1.0)
72 *dest = std::numeric_limits<int>::min();
73 else if (samp >= 1.0)
74 *dest = std::numeric_limits<int>::max();
75 else
76 *dest = roundToInt (std::numeric_limits<int>::max() * samp);
77
78 ++dest;
79 }
80}
81
83 int64 startSample,
84 int64 numSamplesToRead)
85{
86 const int bufferSize = 16384;
87 AudioBuffer<float> tempBuffer ((int) numChannels, bufferSize);
88
89 int* buffers[128] = { nullptr };
90
91 for (int i = tempBuffer.getNumChannels(); --i >= 0;)
92 buffers[i] = reinterpret_cast<int*> (tempBuffer.getWritePointer (i, 0));
93
94 if (numSamplesToRead < 0)
95 numSamplesToRead = reader.lengthInSamples;
96
97 while (numSamplesToRead > 0)
98 {
99 const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize);
100
101 if (! reader.read (buffers, (int) numChannels, startSample, numToDo, false))
102 return false;
103
105 {
106 int** bufferChan = buffers;
107
108 while (*bufferChan != nullptr)
109 {
110 void* const b = *bufferChan++;
111
112 if (isFloatingPoint())
113 FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, 1.0f / 0x7fffffff, numToDo);
114 else
115 convertFloatsToInts ((int*) b, (float*) b, numToDo);
116 }
117 }
118
119 if (! write (const_cast<const int**> (buffers), numToDo))
120 return false;
121
122 numSamplesToRead -= numToDo;
123 startSample += numToDo;
124 }
125
126 return true;
127}
128
129bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock)
130{
131 AudioBuffer<float> tempBuffer (getNumChannels(), samplesPerBlock);
132
133 while (numSamplesToRead > 0)
134 {
135 auto numToDo = jmin (numSamplesToRead, samplesPerBlock);
136
137 AudioSourceChannelInfo info (&tempBuffer, 0, numToDo);
139
140 source.getNextAudioBlock (info);
141
142 if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo))
143 return false;
144
145 numSamplesToRead -= numToDo;
146 }
147
148 return true;
149}
150
151bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int numSourceChannels, int numSamples)
152{
153 if (numSamples <= 0)
154 return true;
155
156 if (isFloatingPoint())
157 return write ((const int**) channels, numSamples);
158
159 int* chans[256];
160 int scratch[4096];
161
162 jassert (numSourceChannels < numElementsInArray (chans));
163 const int maxSamples = (int) (numElementsInArray (scratch) / numSourceChannels);
164
165 for (int i = 0; i < numSourceChannels; ++i)
166 chans[i] = scratch + (i * maxSamples);
167
168 chans[numSourceChannels] = nullptr;
169 int startSample = 0;
170
171 while (numSamples > 0)
172 {
173 auto numToDo = jmin (numSamples, maxSamples);
174
175 for (int i = 0; i < numSourceChannels; ++i)
176 convertFloatsToInts (chans[i], channels[i] + startSample, numToDo);
177
178 if (! write ((const int**) chans, numToDo))
179 return false;
180
181 startSample += numToDo;
182 numSamples -= numToDo;
183 }
184
185 return true;
186}
187
188bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioBuffer<float>& source, int startSample, int numSamples)
189{
190 auto numSourceChannels = source.getNumChannels();
191 jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0);
192
193 if (startSample == 0)
194 return writeFromFloatArrays (source.getArrayOfReadPointers(), numSourceChannels, numSamples);
195
196 const float* chans[256];
197 jassert ((int) numChannels < numElementsInArray (chans));
198
199 for (int i = 0; i < numSourceChannels; ++i)
200 chans[i] = source.getReadPointer (i, startSample);
201
202 chans[numSourceChannels] = nullptr;
203
204 return writeFromFloatArrays (chans, numSourceChannels, numSamples);
205}
206
208{
209 return false;
210}
211
212//==============================================================================
214{
215public:
216 Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples)
217 : fifo (numSamples),
218 buffer (channels, numSamples),
219 timeSliceThread (tst),
220 writer (w)
221 {
222 timeSliceThread.addTimeSliceClient (this);
223 }
224
225 ~Buffer() override
226 {
227 isRunning = false;
228 timeSliceThread.removeTimeSliceClient (this);
229
230 while (writePendingData() == 0)
231 {}
232 }
233
234 bool write (const float* const* data, int numSamples)
235 {
236 if (numSamples <= 0 || ! isRunning)
237 return true;
238
239 jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this!
240
241 int start1, size1, start2, size2;
242 fifo.prepareToWrite (numSamples, start1, size1, start2, size2);
243
244 if (size1 + size2 < numSamples)
245 return false;
246
247 for (int i = buffer.getNumChannels(); --i >= 0;)
248 {
249 buffer.copyFrom (i, start1, data[i], size1);
250 buffer.copyFrom (i, start2, data[i] + size1, size2);
251 }
252
253 fifo.finishedWrite (size1 + size2);
254 timeSliceThread.notify();
255 return true;
256 }
257
258 int useTimeSlice() override
259 {
260 return writePendingData();
261 }
262
263 int writePendingData()
264 {
265 auto numToDo = fifo.getTotalSize() / 4;
266
267 int start1, size1, start2, size2;
268 fifo.prepareToRead (numToDo, start1, size1, start2, size2);
269
270 if (size1 <= 0)
271 return 10;
272
273 writer->writeFromAudioSampleBuffer (buffer, start1, size1);
274
275 const ScopedLock sl (thumbnailLock);
276
277 if (receiver != nullptr)
278 receiver->addBlock (samplesWritten, buffer, start1, size1);
279
280 samplesWritten += size1;
281
282 if (size2 > 0)
283 {
284 writer->writeFromAudioSampleBuffer (buffer, start2, size2);
285
286 if (receiver != nullptr)
287 receiver->addBlock (samplesWritten, buffer, start2, size2);
288
289 samplesWritten += size2;
290 }
291
292 fifo.finishedRead (size1 + size2);
293
294 if (samplesPerFlush > 0)
295 {
296 flushSampleCounter -= size1 + size2;
297
298 if (flushSampleCounter <= 0)
299 {
300 flushSampleCounter = samplesPerFlush;
301 writer->flush();
302 }
303 }
304
305 return 0;
306 }
307
308 void setDataReceiver (IncomingDataReceiver* newReceiver)
309 {
310 if (newReceiver != nullptr)
311 newReceiver->reset (buffer.getNumChannels(), writer->getSampleRate(), 0);
312
313 const ScopedLock sl (thumbnailLock);
314 receiver = newReceiver;
315 samplesWritten = 0;
316 }
317
318 void setFlushInterval (int numSamples) noexcept
319 {
320 samplesPerFlush = numSamples;
321 }
322
323private:
324 AbstractFifo fifo;
325 AudioBuffer<float> buffer;
326 TimeSliceThread& timeSliceThread;
327 std::unique_ptr<AudioFormatWriter> writer;
328 CriticalSection thumbnailLock;
329 IncomingDataReceiver* receiver = {};
330 int64 samplesWritten = 0;
331 int samplesPerFlush = 0, flushSampleCounter = 0;
332 std::atomic<bool> isRunning { true };
333
334 JUCE_DECLARE_NON_COPYABLE (Buffer)
335};
336
338 : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, (int) writer->numChannels, numSamplesToBuffer))
339{
340}
341
343{
344}
345
346bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples)
347{
348 return buffer->write (data, numSamples);
349}
350
352{
353 buffer->setDataReceiver (receiver);
354}
355
356void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept
357{
358 buffer->setFlushInterval (numSamplesPerFlush);
359}
360
361} // namespace juce
void prepareToWrite(int numToWrite, int &startIndex1, int &blockSize1, int &startIndex2, int &blockSize2) const noexcept
Returns the location within the buffer at which an incoming block of data should be written.
int getTotalSize() const noexcept
Returns the total size of the buffer being managed.
void prepareToRead(int numWanted, int &startIndex1, int &blockSize1, int &startIndex2, int &blockSize2) const noexcept
Returns the location within the buffer from which the next block of data should be read.
void finishedRead(int numRead) noexcept
Called after reading from the FIFO, to indicate that this many items have now been consumed.
void finishedWrite(int numWritten) noexcept
Called after writing from the FIFO, to indicate that this many items have been added.
A multi-channel buffer containing floating point audio samples.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer's channels.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
int getNumSamples() const noexcept
Returns the number of samples allocated in each of the buffer's channels.
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
Copies samples from another buffer to this one.
const Type * getReadPointer(int channelNumber) const noexcept
Returns a pointer to an array of read-only samples in one of the buffer's channels.
const Type ** getArrayOfReadPointers() const noexcept
Returns an array of pointers to the channels in the buffer.
Represents a set of audio channel types.
Reads samples from an audio file stream.
bool usesFloatingPointData
Indicates whether the data is floating-point or fixed.
bool read(float *const *destChannels, int numDestChannels, int64 startSampleInSource, int numSamplesToRead)
Reads samples from the stream.
int64 lengthInSamples
The total number of samples in the audio stream.
int useTimeSlice() override
Called back by a TimeSliceThread.
Provides a FIFO for an AudioFormatWriter, allowing you to push incoming data into a buffer which will...
bool write(const float *const *data, int numSamples)
Pushes some incoming audio data into the FIFO.
void setDataReceiver(IncomingDataReceiver *)
Allows you to specify a callback that this writer should update with the incoming data.
ThreadedWriter(AudioFormatWriter *writer, TimeSliceThread &backgroundThread, int numSamplesToBuffer)
Creates a ThreadedWriter for a given writer and a thread.
void setFlushInterval(int numSamplesPerFlush) noexcept
Sets how many samples should be written before calling the AudioFormatWriter::flush method.
Writes samples to an audio file stream.
bool writeFromAudioReader(AudioFormatReader &reader, int64 startSample, int64 numSamplesToRead)
Reads a section of samples from an AudioFormatReader, and writes these to the output.
unsigned int numChannels
The number of channels being written to the stream.
bool writeFromFloatArrays(const float *const *channels, int numChannels, int numSamples)
Writes some samples from a set of float data channels.
bool writeFromAudioSource(AudioSource &source, int numSamplesToRead, int samplesPerBlock=2048)
Reads some samples from an AudioSource, and writes these to the output.
virtual bool flush()
Some formats may support a flush operation that makes sure the file is in a valid state before carryi...
virtual bool write(const int **samplesToWrite, int numSamples)=0
Writes a set of samples to the audio stream.
int getNumChannels() const noexcept
Returns the number of channels being written.
AudioFormatWriter(OutputStream *destStream, const String &formatName, double sampleRate, unsigned int numberOfChannels, unsigned int bitsPerSample)
Creates an AudioFormatWriter object.
bool writeFromAudioSampleBuffer(const AudioBuffer< float > &source, int startSample, int numSamples)
Writes some samples from an AudioBuffer.
virtual ~AudioFormatWriter()
Destructor.
bool isFloatingPoint() const noexcept
Returns true if it's a floating-point format, false if it's fixed-point.
OutputStream * output
The output stream for use by subclasses.
Base class for objects that can produce a continuous stream of audio.
virtual void getNextAudioBlock(const AudioSourceChannelInfo &bufferToFill)=0
Called repeatedly to fetch subsequent blocks of audio data.
static void JUCE_CALLTYPE convertFixedToFloat(float *dest, const int *src, float multiplier, int numValues) noexcept
Converts a stream of integers to floats, multiplying each one by the given multiplier.
Automatically locks and unlocks a mutex object.
The base class for streams that write data to some kind of destination.
The JUCE String class!
Definition juce_String.h:43
void notify() const
Wakes up the thread.
bool isThreadRunning() const
Returns true if the thread is currently active.
Used by the TimeSliceThread class.
A thread that keeps a list of clients, and calls each one in turn, giving them all a chance to run so...
void removeTimeSliceClient(TimeSliceClient *clientToRemove)
Removes a client from the list.
void addTimeSliceClient(TimeSliceClient *clientToAdd, int millisecondsBeforeStarting=0)
Adds a client to the list.
Used by AudioSource::getNextAudioBlock().
void clearActiveBufferRegion() const
Convenient method to clear the buffer if the source is not producing any data.