OpenShot Audio Library | OpenShotAudio 0.4.0
 
Loading...
Searching...
No Matches
juce_AudioFormatWriter.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
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 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
30 const String& formatName_,
31 const double rate,
32 const unsigned int numChannels_,
33 const unsigned int bitsPerSample_)
34 : sampleRate (rate),
35 numChannels (numChannels_),
36 bitsPerSample (bitsPerSample_),
38 channelLayout (AudioChannelSet::canonicalChannelSet (static_cast<int> (numChannels_))),
39 output (out),
40 formatName (formatName_)
41{
42}
43
45 const String& formatName_,
46 const double rate,
47 const AudioChannelSet& channelLayout_,
48 const unsigned int bitsPerSample_)
49 : sampleRate (rate),
50 numChannels (static_cast<unsigned int> (channelLayout_.size())),
51 bitsPerSample (bitsPerSample_),
53 channelLayout (channelLayout_),
54 output (out),
55 formatName (formatName_)
56{
57}
58
63
64static void convertFloatsToInts (int* dest, const float* src, int numSamples) noexcept
65{
66 while (--numSamples >= 0)
67 {
68 const double samp = *src++;
69
70 if (samp <= -1.0)
71 *dest = std::numeric_limits<int>::min();
72 else if (samp >= 1.0)
73 *dest = std::numeric_limits<int>::max();
74 else
75 *dest = roundToInt (std::numeric_limits<int>::max() * samp);
76
77 ++dest;
78 }
79}
80
82 int64 startSample,
83 int64 numSamplesToRead)
84{
85 const int bufferSize = 16384;
86 AudioBuffer<float> tempBuffer ((int) numChannels, bufferSize);
87
88 int* buffers[128] = { nullptr };
89
90 for (int i = tempBuffer.getNumChannels(); --i >= 0;)
91 buffers[i] = reinterpret_cast<int*> (tempBuffer.getWritePointer (i, 0));
92
93 if (numSamplesToRead < 0)
94 numSamplesToRead = reader.lengthInSamples;
95
96 while (numSamplesToRead > 0)
97 {
98 const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize);
99
100 if (! reader.read (buffers, (int) numChannels, startSample, numToDo, false))
101 return false;
102
104 {
105 int** bufferChan = buffers;
106
107 while (*bufferChan != nullptr)
108 {
109 void* const b = *bufferChan++;
110
111 constexpr auto scaleFactor = 1.0f / static_cast<float> (0x7fffffff);
112
113 if (isFloatingPoint())
114 FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, scaleFactor, numToDo);
115 else
116 convertFloatsToInts ((int*) b, (float*) b, numToDo);
117 }
118 }
119
120 if (! write (const_cast<const int**> (buffers), numToDo))
121 return false;
122
123 numSamplesToRead -= numToDo;
124 startSample += numToDo;
125 }
126
127 return true;
128}
129
130bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock)
131{
132 AudioBuffer<float> tempBuffer (getNumChannels(), samplesPerBlock);
133
134 while (numSamplesToRead > 0)
135 {
136 auto numToDo = jmin (numSamplesToRead, samplesPerBlock);
137
138 AudioSourceChannelInfo info (&tempBuffer, 0, numToDo);
140
141 source.getNextAudioBlock (info);
142
143 if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo))
144 return false;
145
146 numSamplesToRead -= numToDo;
147 }
148
149 return true;
150}
151
152bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int numSourceChannels, int numSamples)
153{
154 if (numSamples <= 0)
155 return true;
156
157 if (isFloatingPoint())
158 return write ((const int**) channels, numSamples);
159
160 std::vector<int*> chans (256);
161 std::vector<int> scratch (4096);
162
163 jassert (numSourceChannels < (int) chans.size());
164 const int maxSamples = (int) scratch.size() / numSourceChannels;
165
166 for (int i = 0; i < numSourceChannels; ++i)
167 chans[(size_t) i] = scratch.data() + (i * maxSamples);
168
169 chans[(size_t) numSourceChannels] = nullptr;
170 int startSample = 0;
171
172 while (numSamples > 0)
173 {
174 auto numToDo = jmin (numSamples, maxSamples);
175
176 for (int i = 0; i < numSourceChannels; ++i)
177 convertFloatsToInts (chans[(size_t) i], channels[(size_t) i] + startSample, numToDo);
178
179 if (! write ((const int**) chans.data(), numToDo))
180 return false;
181
182 startSample += numToDo;
183 numSamples -= numToDo;
184 }
185
186 return true;
187}
188
189bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioBuffer<float>& source, int startSample, int numSamples)
190{
191 auto numSourceChannels = source.getNumChannels();
192 jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0);
193
194 if (startSample == 0)
195 return writeFromFloatArrays (source.getArrayOfReadPointers(), numSourceChannels, numSamples);
196
197 const float* chans[256];
198 jassert ((int) numChannels < numElementsInArray (chans));
199
200 for (int i = 0; i < numSourceChannels; ++i)
201 chans[i] = source.getReadPointer (i, startSample);
202
203 chans[numSourceChannels] = nullptr;
204
205 return writeFromFloatArrays (chans, numSourceChannels, numSamples);
206}
207
209{
210 return false;
211}
212
213//==============================================================================
214class AudioFormatWriter::ThreadedWriter::Buffer final : private TimeSliceClient
215{
216public:
217 Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples)
218 : fifo (numSamples),
219 buffer (channels, numSamples),
220 timeSliceThread (tst),
221 writer (w)
222 {
223 timeSliceThread.addTimeSliceClient (this);
224 }
225
226 ~Buffer() override
227 {
228 isRunning = false;
229 timeSliceThread.removeTimeSliceClient (this);
230
231 while (writePendingData() == 0)
232 {}
233 }
234
235 bool write (const float* const* data, int numSamples)
236 {
237 if (numSamples <= 0 || ! isRunning)
238 return true;
239
240 jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this!
241
242 int start1, size1, start2, size2;
243 fifo.prepareToWrite (numSamples, start1, size1, start2, size2);
244
245 if (size1 + size2 < numSamples)
246 return false;
247
248 for (int i = buffer.getNumChannels(); --i >= 0;)
249 {
250 buffer.copyFrom (i, start1, data[i], size1);
251 buffer.copyFrom (i, start2, data[i] + size1, size2);
252 }
253
254 fifo.finishedWrite (size1 + size2);
255 timeSliceThread.notify();
256 return true;
257 }
258
259 int useTimeSlice() override
260 {
261 return writePendingData();
262 }
263
264 int writePendingData()
265 {
266 auto numToDo = fifo.getTotalSize() / 4;
267
268 int start1, size1, start2, size2;
269 fifo.prepareToRead (numToDo, start1, size1, start2, size2);
270
271 if (size1 <= 0)
272 return 10;
273
274 writer->writeFromAudioSampleBuffer (buffer, start1, size1);
275
276 const ScopedLock sl (thumbnailLock);
277
278 if (receiver != nullptr)
279 receiver->addBlock (samplesWritten, buffer, start1, size1);
280
281 samplesWritten += size1;
282
283 if (size2 > 0)
284 {
285 writer->writeFromAudioSampleBuffer (buffer, start2, size2);
286
287 if (receiver != nullptr)
288 receiver->addBlock (samplesWritten, buffer, start2, size2);
289
290 samplesWritten += size2;
291 }
292
293 fifo.finishedRead (size1 + size2);
294
295 if (samplesPerFlush > 0)
296 {
297 flushSampleCounter -= size1 + size2;
298
299 if (flushSampleCounter <= 0)
300 {
301 flushSampleCounter = samplesPerFlush;
302 writer->flush();
303 }
304 }
305
306 return 0;
307 }
308
309 void setDataReceiver (IncomingDataReceiver* newReceiver)
310 {
311 if (newReceiver != nullptr)
312 newReceiver->reset (buffer.getNumChannels(), writer->getSampleRate(), 0);
313
314 const ScopedLock sl (thumbnailLock);
315 receiver = newReceiver;
316 samplesWritten = 0;
317 }
318
319 void setFlushInterval (int numSamples) noexcept
320 {
321 samplesPerFlush = numSamples;
322 }
323
324private:
325 AbstractFifo fifo;
326 AudioBuffer<float> buffer;
327 TimeSliceThread& timeSliceThread;
328 std::unique_ptr<AudioFormatWriter> writer;
329 CriticalSection thumbnailLock;
330 IncomingDataReceiver* receiver = {};
331 int64 samplesWritten = 0;
332 int samplesPerFlush = 0, flushSampleCounter = 0;
333 std::atomic<bool> isRunning { true };
334
335 JUCE_DECLARE_NON_COPYABLE (Buffer)
336};
337
339 : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, (int) writer->numChannels, numSamplesToBuffer))
340{
341}
342
346
347bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples)
348{
349 return buffer->write (data, numSamples);
350}
351
356
357void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept
358{
359 buffer->setFlushInterval (numSamplesPerFlush);
360}
361
362} // namespace juce
Type * getWritePointer(int channelNumber) noexcept
int getNumChannels() const noexcept
int getNumSamples() const noexcept
const Type * getReadPointer(int channelNumber) const noexcept
const Type *const * getArrayOfReadPointers() const noexcept
bool read(float *const *destChannels, int numDestChannels, int64 startSampleInSource, int numSamplesToRead)
bool write(const float *const *data, int numSamples)
ThreadedWriter(AudioFormatWriter *writer, TimeSliceThread &backgroundThread, int numSamplesToBuffer)
void setFlushInterval(int numSamplesPerFlush) noexcept
bool writeFromAudioReader(AudioFormatReader &reader, int64 startSample, int64 numSamplesToRead)
bool writeFromFloatArrays(const float *const *channels, int numChannels, int numSamples)
bool writeFromAudioSource(AudioSource &source, int numSamplesToRead, int samplesPerBlock=2048)
virtual bool write(const int **samplesToWrite, int numSamples)=0
int getNumChannels() const noexcept
AudioFormatWriter(OutputStream *destStream, const String &formatName, double sampleRate, unsigned int numberOfChannels, unsigned int bitsPerSample)
bool writeFromAudioSampleBuffer(const AudioBuffer< float > &source, int startSample, int numSamples)
bool isFloatingPoint() const noexcept
virtual void getNextAudioBlock(const AudioSourceChannelInfo &bufferToFill)=0
void removeTimeSliceClient(TimeSliceClient *clientToRemove)
void addTimeSliceClient(TimeSliceClient *clientToAdd, int millisecondsBeforeStarting=0)