/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-10 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online at www.gnu.org/licenses. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #include "../../core/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE #include "juce_AudioFormat.h" #include "../dsp/juce_AudioSampleBuffer.h" #include "../../containers/juce_AbstractFifo.h" //============================================================================== AudioFormatWriter::AudioFormatWriter (OutputStream* const out, const String& formatName_, const double rate, const unsigned int numChannels_, const unsigned int bitsPerSample_) : sampleRate (rate), numChannels (numChannels_), bitsPerSample (bitsPerSample_), usesFloatingPointData (false), output (out), formatName (formatName_) { } AudioFormatWriter::~AudioFormatWriter() { delete output; } bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, int64 startSample, int64 numSamplesToRead) { const int bufferSize = 16384; AudioSampleBuffer tempBuffer (numChannels, bufferSize); int* buffers [128]; zerostruct (buffers); for (int i = tempBuffer.getNumChannels(); --i >= 0;) buffers[i] = reinterpret_cast (tempBuffer.getSampleData (i, 0)); if (numSamplesToRead < 0) numSamplesToRead = reader.lengthInSamples; while (numSamplesToRead > 0) { const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize); if (! reader.read (buffers, numChannels, startSample, numToDo, false)) return false; if (reader.usesFloatingPointData != isFloatingPoint()) { int** bufferChan = buffers; while (*bufferChan != 0) { int* b = *bufferChan++; if (isFloatingPoint()) { // int -> float const double factor = 1.0 / std::numeric_limits::max(); for (int i = 0; i < numToDo; ++i) ((float*) b)[i] = (float) (factor * b[i]); } else { // float -> int for (int i = 0; i < numToDo; ++i) { const double samp = *(const float*) b; if (samp <= -1.0) *b++ = std::numeric_limits::min(); else if (samp >= 1.0) *b++ = std::numeric_limits::max(); else *b++ = roundToInt (std::numeric_limits::max() * samp); } } } } if (! write (const_cast (buffers), numToDo)) return false; numSamplesToRead -= numToDo; startSample += numToDo; } return true; } bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock) { AudioSampleBuffer tempBuffer (getNumChannels(), samplesPerBlock); while (numSamplesToRead > 0) { const int numToDo = jmin (numSamplesToRead, samplesPerBlock); AudioSourceChannelInfo info; info.buffer = &tempBuffer; info.startSample = 0; info.numSamples = numToDo; info.clearActiveBufferRegion(); source.getNextAudioBlock (info); if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo)) return false; numSamplesToRead -= numToDo; } return true; } bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& source, int startSample, int numSamples) { jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && source.getNumChannels() > 0); if (numSamples <= 0) return true; HeapBlock tempBuffer; HeapBlock chans (numChannels + 1); chans [numChannels] = 0; if (isFloatingPoint()) { for (int i = numChannels; --i >= 0;) chans[i] = reinterpret_cast (source.getSampleData (i, startSample)); } else { tempBuffer.malloc (numSamples * numChannels); for (unsigned int i = 0; i < numChannels; ++i) { typedef AudioData::Pointer DestSampleType; typedef AudioData::Pointer SourceSampleType; DestSampleType destData (chans[i] = tempBuffer + i * numSamples); SourceSampleType sourceData (source.getSampleData (i, startSample)); destData.convertSamples (sourceData, numSamples); } } return write ((const int**) chans.getData(), numSamples); } //============================================================================== class AudioFormatWriter::ThreadedWriter::Buffer : public TimeSliceClient, public AbstractFifo { public: Buffer (TimeSliceThread& timeSliceThread_, AudioFormatWriter* writer_, int numChannels, int bufferSize) : AbstractFifo (bufferSize), buffer (numChannels, bufferSize), timeSliceThread (timeSliceThread_), writer (writer_), isRunning (true) { timeSliceThread.addTimeSliceClient (this); } ~Buffer() { isRunning = false; timeSliceThread.removeTimeSliceClient (this); while (useTimeSlice()) {} } bool write (const float** data, int numSamples) { if (numSamples <= 0 || ! isRunning) return true; jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this! int start1, size1, start2, size2; prepareToWrite (numSamples, start1, size1, start2, size2); if (size1 + size2 < numSamples) return false; for (int i = buffer.getNumChannels(); --i >= 0;) { buffer.copyFrom (i, start1, data[i], size1); buffer.copyFrom (i, start2, data[i] + size1, size2); } finishedWrite (size1 + size2); timeSliceThread.notify(); return true; } bool useTimeSlice() { const int numToDo = getTotalSize() / 4; int start1, size1, start2, size2; prepareToRead (numToDo, start1, size1, start2, size2); if (size1 <= 0) return false; writer->writeFromAudioSampleBuffer (buffer, start1, size1); if (size2 > 0) writer->writeFromAudioSampleBuffer (buffer, start2, size2); finishedRead (size1 + size2); return true; } private: AudioSampleBuffer buffer; TimeSliceThread& timeSliceThread; ScopedPointer writer; volatile bool isRunning; Buffer (const Buffer&); Buffer& operator= (const Buffer&); }; AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer) : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, writer->numChannels, numSamplesToBuffer)) { } AudioFormatWriter::ThreadedWriter::~ThreadedWriter() { } bool AudioFormatWriter::ThreadedWriter::write (const float** data, int numSamples) { return buffer->write (data, numSamples); } END_JUCE_NAMESPACE