OpenShot Audio Library | OpenShotAudio  0.6.0
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 
26 namespace 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_),
37  usesFloatingPointData (false),
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_),
52  usesFloatingPointData (false),
53  channelLayout (channelLayout_),
54  output (out),
55  formatName (formatName_)
56 {
57 }
58 
60 {
61  delete output;
62 }
63 
64 static 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 
103  if (reader.usesFloatingPointData != isFloatingPoint())
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 
130 bool 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 
152 bool 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 
189 bool 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 //==============================================================================
214 class AudioFormatWriter::ThreadedWriter::Buffer final : private TimeSliceClient
215 {
216 public:
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 
324 private:
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 
338 AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer)
339  : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, (int) writer->numChannels, numSamplesToBuffer))
340 {
341 }
342 
344 {
345 }
346 
347 bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples)
348 {
349  return buffer->write (data, numSamples);
350 }
351 
353 {
354  buffer->setDataReceiver (receiver);
355 }
356 
357 void 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 *const * getArrayOfReadPointers() const noexcept
const Type * getReadPointer(int channelNumber) 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