OpenShot Audio Library | OpenShotAudio  0.3.1
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 
27 namespace 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 
65 static 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 
104  if (reader.usesFloatingPointData != isFloatingPoint())
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 
129 bool 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 
151 bool 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 
188 bool 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 //==============================================================================
213 class AudioFormatWriter::ThreadedWriter::Buffer : private TimeSliceClient
214 {
215 public:
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 
323 private:
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 
337 AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer)
338  : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, (int) writer->numChannels, numSamplesToBuffer))
339 {
340 }
341 
343 {
344 }
345 
346 bool 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 
356 void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept
357 {
358  buffer->setFlushInterval (numSamplesPerFlush);
359 }
360 
361 } // namespace juce
bool writeFromAudioReader(AudioFormatReader &reader, int64 startSample, int64 numSamplesToRead)
virtual void getNextAudioBlock(const AudioSourceChannelInfo &bufferToFill)=0
bool write(const float *const *data, int numSamples)
virtual bool write(const int **samplesToWrite, int numSamples)=0
static void JUCE_CALLTYPE convertFixedToFloat(float *dest, const int *src, float multiplier, int numValues) noexcept
AudioFormatWriter(OutputStream *destStream, const String &formatName, double sampleRate, unsigned int numberOfChannels, unsigned int bitsPerSample)
const Type * getReadPointer(int channelNumber) const noexcept
int getNumChannels() const noexcept
bool isFloatingPoint() const noexcept
bool writeFromAudioSampleBuffer(const AudioBuffer< float > &source, int startSample, int numSamples)
bool writeFromFloatArrays(const float *const *channels, int numChannels, int numSamples)
bool writeFromAudioSource(AudioSource &source, int numSamplesToRead, int samplesPerBlock=2048)
bool read(float *const *destChannels, int numDestChannels, int64 startSampleInSource, int numSamplesToRead)
Type * getWritePointer(int channelNumber) noexcept
int getNumChannels() const noexcept
void setFlushInterval(int numSamplesPerFlush) noexcept
int getNumSamples() const noexcept
const Type ** getArrayOfReadPointers() const noexcept
ThreadedWriter(AudioFormatWriter *writer, TimeSliceThread &backgroundThread, int numSamplesToBuffer)