OpenShot Library | OpenShotAudio  0.2.2
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 //==============================================================================
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
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.
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 ** getArrayOfReadPointers() const noexcept
Returns an array of pointers to the channels in the buffer.
const Type * getReadPointer(int channelNumber) const noexcept
Returns a pointer to an array of read-only samples in one of the buffer's channels.
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.