migrating to the latest JUCE version
This commit is contained in:
		
							
								
								
									
										310
									
								
								deps/juce/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								deps/juce/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,310 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ARAAudioSourceReader::ARAAudioSourceReader (ARAAudioSource* audioSource)
 | 
			
		||||
    : AudioFormatReader (nullptr, "ARAAudioSourceReader"),
 | 
			
		||||
      audioSourceBeingRead (audioSource)
 | 
			
		||||
{
 | 
			
		||||
    jassert (audioSourceBeingRead != nullptr);
 | 
			
		||||
 | 
			
		||||
    bitsPerSample = 32;
 | 
			
		||||
    usesFloatingPointData = true;
 | 
			
		||||
    sampleRate = audioSourceBeingRead->getSampleRate();
 | 
			
		||||
    numChannels = (unsigned int) audioSourceBeingRead->getChannelCount();
 | 
			
		||||
    lengthInSamples = audioSourceBeingRead->getSampleCount();
 | 
			
		||||
    tmpPtrs.resize (numChannels);
 | 
			
		||||
 | 
			
		||||
    audioSourceBeingRead->addListener (this);
 | 
			
		||||
 | 
			
		||||
    if (audioSourceBeingRead->isSampleAccessEnabled())
 | 
			
		||||
        hostReader.reset (new ARA::PlugIn::HostAudioReader (audioSourceBeingRead));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ARAAudioSourceReader::~ARAAudioSourceReader()
 | 
			
		||||
{
 | 
			
		||||
    invalidate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAAudioSourceReader::invalidate()
 | 
			
		||||
{
 | 
			
		||||
    ScopedWriteLock scopedLock (lock);
 | 
			
		||||
 | 
			
		||||
    if (! isValid())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    hostReader.reset();
 | 
			
		||||
 | 
			
		||||
    audioSourceBeingRead->removeListener (this);
 | 
			
		||||
    audioSourceBeingRead = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAAudioSourceReader::willUpdateAudioSourceProperties (ARAAudioSource* audioSource,
 | 
			
		||||
                                                            ARAAudioSource::PropertiesPtr newProperties)
 | 
			
		||||
{
 | 
			
		||||
    if (audioSource->getSampleCount() != newProperties->sampleCount
 | 
			
		||||
        || audioSource->getSampleRate() != newProperties->sampleRate
 | 
			
		||||
        || audioSource->getChannelCount() != newProperties->channelCount)
 | 
			
		||||
    {
 | 
			
		||||
        invalidate();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAAudioSourceReader::doUpdateAudioSourceContent (ARAAudioSource* audioSource,
 | 
			
		||||
                                                       ARAContentUpdateScopes scopeFlags)
 | 
			
		||||
{
 | 
			
		||||
    jassertquiet (audioSourceBeingRead == audioSource);
 | 
			
		||||
 | 
			
		||||
    // Don't invalidate if the audio signal is unchanged
 | 
			
		||||
    if (scopeFlags.affectSamples())
 | 
			
		||||
        invalidate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAAudioSourceReader::willEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    jassertquiet (audioSourceBeingRead == audioSource);
 | 
			
		||||
 | 
			
		||||
    // Invalidate our reader if sample access is disabled
 | 
			
		||||
    if (! enable)
 | 
			
		||||
    {
 | 
			
		||||
        ScopedWriteLock scopedLock (lock);
 | 
			
		||||
        hostReader.reset();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAAudioSourceReader::didEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    jassertquiet (audioSourceBeingRead == audioSource);
 | 
			
		||||
 | 
			
		||||
    // Recreate our reader if sample access is enabled
 | 
			
		||||
    if (enable && isValid())
 | 
			
		||||
    {
 | 
			
		||||
        ScopedWriteLock scopedLock (lock);
 | 
			
		||||
        hostReader.reset (new ARA::PlugIn::HostAudioReader (audioSourceBeingRead));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAAudioSourceReader::willDestroyAudioSource (ARAAudioSource* audioSource)
 | 
			
		||||
{
 | 
			
		||||
    jassertquiet (audioSourceBeingRead == audioSource);
 | 
			
		||||
 | 
			
		||||
    invalidate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ARAAudioSourceReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                                        int64 startSampleInFile, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    const auto destSize = (bitsPerSample / 8) * (size_t) numSamples;
 | 
			
		||||
    const auto bufferOffset = (int) (bitsPerSample / 8) * startOffsetInDestBuffer;
 | 
			
		||||
 | 
			
		||||
    if (isValid() && hostReader != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedTryReadLock readLock (lock);
 | 
			
		||||
 | 
			
		||||
        if (readLock.isLocked())
 | 
			
		||||
        {
 | 
			
		||||
            for (size_t i = 0; i < tmpPtrs.size(); ++i)
 | 
			
		||||
            {
 | 
			
		||||
                if ((i < (size_t) numDestChannels) && (destSamples[i] != nullptr))
 | 
			
		||||
                {
 | 
			
		||||
                    tmpPtrs[i] = ((uint8_t*) destSamples[i]) + bufferOffset;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    // We need to provide destination pointers for all channels in the ARA read call, even if
 | 
			
		||||
                    // readSamples is not reading all of them. Hence we use this dummy buffer to pad the read
 | 
			
		||||
                    // destination area.
 | 
			
		||||
                    static thread_local std::vector<uint8_t> dummyBuffer;
 | 
			
		||||
 | 
			
		||||
                    if (destSize > dummyBuffer.size())
 | 
			
		||||
                        dummyBuffer.resize (destSize);
 | 
			
		||||
 | 
			
		||||
                    tmpPtrs[i] = dummyBuffer.data();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return hostReader->readAudioSamples (startSampleInFile, numSamples, tmpPtrs.data());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numDestChannels; ++i)
 | 
			
		||||
        if (destSamples[i] != nullptr)
 | 
			
		||||
            zeromem (((uint8_t*) destSamples[i]) + bufferOffset, destSize);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ARAPlaybackRegionReader::ARAPlaybackRegionReader (ARAPlaybackRegion* playbackRegion)
 | 
			
		||||
    : ARAPlaybackRegionReader (playbackRegion->getAudioModification()->getAudioSource()->getSampleRate(),
 | 
			
		||||
                               playbackRegion->getAudioModification()->getAudioSource()->getChannelCount(),
 | 
			
		||||
                               { playbackRegion })
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
ARAPlaybackRegionReader::ARAPlaybackRegionReader (double rate, int numChans,
 | 
			
		||||
                                                  const std::vector<ARAPlaybackRegion*>& playbackRegions)
 | 
			
		||||
    : AudioFormatReader (nullptr, "ARAPlaybackRegionReader")
 | 
			
		||||
{
 | 
			
		||||
    // We're only providing the minimal set of meaningful values, since the ARA renderer should only
 | 
			
		||||
    // look at the time position and the playing state, and read any related tempo or bar signature
 | 
			
		||||
    // information from the ARA model directly (MusicalContext).
 | 
			
		||||
    positionInfo.setIsPlaying (true);
 | 
			
		||||
 | 
			
		||||
    sampleRate = rate;
 | 
			
		||||
    numChannels = (unsigned int) numChans;
 | 
			
		||||
    bitsPerSample = 32;
 | 
			
		||||
    usesFloatingPointData = true;
 | 
			
		||||
 | 
			
		||||
    auto* documentController = (! playbackRegions.empty())
 | 
			
		||||
                                   ? playbackRegions.front()->getDocumentController<ARADocumentController>()
 | 
			
		||||
                                   : nullptr;
 | 
			
		||||
 | 
			
		||||
    playbackRenderer.reset (documentController ? static_cast<ARAPlaybackRenderer*> (documentController->doCreatePlaybackRenderer())
 | 
			
		||||
                                               : nullptr);
 | 
			
		||||
 | 
			
		||||
    if (playbackRenderer != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        double regionsStartTime = std::numeric_limits<double>::max();
 | 
			
		||||
        double regionsEndTime = std::numeric_limits<double>::lowest();
 | 
			
		||||
 | 
			
		||||
        for (const auto& playbackRegion : playbackRegions)
 | 
			
		||||
        {
 | 
			
		||||
            jassert (playbackRegion->getDocumentController() == documentController);
 | 
			
		||||
            auto playbackRegionTimeRange = playbackRegion->getTimeRange (ARAPlaybackRegion::IncludeHeadAndTail::yes);
 | 
			
		||||
            regionsStartTime = jmin (regionsStartTime, playbackRegionTimeRange.getStart());
 | 
			
		||||
            regionsEndTime = jmax (regionsEndTime, playbackRegionTimeRange.getEnd());
 | 
			
		||||
 | 
			
		||||
            playbackRenderer->addPlaybackRegion (ARA::PlugIn::toRef (playbackRegion));
 | 
			
		||||
            playbackRegion->addListener (this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        startInSamples = (int64) (regionsStartTime * sampleRate + 0.5);
 | 
			
		||||
        lengthInSamples = (int64) ((regionsEndTime - regionsStartTime) * sampleRate + 0.5);
 | 
			
		||||
 | 
			
		||||
        playbackRenderer->prepareToPlay (rate,
 | 
			
		||||
                                         maximumBlockSize,
 | 
			
		||||
                                         numChans,
 | 
			
		||||
                                         AudioProcessor::ProcessingPrecision::singlePrecision,
 | 
			
		||||
                                         ARARenderer::AlwaysNonRealtime::yes);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        startInSamples = 0;
 | 
			
		||||
        lengthInSamples = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ARAPlaybackRegionReader::~ARAPlaybackRegionReader()
 | 
			
		||||
{
 | 
			
		||||
    invalidate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAPlaybackRegionReader::invalidate()
 | 
			
		||||
{
 | 
			
		||||
    ScopedWriteLock scopedWrite (lock);
 | 
			
		||||
 | 
			
		||||
    if (! isValid())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    for (auto& playbackRegion : playbackRenderer->getPlaybackRegions())
 | 
			
		||||
        playbackRegion->removeListener (this);
 | 
			
		||||
 | 
			
		||||
    playbackRenderer->releaseResources();
 | 
			
		||||
    playbackRenderer.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ARAPlaybackRegionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                                           int64 startSampleInFile, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    bool success = false;
 | 
			
		||||
    bool needClearSamples = true;
 | 
			
		||||
 | 
			
		||||
    const ScopedTryReadLock readLock (lock);
 | 
			
		||||
 | 
			
		||||
    if (readLock.isLocked())
 | 
			
		||||
    {
 | 
			
		||||
        if (isValid())
 | 
			
		||||
        {
 | 
			
		||||
            success = true;
 | 
			
		||||
            needClearSamples = false;
 | 
			
		||||
            positionInfo.setTimeInSamples (startSampleInFile + startInSamples);
 | 
			
		||||
 | 
			
		||||
            while (numSamples > 0)
 | 
			
		||||
            {
 | 
			
		||||
                const int numSliceSamples = jmin (numSamples, maximumBlockSize);
 | 
			
		||||
                AudioBuffer<float> buffer ((float **) destSamples, numDestChannels, startOffsetInDestBuffer, numSliceSamples);
 | 
			
		||||
                positionInfo.setTimeInSeconds (static_cast<double> (*positionInfo.getTimeInSamples()) / sampleRate);
 | 
			
		||||
                success &= playbackRenderer->processBlock (buffer, AudioProcessor::Realtime::no, positionInfo);
 | 
			
		||||
                numSamples -= numSliceSamples;
 | 
			
		||||
                startOffsetInDestBuffer += numSliceSamples;
 | 
			
		||||
                positionInfo.setTimeInSamples (*positionInfo.getTimeInSamples() + numSliceSamples);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (needClearSamples)
 | 
			
		||||
        for (int chan_i = 0; chan_i < numDestChannels; ++chan_i)
 | 
			
		||||
            FloatVectorOperations::clear ((float *) destSamples[chan_i], numSamples);
 | 
			
		||||
 | 
			
		||||
    return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAPlaybackRegionReader::willUpdatePlaybackRegionProperties (ARAPlaybackRegion* playbackRegion, ARAPlaybackRegion::PropertiesPtr newProperties)
 | 
			
		||||
{
 | 
			
		||||
    jassert (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion));
 | 
			
		||||
 | 
			
		||||
    if ((playbackRegion->getStartInAudioModificationTime() != newProperties->startInModificationTime)
 | 
			
		||||
        || (playbackRegion->getDurationInAudioModificationTime() != newProperties->durationInModificationTime)
 | 
			
		||||
        || (playbackRegion->getStartInPlaybackTime() != newProperties->startInPlaybackTime)
 | 
			
		||||
        || (playbackRegion->getDurationInPlaybackTime() != newProperties->durationInPlaybackTime)
 | 
			
		||||
        || (playbackRegion->isTimestretchEnabled() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationTimestretch) != 0))
 | 
			
		||||
        || (playbackRegion->isTimeStretchReflectingTempo() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationTimestretchReflectingTempo) != 0))
 | 
			
		||||
        || (playbackRegion->hasContentBasedFadeAtHead() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationContentBasedFadeAtHead) != 0))
 | 
			
		||||
        || (playbackRegion->hasContentBasedFadeAtTail() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationContentBasedFadeAtTail) != 0)))
 | 
			
		||||
    {
 | 
			
		||||
        invalidate();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAPlaybackRegionReader::didUpdatePlaybackRegionContent (ARAPlaybackRegion* playbackRegion,
 | 
			
		||||
                                                              ARAContentUpdateScopes scopeFlags)
 | 
			
		||||
{
 | 
			
		||||
    jassertquiet (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion));
 | 
			
		||||
 | 
			
		||||
    // Invalidate if the audio signal is changed
 | 
			
		||||
    if (scopeFlags.affectSamples())
 | 
			
		||||
        invalidate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARAPlaybackRegionReader::willDestroyPlaybackRegion (ARAPlaybackRegion* playbackRegion)
 | 
			
		||||
{
 | 
			
		||||
    jassertquiet (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion));
 | 
			
		||||
 | 
			
		||||
    invalidate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										194
									
								
								deps/juce/modules/juce_audio_formats/format/juce_ARAAudioReaders.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								deps/juce/modules/juce_audio_formats/format/juce_ARAAudioReaders.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class AudioProcessor;
 | 
			
		||||
 | 
			
		||||
/* All these readers follow a common pattern of "invalidation":
 | 
			
		||||
 | 
			
		||||
   Whenever the samples they are reading are altered, the readers become invalid and will stop
 | 
			
		||||
   accessing the model graph. These alterations are model edits such as property changes, content
 | 
			
		||||
   changes (if affecting sample scope), or the deletion of some model object involved in the read
 | 
			
		||||
   process. Since these edits are performed on the document controller thread, reader validity can
 | 
			
		||||
   immediately be checked after the edit has been concluded, and any reader that has become invalid
 | 
			
		||||
   can be recreated.
 | 
			
		||||
 | 
			
		||||
   Note that encountering a failure in any individual read call does not invalidate the reader, so
 | 
			
		||||
   that the entity using the reader can decide whether to retry or to back out. This includes trying
 | 
			
		||||
   to read an audio source for which the host has currently disabled access: the failure will be
 | 
			
		||||
   immediately visible, but the reader will remain valid. This ensures that for example a realtime
 | 
			
		||||
   renderer can just keep reading and will be seeing proper samples again once sample access is
 | 
			
		||||
   re-enabled.
 | 
			
		||||
 | 
			
		||||
   If desired, the code calling readSamples() can also implement proper signaling of any read error
 | 
			
		||||
   to the document controller thread to trigger rebuilding the reader as needed. This will typically
 | 
			
		||||
   be done when implementing audio source analysis: if there is an error upon reading the samples
 | 
			
		||||
   that cannot be resolved within a reasonable timeout, then the analysis would be aborted. The
 | 
			
		||||
   document controller code that monitors the analysis tasks can evaluate this and re-launch a new
 | 
			
		||||
   analysis when appropriate (e.g. when access is re-enabled).
 | 
			
		||||
 | 
			
		||||
   When reading playback regions (directly or through a region sequence reader), the reader will
 | 
			
		||||
   represent the regions as a single source object that covers the union of all affected regions.
 | 
			
		||||
   The first sample produced by the reader thus will be the first sample of the earliest region.
 | 
			
		||||
   This means that the location of this region has to be taken into account by the calling code if
 | 
			
		||||
   it wants to relate the samples to the model or any other reader output.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Subclass of AudioFormatReader that reads samples from a single ARA audio source.
 | 
			
		||||
 | 
			
		||||
    Plug-Ins typically use this from their rendering code, wrapped in a BufferingAudioReader
 | 
			
		||||
    to bridge between realtime rendering and non-realtime audio reading.
 | 
			
		||||
 | 
			
		||||
    The reader becomes invalidated if
 | 
			
		||||
        - the audio source content is updated in a way that affects its samples,
 | 
			
		||||
        - the audio source sample access is disabled, or
 | 
			
		||||
        - the audio source being read is destroyed.
 | 
			
		||||
 | 
			
		||||
    @tags{ARA}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ARAAudioSourceReader  : public AudioFormatReader,
 | 
			
		||||
                                        private ARAAudioSource::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Use an ARAAudioSource to construct an audio source reader for the given \p audioSource. */
 | 
			
		||||
    explicit ARAAudioSourceReader (ARAAudioSource* audioSource);
 | 
			
		||||
 | 
			
		||||
    ~ARAAudioSourceReader() override;
 | 
			
		||||
 | 
			
		||||
    bool readSamples (int** destSamples,
 | 
			
		||||
                      int numDestChannels,
 | 
			
		||||
                      int startOffsetInDestBuffer,
 | 
			
		||||
                      int64 startSampleInFile,
 | 
			
		||||
                      int numSamples) override;
 | 
			
		||||
 | 
			
		||||
    /** Returns true as long as the reader's underlying ARAAudioSource remains accessible and its
 | 
			
		||||
        sample content is not changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool isValid() const { return audioSourceBeingRead != nullptr; }
 | 
			
		||||
 | 
			
		||||
    /** Invalidate the reader - the reader will call this internally if needed, but can also be
 | 
			
		||||
        invalidated from the outside (from message thread only!).
 | 
			
		||||
    */
 | 
			
		||||
    void invalidate();
 | 
			
		||||
 | 
			
		||||
    void willUpdateAudioSourceProperties (ARAAudioSource* audioSource,
 | 
			
		||||
                                          ARAAudioSource::PropertiesPtr newProperties) override;
 | 
			
		||||
    void doUpdateAudioSourceContent (ARAAudioSource* audioSource,
 | 
			
		||||
                                     ARAContentUpdateScopes scopeFlags) override;
 | 
			
		||||
    void willEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable) override;
 | 
			
		||||
    void didEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable) override;
 | 
			
		||||
    void willDestroyAudioSource (ARAAudioSource* audioSource) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ARAAudioSource* audioSourceBeingRead;
 | 
			
		||||
    std::unique_ptr<ARA::PlugIn::HostAudioReader> hostReader;
 | 
			
		||||
    ReadWriteLock lock;
 | 
			
		||||
    std::vector<void*> tmpPtrs;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAAudioSourceReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Subclass of AudioFormatReader that reads samples from a group of playback regions.
 | 
			
		||||
 | 
			
		||||
    Plug-Ins typically use this to draw the output of a playback region in their UI.
 | 
			
		||||
 | 
			
		||||
    In order to read from playback regions, the reader requires an audio processor that acts as ARA
 | 
			
		||||
    playback renderer. Configuring the audio processor for real-time operation results in the reader
 | 
			
		||||
    being real-time capable too, unlike most other AudioFormatReaders. The reader instance will take
 | 
			
		||||
    care of adding all regions being read to the renderer and invoke its processBlock function in
 | 
			
		||||
    order to read the region samples.
 | 
			
		||||
 | 
			
		||||
    The reader becomes invalid if
 | 
			
		||||
        - any region properties are updated in a way that would affect its samples,
 | 
			
		||||
        - any region content is updated in a way that would affect its samples, or
 | 
			
		||||
        - any of its regions are destroyed.
 | 
			
		||||
 | 
			
		||||
    @tags{ARA}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ARAPlaybackRegionReader   : public AudioFormatReader,
 | 
			
		||||
                                            private ARAPlaybackRegion::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Create an ARAPlaybackRegionReader instance to read the given \p playbackRegion, using the
 | 
			
		||||
        sample rate and channel count of the underlying ARAAudioSource.
 | 
			
		||||
 | 
			
		||||
        @param playbackRegion The playback region that should be read - must not be nullptr!
 | 
			
		||||
    */
 | 
			
		||||
    explicit ARAPlaybackRegionReader (ARAPlaybackRegion* playbackRegion);
 | 
			
		||||
 | 
			
		||||
    /** Create an ARAPlaybackRegionReader instance to read the given \p playbackRegions
 | 
			
		||||
 | 
			
		||||
        @param sampleRate      The sample rate that should be used for reading.
 | 
			
		||||
        @param numChannels     The channel count that should be used for reading.
 | 
			
		||||
        @param playbackRegions The vector of playback regions that should be read - must not be empty!
 | 
			
		||||
                               All regions must be part of the same ARADocument.
 | 
			
		||||
    */
 | 
			
		||||
    ARAPlaybackRegionReader (double sampleRate, int numChannels,
 | 
			
		||||
                             const std::vector<ARAPlaybackRegion*>& playbackRegions);
 | 
			
		||||
 | 
			
		||||
    ~ARAPlaybackRegionReader() override;
 | 
			
		||||
 | 
			
		||||
    /** Returns true as long as any of the reader's underlying playback region's haven't changed. */
 | 
			
		||||
    bool isValid() const { return (playbackRenderer != nullptr); }
 | 
			
		||||
 | 
			
		||||
    /** Invalidate the reader - this should be called if the sample content of any of the reader's
 | 
			
		||||
        ARAPlaybackRegions changes.
 | 
			
		||||
    */
 | 
			
		||||
    void invalidate();
 | 
			
		||||
 | 
			
		||||
    bool readSamples (int** destSamples,
 | 
			
		||||
                      int numDestChannels,
 | 
			
		||||
                      int startOffsetInDestBuffer,
 | 
			
		||||
                      int64 startSampleInFile,
 | 
			
		||||
                      int numSamples) override;
 | 
			
		||||
 | 
			
		||||
    void willUpdatePlaybackRegionProperties (ARAPlaybackRegion* playbackRegion,
 | 
			
		||||
                                             ARAPlaybackRegion::PropertiesPtr newProperties) override;
 | 
			
		||||
    void didUpdatePlaybackRegionContent (ARAPlaybackRegion* playbackRegion,
 | 
			
		||||
                                         ARAContentUpdateScopes scopeFlags) override;
 | 
			
		||||
    void willDestroyPlaybackRegion (ARAPlaybackRegion* playbackRegion) override;
 | 
			
		||||
 | 
			
		||||
    /** The starting point of the reader in playback samples */
 | 
			
		||||
    int64 startInSamples = 0;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<ARAPlaybackRenderer> playbackRenderer;
 | 
			
		||||
    AudioPlayHead::PositionInfo positionInfo;
 | 
			
		||||
    ReadWriteLock lock;
 | 
			
		||||
 | 
			
		||||
    static constexpr int maximumBlockSize = 4 * 1024;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAPlaybackRegionReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
@@ -1,91 +1,91 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioFormat::AudioFormat (String name, StringArray extensions)
 | 
			
		||||
   : formatName (name), fileExtensions (extensions)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormat::AudioFormat (StringRef name, StringRef extensions)
 | 
			
		||||
   : formatName (name.text), fileExtensions (StringArray::fromTokens (extensions, false))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormat::~AudioFormat()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormat::canHandleFile (const File& f)
 | 
			
		||||
{
 | 
			
		||||
    for (auto& e : getFileExtensions())
 | 
			
		||||
        if (f.hasFileExtension (e))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const String& AudioFormat::getFormatName() const                { return formatName; }
 | 
			
		||||
StringArray AudioFormat::getFileExtensions() const              { return fileExtensions; }
 | 
			
		||||
bool AudioFormat::isCompressed()                                { return false; }
 | 
			
		||||
StringArray AudioFormat::getQualityOptions()                    { return {}; }
 | 
			
		||||
 | 
			
		||||
MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (const File&)
 | 
			
		||||
{
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (FileInputStream* fin)
 | 
			
		||||
{
 | 
			
		||||
    delete fin;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormat::isChannelLayoutSupported (const AudioChannelSet& channelSet)
 | 
			
		||||
{
 | 
			
		||||
    if (channelSet == AudioChannelSet::mono())      return canDoMono();
 | 
			
		||||
    if (channelSet == AudioChannelSet::stereo())    return canDoStereo();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter* AudioFormat::createWriterFor (OutputStream* streamToWriteTo,
 | 
			
		||||
                                                 double sampleRateToUse,
 | 
			
		||||
                                                 const AudioChannelSet& channelLayout,
 | 
			
		||||
                                                 int bitsPerSample,
 | 
			
		||||
                                                 const StringPairArray& metadataValues,
 | 
			
		||||
                                                 int qualityOptionIndex)
 | 
			
		||||
{
 | 
			
		||||
    if (isChannelLayoutSupported (channelLayout))
 | 
			
		||||
        return createWriterFor (streamToWriteTo, sampleRateToUse,
 | 
			
		||||
                                static_cast<unsigned int> (channelLayout.size()),
 | 
			
		||||
                                bitsPerSample, metadataValues, qualityOptionIndex);
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioFormat::AudioFormat (String name, StringArray extensions)
 | 
			
		||||
   : formatName (name), fileExtensions (extensions)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormat::AudioFormat (StringRef name, StringRef extensions)
 | 
			
		||||
   : formatName (name.text), fileExtensions (StringArray::fromTokens (extensions, false))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormat::~AudioFormat()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormat::canHandleFile (const File& f)
 | 
			
		||||
{
 | 
			
		||||
    for (auto& e : getFileExtensions())
 | 
			
		||||
        if (f.hasFileExtension (e))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const String& AudioFormat::getFormatName() const                { return formatName; }
 | 
			
		||||
StringArray AudioFormat::getFileExtensions() const              { return fileExtensions; }
 | 
			
		||||
bool AudioFormat::isCompressed()                                { return false; }
 | 
			
		||||
StringArray AudioFormat::getQualityOptions()                    { return {}; }
 | 
			
		||||
 | 
			
		||||
MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (const File&)
 | 
			
		||||
{
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (FileInputStream* fin)
 | 
			
		||||
{
 | 
			
		||||
    delete fin;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormat::isChannelLayoutSupported (const AudioChannelSet& channelSet)
 | 
			
		||||
{
 | 
			
		||||
    if (channelSet == AudioChannelSet::mono())      return canDoMono();
 | 
			
		||||
    if (channelSet == AudioChannelSet::stereo())    return canDoStereo();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter* AudioFormat::createWriterFor (OutputStream* streamToWriteTo,
 | 
			
		||||
                                                 double sampleRateToUse,
 | 
			
		||||
                                                 const AudioChannelSet& channelLayout,
 | 
			
		||||
                                                 int bitsPerSample,
 | 
			
		||||
                                                 const StringPairArray& metadataValues,
 | 
			
		||||
                                                 int qualityOptionIndex)
 | 
			
		||||
{
 | 
			
		||||
    if (isChannelLayoutSupported (channelLayout))
 | 
			
		||||
        return createWriterFor (streamToWriteTo, sampleRateToUse,
 | 
			
		||||
                                static_cast<unsigned int> (channelLayout.size()),
 | 
			
		||||
                                bitsPerSample, metadataValues, qualityOptionIndex);
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,216 +1,216 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Subclasses of AudioFormat are used to read and write different audio
 | 
			
		||||
    file formats.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormatReader, AudioFormatWriter, WavAudioFormat, AiffAudioFormat
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormat
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~AudioFormat();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the name of this format.
 | 
			
		||||
        e.g. "WAV file" or "AIFF file"
 | 
			
		||||
    */
 | 
			
		||||
    const String& getFormatName() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns all the file extensions that might apply to a file of this format.
 | 
			
		||||
        The first item will be the one that's preferred when creating a new file.
 | 
			
		||||
        So for a wav file this might just return ".wav"; for an AIFF file it might
 | 
			
		||||
        return two items, ".aif" and ".aiff"
 | 
			
		||||
    */
 | 
			
		||||
    virtual StringArray getFileExtensions() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if this the given file can be read by this format.
 | 
			
		||||
        Subclasses shouldn't do too much work here, just check the extension or
 | 
			
		||||
        file type. The base class implementation just checks the file's extension
 | 
			
		||||
        against one of the ones that was registered in the constructor.
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool canHandleFile (const File& fileToTest);
 | 
			
		||||
 | 
			
		||||
    /** Returns a set of sample rates that the format can read and write. */
 | 
			
		||||
    virtual Array<int> getPossibleSampleRates() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns a set of bit depths that the format can read and write. */
 | 
			
		||||
    virtual Array<int> getPossibleBitDepths() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the format can do 2-channel audio. */
 | 
			
		||||
    virtual bool canDoStereo() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the format can do 1-channel audio. */
 | 
			
		||||
    virtual bool canDoMono() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the format uses compressed data. */
 | 
			
		||||
    virtual bool isCompressed();
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the channel layout is supported by this format. */
 | 
			
		||||
    virtual bool isChannelLayoutSupported (const AudioChannelSet& channelSet);
 | 
			
		||||
 | 
			
		||||
    /** Returns a list of different qualities that can be used when writing.
 | 
			
		||||
 | 
			
		||||
        Non-compressed formats will just return an empty array, but for something
 | 
			
		||||
        like Ogg-Vorbis or MP3, it might return a list of bit-rates, etc.
 | 
			
		||||
 | 
			
		||||
        When calling createWriterFor(), an index from this array is passed in to
 | 
			
		||||
        tell the format which option is required.
 | 
			
		||||
    */
 | 
			
		||||
    virtual StringArray getQualityOptions();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Tries to create an object that can read from a stream containing audio
 | 
			
		||||
        data in this format.
 | 
			
		||||
 | 
			
		||||
        The reader object that is returned can be used to read from the stream, and
 | 
			
		||||
        should then be deleted by the caller.
 | 
			
		||||
 | 
			
		||||
        @param sourceStream                 the stream to read from - the AudioFormatReader object
 | 
			
		||||
                                            that is returned will delete this stream when it no longer
 | 
			
		||||
                                            needs it.
 | 
			
		||||
        @param deleteStreamIfOpeningFails   if no reader can be created, this determines whether this method
 | 
			
		||||
                                            should delete the stream object that was passed-in. (If a valid
 | 
			
		||||
                                            reader is returned, it will always be in charge of deleting the
 | 
			
		||||
                                            stream, so this parameter is ignored)
 | 
			
		||||
        @see AudioFormatReader
 | 
			
		||||
    */
 | 
			
		||||
    virtual AudioFormatReader* createReaderFor (InputStream* sourceStream,
 | 
			
		||||
                                                bool deleteStreamIfOpeningFails) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Attempts to create a MemoryMappedAudioFormatReader, if possible for this format.
 | 
			
		||||
        If the format does not support this, the method will return nullptr;
 | 
			
		||||
    */
 | 
			
		||||
    virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file);
 | 
			
		||||
    virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (FileInputStream* fin);
 | 
			
		||||
 | 
			
		||||
    /** Tries to create an object that can write to a stream with this audio format.
 | 
			
		||||
 | 
			
		||||
        The writer object that is returned can be used to write to the stream, and
 | 
			
		||||
        should then be deleted by the caller.
 | 
			
		||||
 | 
			
		||||
        If the stream can't be created for some reason (e.g. the parameters passed in
 | 
			
		||||
        here aren't suitable), this will return nullptr.
 | 
			
		||||
 | 
			
		||||
        @param streamToWriteTo      the stream that the data will go to - this will be
 | 
			
		||||
                                    deleted by the AudioFormatWriter object when it's no longer
 | 
			
		||||
                                    needed. If no AudioFormatWriter can be created by this method,
 | 
			
		||||
                                    the stream will NOT be deleted, so that the caller can re-use it
 | 
			
		||||
                                    to try to open a different format, etc
 | 
			
		||||
        @param sampleRateToUse      the sample rate for the file, which must be one of the ones
 | 
			
		||||
                                    returned by getPossibleSampleRates()
 | 
			
		||||
        @param numberOfChannels     the number of channels
 | 
			
		||||
        @param bitsPerSample        the bits per sample to use - this must be one of the values
 | 
			
		||||
                                    returned by getPossibleBitDepths()
 | 
			
		||||
        @param metadataValues       a set of metadata values that the writer should try to write
 | 
			
		||||
                                    to the stream. Exactly what these are depends on the format,
 | 
			
		||||
                                    and the subclass doesn't actually have to do anything with
 | 
			
		||||
                                    them if it doesn't want to. Have a look at the specific format
 | 
			
		||||
                                    implementation classes to see possible values that can be
 | 
			
		||||
                                    used
 | 
			
		||||
        @param qualityOptionIndex   the index of one of compression qualities returned by the
 | 
			
		||||
                                    getQualityOptions() method. If there aren't any quality options
 | 
			
		||||
                                    for this format, just pass 0 in this parameter, as it'll be
 | 
			
		||||
                                    ignored
 | 
			
		||||
        @see AudioFormatWriter
 | 
			
		||||
    */
 | 
			
		||||
    virtual AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
 | 
			
		||||
                                                double sampleRateToUse,
 | 
			
		||||
                                                unsigned int numberOfChannels,
 | 
			
		||||
                                                int bitsPerSample,
 | 
			
		||||
                                                const StringPairArray& metadataValues,
 | 
			
		||||
                                                int qualityOptionIndex) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Tries to create an object that can write to a stream with this audio format.
 | 
			
		||||
 | 
			
		||||
        The writer object that is returned can be used to write to the stream, and
 | 
			
		||||
        should then be deleted by the caller.
 | 
			
		||||
 | 
			
		||||
        If the stream can't be created for some reason (e.g. the parameters passed in
 | 
			
		||||
        here aren't suitable), this will return nullptr.
 | 
			
		||||
 | 
			
		||||
        @param streamToWriteTo      the stream that the data will go to - this will be
 | 
			
		||||
                                    deleted by the AudioFormatWriter object when it's no longer
 | 
			
		||||
                                    needed. If no AudioFormatWriter can be created by this method,
 | 
			
		||||
                                    the stream will NOT be deleted, so that the caller can re-use it
 | 
			
		||||
                                    to try to open a different format, etc
 | 
			
		||||
        @param sampleRateToUse      the sample rate for the file, which must be one of the ones
 | 
			
		||||
                                    returned by getPossibleSampleRates()
 | 
			
		||||
        @param channelLayout        the channel layout for the file. Use isChannelLayoutSupported
 | 
			
		||||
                                    to check if the writer supports this layout.
 | 
			
		||||
        @param bitsPerSample        the bits per sample to use - this must be one of the values
 | 
			
		||||
                                    returned by getPossibleBitDepths()
 | 
			
		||||
        @param metadataValues       a set of metadata values that the writer should try to write
 | 
			
		||||
                                    to the stream. Exactly what these are depends on the format,
 | 
			
		||||
                                    and the subclass doesn't actually have to do anything with
 | 
			
		||||
                                    them if it doesn't want to. Have a look at the specific format
 | 
			
		||||
                                    implementation classes to see possible values that can be
 | 
			
		||||
                                    used
 | 
			
		||||
        @param qualityOptionIndex   the index of one of compression qualities returned by the
 | 
			
		||||
                                    getQualityOptions() method. If there aren't any quality options
 | 
			
		||||
                                    for this format, just pass 0 in this parameter, as it'll be
 | 
			
		||||
                                    ignored
 | 
			
		||||
        @see AudioFormatWriter
 | 
			
		||||
    */
 | 
			
		||||
    virtual AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
 | 
			
		||||
                                                double sampleRateToUse,
 | 
			
		||||
                                                const AudioChannelSet& channelLayout,
 | 
			
		||||
                                                int bitsPerSample,
 | 
			
		||||
                                                const StringPairArray& metadataValues,
 | 
			
		||||
                                                int qualityOptionIndex);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /** Creates an AudioFormat object.
 | 
			
		||||
 | 
			
		||||
        @param formatName       this sets the value that will be returned by getFormatName()
 | 
			
		||||
        @param fileExtensions   an array of file extensions - these will be returned by getFileExtensions()
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormat (String formatName, StringArray fileExtensions);
 | 
			
		||||
 | 
			
		||||
    /** Creates an AudioFormat object.
 | 
			
		||||
 | 
			
		||||
        @param formatName       this sets the value that will be returned by getFormatName()
 | 
			
		||||
        @param fileExtensions   a whitespace-separated list of file extensions - these will
 | 
			
		||||
                                be returned by getFileExtensions()
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormat (StringRef formatName, StringRef fileExtensions);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    String formatName;
 | 
			
		||||
    StringArray fileExtensions;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Subclasses of AudioFormat are used to read and write different audio
 | 
			
		||||
    file formats.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormatReader, AudioFormatWriter, WavAudioFormat, AiffAudioFormat
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormat
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~AudioFormat();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the name of this format.
 | 
			
		||||
        e.g. "WAV file" or "AIFF file"
 | 
			
		||||
    */
 | 
			
		||||
    const String& getFormatName() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns all the file extensions that might apply to a file of this format.
 | 
			
		||||
        The first item will be the one that's preferred when creating a new file.
 | 
			
		||||
        So for a wav file this might just return ".wav"; for an AIFF file it might
 | 
			
		||||
        return two items, ".aif" and ".aiff"
 | 
			
		||||
    */
 | 
			
		||||
    virtual StringArray getFileExtensions() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if this the given file can be read by this format.
 | 
			
		||||
        Subclasses shouldn't do too much work here, just check the extension or
 | 
			
		||||
        file type. The base class implementation just checks the file's extension
 | 
			
		||||
        against one of the ones that was registered in the constructor.
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool canHandleFile (const File& fileToTest);
 | 
			
		||||
 | 
			
		||||
    /** Returns a set of sample rates that the format can read and write. */
 | 
			
		||||
    virtual Array<int> getPossibleSampleRates() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns a set of bit depths that the format can read and write. */
 | 
			
		||||
    virtual Array<int> getPossibleBitDepths() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the format can do 2-channel audio. */
 | 
			
		||||
    virtual bool canDoStereo() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the format can do 1-channel audio. */
 | 
			
		||||
    virtual bool canDoMono() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the format uses compressed data. */
 | 
			
		||||
    virtual bool isCompressed();
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the channel layout is supported by this format. */
 | 
			
		||||
    virtual bool isChannelLayoutSupported (const AudioChannelSet& channelSet);
 | 
			
		||||
 | 
			
		||||
    /** Returns a list of different qualities that can be used when writing.
 | 
			
		||||
 | 
			
		||||
        Non-compressed formats will just return an empty array, but for something
 | 
			
		||||
        like Ogg-Vorbis or MP3, it might return a list of bit-rates, etc.
 | 
			
		||||
 | 
			
		||||
        When calling createWriterFor(), an index from this array is passed in to
 | 
			
		||||
        tell the format which option is required.
 | 
			
		||||
    */
 | 
			
		||||
    virtual StringArray getQualityOptions();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Tries to create an object that can read from a stream containing audio
 | 
			
		||||
        data in this format.
 | 
			
		||||
 | 
			
		||||
        The reader object that is returned can be used to read from the stream, and
 | 
			
		||||
        should then be deleted by the caller.
 | 
			
		||||
 | 
			
		||||
        @param sourceStream                 the stream to read from - the AudioFormatReader object
 | 
			
		||||
                                            that is returned will delete this stream when it no longer
 | 
			
		||||
                                            needs it.
 | 
			
		||||
        @param deleteStreamIfOpeningFails   if no reader can be created, this determines whether this method
 | 
			
		||||
                                            should delete the stream object that was passed-in. (If a valid
 | 
			
		||||
                                            reader is returned, it will always be in charge of deleting the
 | 
			
		||||
                                            stream, so this parameter is ignored)
 | 
			
		||||
        @see AudioFormatReader
 | 
			
		||||
    */
 | 
			
		||||
    virtual AudioFormatReader* createReaderFor (InputStream* sourceStream,
 | 
			
		||||
                                                bool deleteStreamIfOpeningFails) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Attempts to create a MemoryMappedAudioFormatReader, if possible for this format.
 | 
			
		||||
        If the format does not support this, the method will return nullptr;
 | 
			
		||||
    */
 | 
			
		||||
    virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file);
 | 
			
		||||
    virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (FileInputStream* fin);
 | 
			
		||||
 | 
			
		||||
    /** Tries to create an object that can write to a stream with this audio format.
 | 
			
		||||
 | 
			
		||||
        The writer object that is returned can be used to write to the stream, and
 | 
			
		||||
        should then be deleted by the caller.
 | 
			
		||||
 | 
			
		||||
        If the stream can't be created for some reason (e.g. the parameters passed in
 | 
			
		||||
        here aren't suitable), this will return nullptr.
 | 
			
		||||
 | 
			
		||||
        @param streamToWriteTo      the stream that the data will go to - this will be
 | 
			
		||||
                                    deleted by the AudioFormatWriter object when it's no longer
 | 
			
		||||
                                    needed. If no AudioFormatWriter can be created by this method,
 | 
			
		||||
                                    the stream will NOT be deleted, so that the caller can re-use it
 | 
			
		||||
                                    to try to open a different format, etc
 | 
			
		||||
        @param sampleRateToUse      the sample rate for the file, which must be one of the ones
 | 
			
		||||
                                    returned by getPossibleSampleRates()
 | 
			
		||||
        @param numberOfChannels     the number of channels
 | 
			
		||||
        @param bitsPerSample        the bits per sample to use - this must be one of the values
 | 
			
		||||
                                    returned by getPossibleBitDepths()
 | 
			
		||||
        @param metadataValues       a set of metadata values that the writer should try to write
 | 
			
		||||
                                    to the stream. Exactly what these are depends on the format,
 | 
			
		||||
                                    and the subclass doesn't actually have to do anything with
 | 
			
		||||
                                    them if it doesn't want to. Have a look at the specific format
 | 
			
		||||
                                    implementation classes to see possible values that can be
 | 
			
		||||
                                    used
 | 
			
		||||
        @param qualityOptionIndex   the index of one of compression qualities returned by the
 | 
			
		||||
                                    getQualityOptions() method. If there aren't any quality options
 | 
			
		||||
                                    for this format, just pass 0 in this parameter, as it'll be
 | 
			
		||||
                                    ignored
 | 
			
		||||
        @see AudioFormatWriter
 | 
			
		||||
    */
 | 
			
		||||
    virtual AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
 | 
			
		||||
                                                double sampleRateToUse,
 | 
			
		||||
                                                unsigned int numberOfChannels,
 | 
			
		||||
                                                int bitsPerSample,
 | 
			
		||||
                                                const StringPairArray& metadataValues,
 | 
			
		||||
                                                int qualityOptionIndex) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Tries to create an object that can write to a stream with this audio format.
 | 
			
		||||
 | 
			
		||||
        The writer object that is returned can be used to write to the stream, and
 | 
			
		||||
        should then be deleted by the caller.
 | 
			
		||||
 | 
			
		||||
        If the stream can't be created for some reason (e.g. the parameters passed in
 | 
			
		||||
        here aren't suitable), this will return nullptr.
 | 
			
		||||
 | 
			
		||||
        @param streamToWriteTo      the stream that the data will go to - this will be
 | 
			
		||||
                                    deleted by the AudioFormatWriter object when it's no longer
 | 
			
		||||
                                    needed. If no AudioFormatWriter can be created by this method,
 | 
			
		||||
                                    the stream will NOT be deleted, so that the caller can re-use it
 | 
			
		||||
                                    to try to open a different format, etc
 | 
			
		||||
        @param sampleRateToUse      the sample rate for the file, which must be one of the ones
 | 
			
		||||
                                    returned by getPossibleSampleRates()
 | 
			
		||||
        @param channelLayout        the channel layout for the file. Use isChannelLayoutSupported
 | 
			
		||||
                                    to check if the writer supports this layout.
 | 
			
		||||
        @param bitsPerSample        the bits per sample to use - this must be one of the values
 | 
			
		||||
                                    returned by getPossibleBitDepths()
 | 
			
		||||
        @param metadataValues       a set of metadata values that the writer should try to write
 | 
			
		||||
                                    to the stream. Exactly what these are depends on the format,
 | 
			
		||||
                                    and the subclass doesn't actually have to do anything with
 | 
			
		||||
                                    them if it doesn't want to. Have a look at the specific format
 | 
			
		||||
                                    implementation classes to see possible values that can be
 | 
			
		||||
                                    used
 | 
			
		||||
        @param qualityOptionIndex   the index of one of compression qualities returned by the
 | 
			
		||||
                                    getQualityOptions() method. If there aren't any quality options
 | 
			
		||||
                                    for this format, just pass 0 in this parameter, as it'll be
 | 
			
		||||
                                    ignored
 | 
			
		||||
        @see AudioFormatWriter
 | 
			
		||||
    */
 | 
			
		||||
    virtual AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
 | 
			
		||||
                                                double sampleRateToUse,
 | 
			
		||||
                                                const AudioChannelSet& channelLayout,
 | 
			
		||||
                                                int bitsPerSample,
 | 
			
		||||
                                                const StringPairArray& metadataValues,
 | 
			
		||||
                                                int qualityOptionIndex);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /** Creates an AudioFormat object.
 | 
			
		||||
 | 
			
		||||
        @param formatName       this sets the value that will be returned by getFormatName()
 | 
			
		||||
        @param fileExtensions   an array of file extensions - these will be returned by getFileExtensions()
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormat (String formatName, StringArray fileExtensions);
 | 
			
		||||
 | 
			
		||||
    /** Creates an AudioFormat object.
 | 
			
		||||
 | 
			
		||||
        @param formatName       this sets the value that will be returned by getFormatName()
 | 
			
		||||
        @param fileExtensions   a whitespace-separated list of file extensions - these will
 | 
			
		||||
                                be returned by getFileExtensions()
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormat (StringRef formatName, StringRef fileExtensions);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    String formatName;
 | 
			
		||||
    StringArray fileExtensions;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,164 +1,164 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioFormatManager::AudioFormatManager() {}
 | 
			
		||||
AudioFormatManager::~AudioFormatManager() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void AudioFormatManager::registerFormat (AudioFormat* newFormat, bool makeThisTheDefaultFormat)
 | 
			
		||||
{
 | 
			
		||||
    jassert (newFormat != nullptr);
 | 
			
		||||
 | 
			
		||||
    if (newFormat != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
       #if JUCE_DEBUG
 | 
			
		||||
        for (auto* af : knownFormats)
 | 
			
		||||
        {
 | 
			
		||||
            if (af->getFormatName() == newFormat->getFormatName())
 | 
			
		||||
                jassertfalse; // trying to add the same format twice!
 | 
			
		||||
        }
 | 
			
		||||
       #endif
 | 
			
		||||
 | 
			
		||||
        if (makeThisTheDefaultFormat)
 | 
			
		||||
            defaultFormatIndex = getNumKnownFormats();
 | 
			
		||||
 | 
			
		||||
        knownFormats.add (newFormat);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatManager::registerBasicFormats()
 | 
			
		||||
{
 | 
			
		||||
    registerFormat (new WavAudioFormat(), true);
 | 
			
		||||
    registerFormat (new AiffAudioFormat(), false);
 | 
			
		||||
 | 
			
		||||
   #if JUCE_USE_FLAC
 | 
			
		||||
    registerFormat (new FlacAudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
   #if JUCE_USE_OGGVORBIS
 | 
			
		||||
    registerFormat (new OggVorbisAudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
   #if JUCE_MAC || JUCE_IOS
 | 
			
		||||
    registerFormat (new CoreAudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
   #if JUCE_USE_MP3AUDIOFORMAT
 | 
			
		||||
    registerFormat (new MP3AudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
   #if JUCE_USE_WINDOWS_MEDIA_FORMAT
 | 
			
		||||
    registerFormat (new WindowsMediaAudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatManager::clearFormats()
 | 
			
		||||
{
 | 
			
		||||
    knownFormats.clear();
 | 
			
		||||
    defaultFormatIndex = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int AudioFormatManager::getNumKnownFormats() const                  { return knownFormats.size(); }
 | 
			
		||||
AudioFormat* AudioFormatManager::getKnownFormat (int index) const   { return knownFormats[index]; }
 | 
			
		||||
AudioFormat* AudioFormatManager::getDefaultFormat() const           { return getKnownFormat (defaultFormatIndex); }
 | 
			
		||||
 | 
			
		||||
AudioFormat* AudioFormatManager::findFormatForFileExtension (const String& fileExtension) const
 | 
			
		||||
{
 | 
			
		||||
    if (! fileExtension.startsWithChar ('.'))
 | 
			
		||||
        return findFormatForFileExtension ("." + fileExtension);
 | 
			
		||||
 | 
			
		||||
    for (auto* af : knownFormats)
 | 
			
		||||
        if (af->getFileExtensions().contains (fileExtension, true))
 | 
			
		||||
            return af;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String AudioFormatManager::getWildcardForAllFormats() const
 | 
			
		||||
{
 | 
			
		||||
    StringArray extensions;
 | 
			
		||||
 | 
			
		||||
    for (auto* af : knownFormats)
 | 
			
		||||
        extensions.addArray (af->getFileExtensions());
 | 
			
		||||
 | 
			
		||||
    extensions.trim();
 | 
			
		||||
    extensions.removeEmptyStrings();
 | 
			
		||||
 | 
			
		||||
    for (auto& e : extensions)
 | 
			
		||||
        e = (e.startsWithChar ('.') ? "*" : "*.") + e;
 | 
			
		||||
 | 
			
		||||
    extensions.removeDuplicates (true);
 | 
			
		||||
    return extensions.joinIntoString (";");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
AudioFormatReader* AudioFormatManager::createReaderFor (const File& file)
 | 
			
		||||
{
 | 
			
		||||
    // you need to actually register some formats before the manager can
 | 
			
		||||
    // use them to open a file!
 | 
			
		||||
    jassert (getNumKnownFormats() > 0);
 | 
			
		||||
 | 
			
		||||
    for (auto* af : knownFormats)
 | 
			
		||||
        if (af->canHandleFile (file))
 | 
			
		||||
            if (auto in = file.createInputStream())
 | 
			
		||||
                if (auto* r = af->createReaderFor (in.release(), true))
 | 
			
		||||
                    return r;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatReader* AudioFormatManager::createReaderFor (std::unique_ptr<InputStream> audioFileStream)
 | 
			
		||||
{
 | 
			
		||||
    // you need to actually register some formats before the manager can
 | 
			
		||||
    // use them to open a file!
 | 
			
		||||
    jassert (getNumKnownFormats() > 0);
 | 
			
		||||
 | 
			
		||||
    if (audioFileStream != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto originalStreamPos = audioFileStream->getPosition();
 | 
			
		||||
 | 
			
		||||
        for (auto* af : knownFormats)
 | 
			
		||||
        {
 | 
			
		||||
            if (auto* r = af->createReaderFor (audioFileStream.get(), false))
 | 
			
		||||
            {
 | 
			
		||||
                audioFileStream.release();
 | 
			
		||||
                return r;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            audioFileStream->setPosition (originalStreamPos);
 | 
			
		||||
 | 
			
		||||
            // the stream that is passed-in must be capable of being repositioned so
 | 
			
		||||
            // that all the formats can have a go at opening it.
 | 
			
		||||
            jassert (audioFileStream->getPosition() == originalStreamPos);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioFormatManager::AudioFormatManager() {}
 | 
			
		||||
AudioFormatManager::~AudioFormatManager() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void AudioFormatManager::registerFormat (AudioFormat* newFormat, bool makeThisTheDefaultFormat)
 | 
			
		||||
{
 | 
			
		||||
    jassert (newFormat != nullptr);
 | 
			
		||||
 | 
			
		||||
    if (newFormat != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
       #if JUCE_DEBUG
 | 
			
		||||
        for (auto* af : knownFormats)
 | 
			
		||||
        {
 | 
			
		||||
            if (af->getFormatName() == newFormat->getFormatName())
 | 
			
		||||
                jassertfalse; // trying to add the same format twice!
 | 
			
		||||
        }
 | 
			
		||||
       #endif
 | 
			
		||||
 | 
			
		||||
        if (makeThisTheDefaultFormat)
 | 
			
		||||
            defaultFormatIndex = getNumKnownFormats();
 | 
			
		||||
 | 
			
		||||
        knownFormats.add (newFormat);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatManager::registerBasicFormats()
 | 
			
		||||
{
 | 
			
		||||
    registerFormat (new WavAudioFormat(), true);
 | 
			
		||||
    registerFormat (new AiffAudioFormat(), false);
 | 
			
		||||
 | 
			
		||||
   #if JUCE_USE_FLAC
 | 
			
		||||
    registerFormat (new FlacAudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
   #if JUCE_USE_OGGVORBIS
 | 
			
		||||
    registerFormat (new OggVorbisAudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
   #if JUCE_MAC || JUCE_IOS
 | 
			
		||||
    registerFormat (new CoreAudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
   #if JUCE_USE_MP3AUDIOFORMAT
 | 
			
		||||
    registerFormat (new MP3AudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
   #if JUCE_USE_WINDOWS_MEDIA_FORMAT
 | 
			
		||||
    registerFormat (new WindowsMediaAudioFormat(), false);
 | 
			
		||||
   #endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatManager::clearFormats()
 | 
			
		||||
{
 | 
			
		||||
    knownFormats.clear();
 | 
			
		||||
    defaultFormatIndex = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int AudioFormatManager::getNumKnownFormats() const                  { return knownFormats.size(); }
 | 
			
		||||
AudioFormat* AudioFormatManager::getKnownFormat (int index) const   { return knownFormats[index]; }
 | 
			
		||||
AudioFormat* AudioFormatManager::getDefaultFormat() const           { return getKnownFormat (defaultFormatIndex); }
 | 
			
		||||
 | 
			
		||||
AudioFormat* AudioFormatManager::findFormatForFileExtension (const String& fileExtension) const
 | 
			
		||||
{
 | 
			
		||||
    if (! fileExtension.startsWithChar ('.'))
 | 
			
		||||
        return findFormatForFileExtension ("." + fileExtension);
 | 
			
		||||
 | 
			
		||||
    for (auto* af : knownFormats)
 | 
			
		||||
        if (af->getFileExtensions().contains (fileExtension, true))
 | 
			
		||||
            return af;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String AudioFormatManager::getWildcardForAllFormats() const
 | 
			
		||||
{
 | 
			
		||||
    StringArray extensions;
 | 
			
		||||
 | 
			
		||||
    for (auto* af : knownFormats)
 | 
			
		||||
        extensions.addArray (af->getFileExtensions());
 | 
			
		||||
 | 
			
		||||
    extensions.trim();
 | 
			
		||||
    extensions.removeEmptyStrings();
 | 
			
		||||
 | 
			
		||||
    for (auto& e : extensions)
 | 
			
		||||
        e = (e.startsWithChar ('.') ? "*" : "*.") + e;
 | 
			
		||||
 | 
			
		||||
    extensions.removeDuplicates (true);
 | 
			
		||||
    return extensions.joinIntoString (";");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
AudioFormatReader* AudioFormatManager::createReaderFor (const File& file)
 | 
			
		||||
{
 | 
			
		||||
    // you need to actually register some formats before the manager can
 | 
			
		||||
    // use them to open a file!
 | 
			
		||||
    jassert (getNumKnownFormats() > 0);
 | 
			
		||||
 | 
			
		||||
    for (auto* af : knownFormats)
 | 
			
		||||
        if (af->canHandleFile (file))
 | 
			
		||||
            if (auto in = file.createInputStream())
 | 
			
		||||
                if (auto* r = af->createReaderFor (in.release(), true))
 | 
			
		||||
                    return r;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatReader* AudioFormatManager::createReaderFor (std::unique_ptr<InputStream> audioFileStream)
 | 
			
		||||
{
 | 
			
		||||
    // you need to actually register some formats before the manager can
 | 
			
		||||
    // use them to open a file!
 | 
			
		||||
    jassert (getNumKnownFormats() > 0);
 | 
			
		||||
 | 
			
		||||
    if (audioFileStream != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto originalStreamPos = audioFileStream->getPosition();
 | 
			
		||||
 | 
			
		||||
        for (auto* af : knownFormats)
 | 
			
		||||
        {
 | 
			
		||||
            if (auto* r = af->createReaderFor (audioFileStream.get(), false))
 | 
			
		||||
            {
 | 
			
		||||
                audioFileStream.release();
 | 
			
		||||
                return r;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            audioFileStream->setPosition (originalStreamPos);
 | 
			
		||||
 | 
			
		||||
            // the stream that is passed-in must be capable of being repositioned so
 | 
			
		||||
            // that all the formats can have a go at opening it.
 | 
			
		||||
            jassert (audioFileStream->getPosition() == originalStreamPos);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,150 +1,150 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A class for keeping a list of available audio formats, and for deciding which
 | 
			
		||||
    one to use to open a given file.
 | 
			
		||||
 | 
			
		||||
    After creating an AudioFormatManager object, you should call registerFormat()
 | 
			
		||||
    or registerBasicFormats() to give it a list of format types that it can use.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormat
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormatManager
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty format manager.
 | 
			
		||||
 | 
			
		||||
        Before it'll be any use, you'll need to call registerFormat() with all the
 | 
			
		||||
        formats you want it to be able to recognise.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatManager();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~AudioFormatManager();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Adds a format to the manager's list of available file types.
 | 
			
		||||
 | 
			
		||||
        The object passed-in will be deleted by this object, so don't keep a pointer
 | 
			
		||||
        to it!
 | 
			
		||||
 | 
			
		||||
        If makeThisTheDefaultFormat is true, then the getDefaultFormat() method will
 | 
			
		||||
        return this one when called.
 | 
			
		||||
    */
 | 
			
		||||
    void registerFormat (AudioFormat* newFormat,
 | 
			
		||||
                         bool makeThisTheDefaultFormat);
 | 
			
		||||
 | 
			
		||||
    /** Handy method to make it easy to register the formats that come with JUCE.
 | 
			
		||||
        This will add WAV and AIFF to the list, along with any other formats enabled
 | 
			
		||||
        in either the Projucer or your application's preprocessor definitions.
 | 
			
		||||
    */
 | 
			
		||||
    void registerBasicFormats();
 | 
			
		||||
 | 
			
		||||
    /** Clears the list of known formats. */
 | 
			
		||||
    void clearFormats();
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of currently registered file formats. */
 | 
			
		||||
    int getNumKnownFormats() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the registered file formats. */
 | 
			
		||||
    AudioFormat* getKnownFormat (int index) const;
 | 
			
		||||
 | 
			
		||||
    /** Iterator access to the list of known formats. */
 | 
			
		||||
    AudioFormat** begin() noexcept                       { return knownFormats.begin(); }
 | 
			
		||||
 | 
			
		||||
    /** Iterator access to the list of known formats. */
 | 
			
		||||
    AudioFormat* const* begin() const noexcept           { return knownFormats.begin(); }
 | 
			
		||||
 | 
			
		||||
    /** Iterator access to the list of known formats. */
 | 
			
		||||
    AudioFormat** end() noexcept                         { return knownFormats.end(); }
 | 
			
		||||
 | 
			
		||||
    /** Iterator access to the list of known formats. */
 | 
			
		||||
    AudioFormat* const* end() const noexcept             { return knownFormats.end(); }
 | 
			
		||||
 | 
			
		||||
    /** Looks for which of the known formats is listed as being for a given file
 | 
			
		||||
        extension.
 | 
			
		||||
 | 
			
		||||
        The extension may have a dot before it, so e.g. ".wav" or "wav" are both ok.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormat* findFormatForFileExtension (const String& fileExtension) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the format which has been set as the default one.
 | 
			
		||||
 | 
			
		||||
        You can set a format as being the default when it is registered. It's useful
 | 
			
		||||
        when you want to write to a file, because the best format may change between
 | 
			
		||||
        platforms, e.g. AIFF is preferred on the Mac, WAV on Windows.
 | 
			
		||||
 | 
			
		||||
        If none has been set as the default, this method will just return the first
 | 
			
		||||
        one in the list.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormat* getDefaultFormat() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns a set of wildcards for file-matching that contains the extensions for
 | 
			
		||||
        all known formats.
 | 
			
		||||
 | 
			
		||||
        E.g. if might return "*.wav;*.aiff" if it just knows about wavs and aiffs.
 | 
			
		||||
    */
 | 
			
		||||
    String getWildcardForAllFormats() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Searches through the known formats to try to create a suitable reader for
 | 
			
		||||
        this file.
 | 
			
		||||
 | 
			
		||||
        If none of the registered formats can open the file, it'll return nullptr.
 | 
			
		||||
        It's the caller's responsibility to delete the reader that is returned.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatReader* createReaderFor (const File& audioFile);
 | 
			
		||||
 | 
			
		||||
    /** Searches through the known formats to try to create a suitable reader for
 | 
			
		||||
        this stream.
 | 
			
		||||
 | 
			
		||||
        The stream object that is passed-in will be deleted by this method or by the
 | 
			
		||||
        reader that is returned, so the caller should not keep any references to it.
 | 
			
		||||
 | 
			
		||||
        The stream that is passed-in must be capable of being repositioned so
 | 
			
		||||
        that all the formats can have a go at opening it.
 | 
			
		||||
 | 
			
		||||
        If none of the registered formats can open the stream, it'll return nullptr.
 | 
			
		||||
        If it returns a reader, it's the caller's responsibility to delete the reader.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatReader* createReaderFor (std::unique_ptr<InputStream> audioFileStream);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OwnedArray<AudioFormat> knownFormats;
 | 
			
		||||
    int defaultFormatIndex = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatManager)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A class for keeping a list of available audio formats, and for deciding which
 | 
			
		||||
    one to use to open a given file.
 | 
			
		||||
 | 
			
		||||
    After creating an AudioFormatManager object, you should call registerFormat()
 | 
			
		||||
    or registerBasicFormats() to give it a list of format types that it can use.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormat
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormatManager
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty format manager.
 | 
			
		||||
 | 
			
		||||
        Before it'll be any use, you'll need to call registerFormat() with all the
 | 
			
		||||
        formats you want it to be able to recognise.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatManager();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~AudioFormatManager();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Adds a format to the manager's list of available file types.
 | 
			
		||||
 | 
			
		||||
        The object passed-in will be deleted by this object, so don't keep a pointer
 | 
			
		||||
        to it!
 | 
			
		||||
 | 
			
		||||
        If makeThisTheDefaultFormat is true, then the getDefaultFormat() method will
 | 
			
		||||
        return this one when called.
 | 
			
		||||
    */
 | 
			
		||||
    void registerFormat (AudioFormat* newFormat,
 | 
			
		||||
                         bool makeThisTheDefaultFormat);
 | 
			
		||||
 | 
			
		||||
    /** Handy method to make it easy to register the formats that come with JUCE.
 | 
			
		||||
        This will add WAV and AIFF to the list, along with any other formats enabled
 | 
			
		||||
        in either the Projucer or your application's preprocessor definitions.
 | 
			
		||||
    */
 | 
			
		||||
    void registerBasicFormats();
 | 
			
		||||
 | 
			
		||||
    /** Clears the list of known formats. */
 | 
			
		||||
    void clearFormats();
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of currently registered file formats. */
 | 
			
		||||
    int getNumKnownFormats() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the registered file formats. */
 | 
			
		||||
    AudioFormat* getKnownFormat (int index) const;
 | 
			
		||||
 | 
			
		||||
    /** Iterator access to the list of known formats. */
 | 
			
		||||
    AudioFormat** begin() noexcept                       { return knownFormats.begin(); }
 | 
			
		||||
 | 
			
		||||
    /** Iterator access to the list of known formats. */
 | 
			
		||||
    AudioFormat* const* begin() const noexcept           { return knownFormats.begin(); }
 | 
			
		||||
 | 
			
		||||
    /** Iterator access to the list of known formats. */
 | 
			
		||||
    AudioFormat** end() noexcept                         { return knownFormats.end(); }
 | 
			
		||||
 | 
			
		||||
    /** Iterator access to the list of known formats. */
 | 
			
		||||
    AudioFormat* const* end() const noexcept             { return knownFormats.end(); }
 | 
			
		||||
 | 
			
		||||
    /** Looks for which of the known formats is listed as being for a given file
 | 
			
		||||
        extension.
 | 
			
		||||
 | 
			
		||||
        The extension may have a dot before it, so e.g. ".wav" or "wav" are both ok.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormat* findFormatForFileExtension (const String& fileExtension) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the format which has been set as the default one.
 | 
			
		||||
 | 
			
		||||
        You can set a format as being the default when it is registered. It's useful
 | 
			
		||||
        when you want to write to a file, because the best format may change between
 | 
			
		||||
        platforms, e.g. AIFF is preferred on the Mac, WAV on Windows.
 | 
			
		||||
 | 
			
		||||
        If none has been set as the default, this method will just return the first
 | 
			
		||||
        one in the list.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormat* getDefaultFormat() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns a set of wildcards for file-matching that contains the extensions for
 | 
			
		||||
        all known formats.
 | 
			
		||||
 | 
			
		||||
        E.g. if might return "*.wav;*.aiff" if it just knows about wavs and aiffs.
 | 
			
		||||
    */
 | 
			
		||||
    String getWildcardForAllFormats() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Searches through the known formats to try to create a suitable reader for
 | 
			
		||||
        this file.
 | 
			
		||||
 | 
			
		||||
        If none of the registered formats can open the file, it'll return nullptr.
 | 
			
		||||
        It's the caller's responsibility to delete the reader that is returned.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatReader* createReaderFor (const File& audioFile);
 | 
			
		||||
 | 
			
		||||
    /** Searches through the known formats to try to create a suitable reader for
 | 
			
		||||
        this stream.
 | 
			
		||||
 | 
			
		||||
        The stream object that is passed-in will be deleted by this method or by the
 | 
			
		||||
        reader that is returned, so the caller should not keep any references to it.
 | 
			
		||||
 | 
			
		||||
        The stream that is passed-in must be capable of being repositioned so
 | 
			
		||||
        that all the formats can have a go at opening it.
 | 
			
		||||
 | 
			
		||||
        If none of the registered formats can open the stream, it'll return nullptr.
 | 
			
		||||
        If it returns a reader, it's the caller's responsibility to delete the reader.
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatReader* createReaderFor (std::unique_ptr<InputStream> audioFileStream);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OwnedArray<AudioFormat> knownFormats;
 | 
			
		||||
    int defaultFormatIndex = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatManager)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,447 +1,453 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioFormatReader::AudioFormatReader (InputStream* in, const String& name)
 | 
			
		||||
    : input (in), formatName (name)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatReader::~AudioFormatReader()
 | 
			
		||||
{
 | 
			
		||||
    delete input;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void convertFixedToFloat (int* const* channels, int numChannels, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    constexpr auto scaleFactor = 1.0f / static_cast<float> (0x7fffffff);
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numChannels; ++i)
 | 
			
		||||
        if (auto d = channels[i])
 | 
			
		||||
            FloatVectorOperations::convertFixedToFloat (reinterpret_cast<float*> (d), d, scaleFactor, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatReader::read (float* const* destChannels, int numDestChannels,
 | 
			
		||||
                              int64 startSampleInSource, int numSamplesToRead)
 | 
			
		||||
{
 | 
			
		||||
    auto channelsAsInt = reinterpret_cast<int* const*> (destChannels);
 | 
			
		||||
 | 
			
		||||
    if (! read (channelsAsInt, numDestChannels, startSampleInSource, numSamplesToRead, false))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    if (! usesFloatingPointData)
 | 
			
		||||
        convertFixedToFloat (channelsAsInt, numDestChannels, numSamplesToRead);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatReader::read (int* const* destChannels,
 | 
			
		||||
                              int numDestChannels,
 | 
			
		||||
                              int64 startSampleInSource,
 | 
			
		||||
                              int numSamplesToRead,
 | 
			
		||||
                              bool fillLeftoverChannelsWithCopies)
 | 
			
		||||
{
 | 
			
		||||
    jassert (numDestChannels > 0); // you have to actually give this some channels to work with!
 | 
			
		||||
 | 
			
		||||
    auto originalNumSamplesToRead = (size_t) numSamplesToRead;
 | 
			
		||||
    int startOffsetInDestBuffer = 0;
 | 
			
		||||
 | 
			
		||||
    if (startSampleInSource < 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead);
 | 
			
		||||
 | 
			
		||||
        for (int i = numDestChannels; --i >= 0;)
 | 
			
		||||
            if (auto d = destChannels[i])
 | 
			
		||||
                zeromem (d, (size_t) silence * sizeof (int));
 | 
			
		||||
 | 
			
		||||
        startOffsetInDestBuffer += silence;
 | 
			
		||||
        numSamplesToRead -= silence;
 | 
			
		||||
        startSampleInSource = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (numSamplesToRead <= 0)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    if (! readSamples (const_cast<int**> (destChannels),
 | 
			
		||||
                       jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer,
 | 
			
		||||
                       startSampleInSource, numSamplesToRead))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    if (numDestChannels > (int) numChannels)
 | 
			
		||||
    {
 | 
			
		||||
        if (fillLeftoverChannelsWithCopies)
 | 
			
		||||
        {
 | 
			
		||||
            auto lastFullChannel = destChannels[0];
 | 
			
		||||
 | 
			
		||||
            for (int i = (int) numChannels; --i > 0;)
 | 
			
		||||
            {
 | 
			
		||||
                if (destChannels[i] != nullptr)
 | 
			
		||||
                {
 | 
			
		||||
                    lastFullChannel = destChannels[i];
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (lastFullChannel != nullptr)
 | 
			
		||||
                for (int i = (int) numChannels; i < numDestChannels; ++i)
 | 
			
		||||
                    if (auto d = destChannels[i])
 | 
			
		||||
                        memcpy (d, lastFullChannel, sizeof (int) * originalNumSamplesToRead);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = (int) numChannels; i < numDestChannels; ++i)
 | 
			
		||||
                if (auto d = destChannels[i])
 | 
			
		||||
                    zeromem (d, sizeof (int) * originalNumSamplesToRead);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void readChannels (AudioFormatReader& reader, int** chans, AudioBuffer<float>* buffer,
 | 
			
		||||
                          int startSample, int numSamples, int64 readerStartSample, int numTargetChannels,
 | 
			
		||||
                          bool convertToFloat)
 | 
			
		||||
{
 | 
			
		||||
    for (int j = 0; j < numTargetChannels; ++j)
 | 
			
		||||
        chans[j] = reinterpret_cast<int*> (buffer->getWritePointer (j, startSample));
 | 
			
		||||
 | 
			
		||||
    chans[numTargetChannels] = nullptr;
 | 
			
		||||
    reader.read (chans, numTargetChannels, readerStartSample, numSamples, true);
 | 
			
		||||
 | 
			
		||||
    if (convertToFloat)
 | 
			
		||||
        convertFixedToFloat (chans, numTargetChannels, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReader::read (AudioBuffer<float>* buffer,
 | 
			
		||||
                              int startSample,
 | 
			
		||||
                              int numSamples,
 | 
			
		||||
                              int64 readerStartSample,
 | 
			
		||||
                              bool useReaderLeftChan,
 | 
			
		||||
                              bool useReaderRightChan)
 | 
			
		||||
{
 | 
			
		||||
    jassert (buffer != nullptr);
 | 
			
		||||
    jassert (startSample >= 0 && startSample + numSamples <= buffer->getNumSamples());
 | 
			
		||||
 | 
			
		||||
    if (numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numTargetChannels = buffer->getNumChannels();
 | 
			
		||||
 | 
			
		||||
        if (numTargetChannels <= 2)
 | 
			
		||||
        {
 | 
			
		||||
            int* dests[2] = { reinterpret_cast<int*> (buffer->getWritePointer (0, startSample)),
 | 
			
		||||
                              reinterpret_cast<int*> (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr) };
 | 
			
		||||
            int* chans[3] = {};
 | 
			
		||||
 | 
			
		||||
            if (useReaderLeftChan == useReaderRightChan)
 | 
			
		||||
            {
 | 
			
		||||
                chans[0] = dests[0];
 | 
			
		||||
 | 
			
		||||
                if (numChannels > 1)
 | 
			
		||||
                    chans[1] = dests[1];
 | 
			
		||||
            }
 | 
			
		||||
            else if (useReaderLeftChan || (numChannels == 1))
 | 
			
		||||
            {
 | 
			
		||||
                chans[0] = dests[0];
 | 
			
		||||
            }
 | 
			
		||||
            else if (useReaderRightChan)
 | 
			
		||||
            {
 | 
			
		||||
                chans[1] = dests[0];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            read (chans, 2, readerStartSample, numSamples, true);
 | 
			
		||||
 | 
			
		||||
            // if the target's stereo and the source is mono, dupe the first channel..
 | 
			
		||||
            if (numTargetChannels > 1
 | 
			
		||||
                && (chans[0] == nullptr || chans[1] == nullptr)
 | 
			
		||||
                && (dests[0] != nullptr && dests[1] != nullptr))
 | 
			
		||||
            {
 | 
			
		||||
                memcpy (dests[1], dests[0], (size_t) numSamples * sizeof (float));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (! usesFloatingPointData)
 | 
			
		||||
                convertFixedToFloat (dests, 2, numSamples);
 | 
			
		||||
        }
 | 
			
		||||
        else if (numTargetChannels <= 64)
 | 
			
		||||
        {
 | 
			
		||||
            int* chans[65];
 | 
			
		||||
            readChannels (*this, chans, buffer, startSample, numSamples,
 | 
			
		||||
                          readerStartSample, numTargetChannels, ! usesFloatingPointData);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            HeapBlock<int*> chans (numTargetChannels + 1);
 | 
			
		||||
            readChannels (*this, chans, buffer, startSample, numSamples,
 | 
			
		||||
                          readerStartSample, numTargetChannels, ! usesFloatingPointData);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
 | 
			
		||||
                                       Range<float>* const results, const int channelsToRead)
 | 
			
		||||
{
 | 
			
		||||
    jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels);
 | 
			
		||||
 | 
			
		||||
    if (numSamples <= 0)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < channelsToRead; ++i)
 | 
			
		||||
            results[i] = Range<float>();
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto bufferSize = (int) jmin (numSamples, (int64) 4096);
 | 
			
		||||
    AudioBuffer<float> tempSampleBuffer ((int) channelsToRead, bufferSize);
 | 
			
		||||
 | 
			
		||||
    auto floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
 | 
			
		||||
    auto intBuffer = reinterpret_cast<int* const*> (floatBuffer);
 | 
			
		||||
    bool isFirstBlock = true;
 | 
			
		||||
 | 
			
		||||
    while (numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numToDo = (int) jmin (numSamples, (int64) bufferSize);
 | 
			
		||||
 | 
			
		||||
        if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false))
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < channelsToRead; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            Range<float> r;
 | 
			
		||||
 | 
			
		||||
            if (usesFloatingPointData)
 | 
			
		||||
            {
 | 
			
		||||
                r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                auto intRange = Range<int>::findMinAndMax (intBuffer[i], numToDo);
 | 
			
		||||
 | 
			
		||||
                r = Range<float> ((float) intRange.getStart() / (float) std::numeric_limits<int>::max(),
 | 
			
		||||
                                  (float) intRange.getEnd()   / (float) std::numeric_limits<int>::max());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            results[i] = isFirstBlock ? r : results[i].getUnionWith (r);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        isFirstBlock = false;
 | 
			
		||||
        numSamples -= numToDo;
 | 
			
		||||
        startSampleInFile += numToDo;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
 | 
			
		||||
                                       float& lowestLeft, float& highestLeft,
 | 
			
		||||
                                       float& lowestRight, float& highestRight)
 | 
			
		||||
{
 | 
			
		||||
    Range<float> levels[2];
 | 
			
		||||
 | 
			
		||||
    if (numChannels < 2)
 | 
			
		||||
    {
 | 
			
		||||
        readMaxLevels (startSampleInFile, numSamples, levels, (int) numChannels);
 | 
			
		||||
        levels[1] = levels[0];
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        readMaxLevels (startSampleInFile, numSamples, levels, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lowestLeft   = levels[0].getStart();
 | 
			
		||||
    highestLeft  = levels[0].getEnd();
 | 
			
		||||
    lowestRight  = levels[1].getStart();
 | 
			
		||||
    highestRight = levels[1].getEnd();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64 AudioFormatReader::searchForLevel (int64 startSample,
 | 
			
		||||
                                         int64 numSamplesToSearch,
 | 
			
		||||
                                         double magnitudeRangeMinimum,
 | 
			
		||||
                                         double magnitudeRangeMaximum,
 | 
			
		||||
                                         int minimumConsecutiveSamples)
 | 
			
		||||
{
 | 
			
		||||
    if (numSamplesToSearch == 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    const int bufferSize = 4096;
 | 
			
		||||
    HeapBlock<int> tempSpace (bufferSize * 2 + 64);
 | 
			
		||||
 | 
			
		||||
    int* tempBuffer[3] = { tempSpace.get(),
 | 
			
		||||
                           tempSpace.get() + bufferSize,
 | 
			
		||||
                           nullptr };
 | 
			
		||||
 | 
			
		||||
    int consecutive = 0;
 | 
			
		||||
    int64 firstMatchPos = -1;
 | 
			
		||||
 | 
			
		||||
    jassert (magnitudeRangeMaximum > magnitudeRangeMinimum);
 | 
			
		||||
 | 
			
		||||
    auto doubleMin = jlimit (0.0, (double) std::numeric_limits<int>::max(), magnitudeRangeMinimum * std::numeric_limits<int>::max());
 | 
			
		||||
    auto doubleMax = jlimit (doubleMin, (double) std::numeric_limits<int>::max(), magnitudeRangeMaximum * std::numeric_limits<int>::max());
 | 
			
		||||
    auto intMagnitudeRangeMinimum = roundToInt (doubleMin);
 | 
			
		||||
    auto intMagnitudeRangeMaximum = roundToInt (doubleMax);
 | 
			
		||||
 | 
			
		||||
    while (numSamplesToSearch != 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numThisTime = (int) jmin (std::abs (numSamplesToSearch), (int64) bufferSize);
 | 
			
		||||
        int64 bufferStart = startSample;
 | 
			
		||||
 | 
			
		||||
        if (numSamplesToSearch < 0)
 | 
			
		||||
            bufferStart -= numThisTime;
 | 
			
		||||
 | 
			
		||||
        if (bufferStart >= lengthInSamples)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        read (tempBuffer, 2, bufferStart, numThisTime, false);
 | 
			
		||||
        auto num = numThisTime;
 | 
			
		||||
 | 
			
		||||
        while (--num >= 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (numSamplesToSearch < 0)
 | 
			
		||||
                --startSample;
 | 
			
		||||
 | 
			
		||||
            bool matches = false;
 | 
			
		||||
            auto index = (int) (startSample - bufferStart);
 | 
			
		||||
 | 
			
		||||
            if (usesFloatingPointData)
 | 
			
		||||
            {
 | 
			
		||||
                const float sample1 = std::abs (((float*) tempBuffer[0]) [index]);
 | 
			
		||||
 | 
			
		||||
                if (sample1 >= magnitudeRangeMinimum
 | 
			
		||||
                     && sample1 <= magnitudeRangeMaximum)
 | 
			
		||||
                {
 | 
			
		||||
                    matches = true;
 | 
			
		||||
                }
 | 
			
		||||
                else if (numChannels > 1)
 | 
			
		||||
                {
 | 
			
		||||
                    const float sample2 = std::abs (((float*) tempBuffer[1]) [index]);
 | 
			
		||||
 | 
			
		||||
                    matches = (sample2 >= magnitudeRangeMinimum
 | 
			
		||||
                                 && sample2 <= magnitudeRangeMaximum);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                const int sample1 = std::abs (tempBuffer[0] [index]);
 | 
			
		||||
 | 
			
		||||
                if (sample1 >= intMagnitudeRangeMinimum
 | 
			
		||||
                     && sample1 <= intMagnitudeRangeMaximum)
 | 
			
		||||
                {
 | 
			
		||||
                    matches = true;
 | 
			
		||||
                }
 | 
			
		||||
                else if (numChannels > 1)
 | 
			
		||||
                {
 | 
			
		||||
                    const int sample2 = std::abs (tempBuffer[1][index]);
 | 
			
		||||
 | 
			
		||||
                    matches = (sample2 >= intMagnitudeRangeMinimum
 | 
			
		||||
                                 && sample2 <= intMagnitudeRangeMaximum);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (matches)
 | 
			
		||||
            {
 | 
			
		||||
                if (firstMatchPos < 0)
 | 
			
		||||
                    firstMatchPos = startSample;
 | 
			
		||||
 | 
			
		||||
                if (++consecutive >= minimumConsecutiveSamples)
 | 
			
		||||
                {
 | 
			
		||||
                    if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples)
 | 
			
		||||
                        return -1;
 | 
			
		||||
 | 
			
		||||
                    return firstMatchPos;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                consecutive = 0;
 | 
			
		||||
                firstMatchPos = -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (numSamplesToSearch > 0)
 | 
			
		||||
                ++startSample;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (numSamplesToSearch > 0)
 | 
			
		||||
            numSamplesToSearch -= numThisTime;
 | 
			
		||||
        else
 | 
			
		||||
            numSamplesToSearch += numThisTime;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioChannelSet AudioFormatReader::getChannelLayout()
 | 
			
		||||
{
 | 
			
		||||
    return AudioChannelSet::canonicalChannelSet (static_cast<int> (numChannels));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
MemoryMappedAudioFormatReader::MemoryMappedAudioFormatReader (const File& f, const AudioFormatReader& reader,
 | 
			
		||||
                                                              int64 start, int64 length, int frameSize)
 | 
			
		||||
    : AudioFormatReader (nullptr, reader.getFormatName()), file (f),
 | 
			
		||||
      dataChunkStart (start), dataLength (length), bytesPerFrame (frameSize)
 | 
			
		||||
{
 | 
			
		||||
    sampleRate      = reader.sampleRate;
 | 
			
		||||
    bitsPerSample   = reader.bitsPerSample;
 | 
			
		||||
    lengthInSamples = reader.lengthInSamples;
 | 
			
		||||
    numChannels     = reader.numChannels;
 | 
			
		||||
    metadataValues  = reader.metadataValues;
 | 
			
		||||
    usesFloatingPointData = reader.usesFloatingPointData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MemoryMappedAudioFormatReader::mapEntireFile()
 | 
			
		||||
{
 | 
			
		||||
    return mapSectionOfFile (Range<int64> (0, lengthInSamples));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MemoryMappedAudioFormatReader::mapSectionOfFile (Range<int64> samplesToMap)
 | 
			
		||||
{
 | 
			
		||||
    if (map == nullptr || samplesToMap != mappedSection)
 | 
			
		||||
    {
 | 
			
		||||
        map.reset();
 | 
			
		||||
 | 
			
		||||
        const Range<int64> fileRange (sampleToFilePos (samplesToMap.getStart()),
 | 
			
		||||
                                      sampleToFilePos (samplesToMap.getEnd()));
 | 
			
		||||
 | 
			
		||||
        map.reset (new MemoryMappedFile (file, fileRange, MemoryMappedFile::readOnly));
 | 
			
		||||
 | 
			
		||||
        if (map->getData() == nullptr)
 | 
			
		||||
            map.reset();
 | 
			
		||||
        else
 | 
			
		||||
            mappedSection = Range<int64> (jmax ((int64) 0, filePosToSample (map->getRange().getStart() + (bytesPerFrame - 1))),
 | 
			
		||||
                                          jmin (lengthInSamples, filePosToSample (map->getRange().getEnd())));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return map != nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int memoryReadDummyVariable; // used to force the compiler not to optimise-away the read operation
 | 
			
		||||
 | 
			
		||||
void MemoryMappedAudioFormatReader::touchSample (int64 sample) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (map != nullptr && mappedSection.contains (sample))
 | 
			
		||||
        memoryReadDummyVariable += *(char*) sampleToPointer (sample);
 | 
			
		||||
    else
 | 
			
		||||
        jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioFormatReader::AudioFormatReader (InputStream* in, const String& name)
 | 
			
		||||
    : input (in), formatName (name)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatReader::~AudioFormatReader()
 | 
			
		||||
{
 | 
			
		||||
    delete input;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void convertFixedToFloat (int* const* channels, int numChannels, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    constexpr auto scaleFactor = 1.0f / static_cast<float> (0x7fffffff);
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numChannels; ++i)
 | 
			
		||||
        if (auto d = channels[i])
 | 
			
		||||
            FloatVectorOperations::convertFixedToFloat (reinterpret_cast<float*> (d), d, scaleFactor, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatReader::read (float* const* destChannels, int numDestChannels,
 | 
			
		||||
                              int64 startSampleInSource, int numSamplesToRead)
 | 
			
		||||
{
 | 
			
		||||
    auto channelsAsInt = reinterpret_cast<int* const*> (destChannels);
 | 
			
		||||
 | 
			
		||||
    if (! read (channelsAsInt, numDestChannels, startSampleInSource, numSamplesToRead, false))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    if (! usesFloatingPointData)
 | 
			
		||||
        convertFixedToFloat (channelsAsInt, numDestChannels, numSamplesToRead);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatReader::read (int* const* destChannels,
 | 
			
		||||
                              int numDestChannels,
 | 
			
		||||
                              int64 startSampleInSource,
 | 
			
		||||
                              int numSamplesToRead,
 | 
			
		||||
                              bool fillLeftoverChannelsWithCopies)
 | 
			
		||||
{
 | 
			
		||||
    jassert (numDestChannels > 0); // you have to actually give this some channels to work with!
 | 
			
		||||
 | 
			
		||||
    auto originalNumSamplesToRead = (size_t) numSamplesToRead;
 | 
			
		||||
    int startOffsetInDestBuffer = 0;
 | 
			
		||||
 | 
			
		||||
    if (startSampleInSource < 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead);
 | 
			
		||||
 | 
			
		||||
        for (int i = numDestChannels; --i >= 0;)
 | 
			
		||||
            if (auto d = destChannels[i])
 | 
			
		||||
                zeromem (d, (size_t) silence * sizeof (int));
 | 
			
		||||
 | 
			
		||||
        startOffsetInDestBuffer += silence;
 | 
			
		||||
        numSamplesToRead -= silence;
 | 
			
		||||
        startSampleInSource = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (numSamplesToRead <= 0)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    if (! readSamples (const_cast<int**> (destChannels),
 | 
			
		||||
                       jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer,
 | 
			
		||||
                       startSampleInSource, numSamplesToRead))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    if (numDestChannels > (int) numChannels)
 | 
			
		||||
    {
 | 
			
		||||
        if (fillLeftoverChannelsWithCopies)
 | 
			
		||||
        {
 | 
			
		||||
            auto lastFullChannel = destChannels[0];
 | 
			
		||||
 | 
			
		||||
            for (int i = (int) numChannels; --i > 0;)
 | 
			
		||||
            {
 | 
			
		||||
                if (destChannels[i] != nullptr)
 | 
			
		||||
                {
 | 
			
		||||
                    lastFullChannel = destChannels[i];
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (lastFullChannel != nullptr)
 | 
			
		||||
                for (int i = (int) numChannels; i < numDestChannels; ++i)
 | 
			
		||||
                    if (auto d = destChannels[i])
 | 
			
		||||
                        memcpy (d, lastFullChannel, sizeof (int) * originalNumSamplesToRead);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = (int) numChannels; i < numDestChannels; ++i)
 | 
			
		||||
                if (auto d = destChannels[i])
 | 
			
		||||
                    zeromem (d, sizeof (int) * originalNumSamplesToRead);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool readChannels (AudioFormatReader& reader, int** chans, AudioBuffer<float>* buffer,
 | 
			
		||||
                          int startSample, int numSamples, int64 readerStartSample, int numTargetChannels,
 | 
			
		||||
                          bool convertToFloat)
 | 
			
		||||
{
 | 
			
		||||
    for (int j = 0; j < numTargetChannels; ++j)
 | 
			
		||||
        chans[j] = reinterpret_cast<int*> (buffer->getWritePointer (j, startSample));
 | 
			
		||||
 | 
			
		||||
    chans[numTargetChannels] = nullptr;
 | 
			
		||||
 | 
			
		||||
    const bool success = reader.read (chans, numTargetChannels, readerStartSample, numSamples, true);
 | 
			
		||||
 | 
			
		||||
    if (convertToFloat)
 | 
			
		||||
        convertFixedToFloat (chans, numTargetChannels, numSamples);
 | 
			
		||||
 | 
			
		||||
    return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatReader::read (AudioBuffer<float>* buffer,
 | 
			
		||||
                              int startSample,
 | 
			
		||||
                              int numSamples,
 | 
			
		||||
                              int64 readerStartSample,
 | 
			
		||||
                              bool useReaderLeftChan,
 | 
			
		||||
                              bool useReaderRightChan)
 | 
			
		||||
{
 | 
			
		||||
    jassert (buffer != nullptr);
 | 
			
		||||
    jassert (startSample >= 0 && startSample + numSamples <= buffer->getNumSamples());
 | 
			
		||||
 | 
			
		||||
    if (numSamples <= 0)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    auto numTargetChannels = buffer->getNumChannels();
 | 
			
		||||
 | 
			
		||||
    if (numTargetChannels <= 2)
 | 
			
		||||
    {
 | 
			
		||||
        int* dests[2] = { reinterpret_cast<int*> (buffer->getWritePointer (0, startSample)),
 | 
			
		||||
                          reinterpret_cast<int*> (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr) };
 | 
			
		||||
        int* chans[3] = {};
 | 
			
		||||
 | 
			
		||||
        if (useReaderLeftChan == useReaderRightChan)
 | 
			
		||||
        {
 | 
			
		||||
            chans[0] = dests[0];
 | 
			
		||||
 | 
			
		||||
            if (numChannels > 1)
 | 
			
		||||
                chans[1] = dests[1];
 | 
			
		||||
        }
 | 
			
		||||
        else if (useReaderLeftChan || (numChannels == 1))
 | 
			
		||||
        {
 | 
			
		||||
            chans[0] = dests[0];
 | 
			
		||||
        }
 | 
			
		||||
        else if (useReaderRightChan)
 | 
			
		||||
        {
 | 
			
		||||
            chans[1] = dests[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (! read (chans, 2, readerStartSample, numSamples, true))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        // if the target's stereo and the source is mono, dupe the first channel..
 | 
			
		||||
        if (numTargetChannels > 1
 | 
			
		||||
            && (chans[0] == nullptr || chans[1] == nullptr)
 | 
			
		||||
            && (dests[0] != nullptr && dests[1] != nullptr))
 | 
			
		||||
        {
 | 
			
		||||
            memcpy (dests[1], dests[0], (size_t) numSamples * sizeof (float));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (! usesFloatingPointData)
 | 
			
		||||
            convertFixedToFloat (dests, 2, numSamples);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (numTargetChannels <= 64)
 | 
			
		||||
    {
 | 
			
		||||
        int* chans[65];
 | 
			
		||||
        return readChannels (*this, chans, buffer, startSample, numSamples,
 | 
			
		||||
                             readerStartSample, numTargetChannels, ! usesFloatingPointData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    HeapBlock<int*> chans (numTargetChannels + 1);
 | 
			
		||||
 | 
			
		||||
    return readChannels (*this, chans, buffer, startSample, numSamples,
 | 
			
		||||
                         readerStartSample, numTargetChannels, ! usesFloatingPointData);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
 | 
			
		||||
                                       Range<float>* const results, const int channelsToRead)
 | 
			
		||||
{
 | 
			
		||||
    jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels);
 | 
			
		||||
 | 
			
		||||
    if (numSamples <= 0)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < channelsToRead; ++i)
 | 
			
		||||
            results[i] = Range<float>();
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto bufferSize = (int) jmin (numSamples, (int64) 4096);
 | 
			
		||||
    AudioBuffer<float> tempSampleBuffer ((int) channelsToRead, bufferSize);
 | 
			
		||||
 | 
			
		||||
    auto floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
 | 
			
		||||
    auto intBuffer = reinterpret_cast<int* const*> (floatBuffer);
 | 
			
		||||
    bool isFirstBlock = true;
 | 
			
		||||
 | 
			
		||||
    while (numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numToDo = (int) jmin (numSamples, (int64) bufferSize);
 | 
			
		||||
 | 
			
		||||
        if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false))
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < channelsToRead; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            Range<float> r;
 | 
			
		||||
 | 
			
		||||
            if (usesFloatingPointData)
 | 
			
		||||
            {
 | 
			
		||||
                r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                auto intRange = Range<int>::findMinAndMax (intBuffer[i], numToDo);
 | 
			
		||||
 | 
			
		||||
                r = Range<float> ((float) intRange.getStart() / (float) std::numeric_limits<int>::max(),
 | 
			
		||||
                                  (float) intRange.getEnd()   / (float) std::numeric_limits<int>::max());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            results[i] = isFirstBlock ? r : results[i].getUnionWith (r);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        isFirstBlock = false;
 | 
			
		||||
        numSamples -= numToDo;
 | 
			
		||||
        startSampleInFile += numToDo;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
 | 
			
		||||
                                       float& lowestLeft, float& highestLeft,
 | 
			
		||||
                                       float& lowestRight, float& highestRight)
 | 
			
		||||
{
 | 
			
		||||
    Range<float> levels[2];
 | 
			
		||||
 | 
			
		||||
    if (numChannels < 2)
 | 
			
		||||
    {
 | 
			
		||||
        readMaxLevels (startSampleInFile, numSamples, levels, (int) numChannels);
 | 
			
		||||
        levels[1] = levels[0];
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        readMaxLevels (startSampleInFile, numSamples, levels, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lowestLeft   = levels[0].getStart();
 | 
			
		||||
    highestLeft  = levels[0].getEnd();
 | 
			
		||||
    lowestRight  = levels[1].getStart();
 | 
			
		||||
    highestRight = levels[1].getEnd();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64 AudioFormatReader::searchForLevel (int64 startSample,
 | 
			
		||||
                                         int64 numSamplesToSearch,
 | 
			
		||||
                                         double magnitudeRangeMinimum,
 | 
			
		||||
                                         double magnitudeRangeMaximum,
 | 
			
		||||
                                         int minimumConsecutiveSamples)
 | 
			
		||||
{
 | 
			
		||||
    if (numSamplesToSearch == 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    const int bufferSize = 4096;
 | 
			
		||||
    HeapBlock<int> tempSpace (bufferSize * 2 + 64);
 | 
			
		||||
 | 
			
		||||
    int* tempBuffer[3] = { tempSpace.get(),
 | 
			
		||||
                           tempSpace.get() + bufferSize,
 | 
			
		||||
                           nullptr };
 | 
			
		||||
 | 
			
		||||
    int consecutive = 0;
 | 
			
		||||
    int64 firstMatchPos = -1;
 | 
			
		||||
 | 
			
		||||
    jassert (magnitudeRangeMaximum > magnitudeRangeMinimum);
 | 
			
		||||
 | 
			
		||||
    auto doubleMin = jlimit (0.0, (double) std::numeric_limits<int>::max(), magnitudeRangeMinimum * std::numeric_limits<int>::max());
 | 
			
		||||
    auto doubleMax = jlimit (doubleMin, (double) std::numeric_limits<int>::max(), magnitudeRangeMaximum * std::numeric_limits<int>::max());
 | 
			
		||||
    auto intMagnitudeRangeMinimum = roundToInt (doubleMin);
 | 
			
		||||
    auto intMagnitudeRangeMaximum = roundToInt (doubleMax);
 | 
			
		||||
 | 
			
		||||
    while (numSamplesToSearch != 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numThisTime = (int) jmin (std::abs (numSamplesToSearch), (int64) bufferSize);
 | 
			
		||||
        int64 bufferStart = startSample;
 | 
			
		||||
 | 
			
		||||
        if (numSamplesToSearch < 0)
 | 
			
		||||
            bufferStart -= numThisTime;
 | 
			
		||||
 | 
			
		||||
        if (bufferStart >= lengthInSamples)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        read (tempBuffer, 2, bufferStart, numThisTime, false);
 | 
			
		||||
        auto num = numThisTime;
 | 
			
		||||
 | 
			
		||||
        while (--num >= 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (numSamplesToSearch < 0)
 | 
			
		||||
                --startSample;
 | 
			
		||||
 | 
			
		||||
            bool matches = false;
 | 
			
		||||
            auto index = (int) (startSample - bufferStart);
 | 
			
		||||
 | 
			
		||||
            if (usesFloatingPointData)
 | 
			
		||||
            {
 | 
			
		||||
                const float sample1 = std::abs (((float*) tempBuffer[0]) [index]);
 | 
			
		||||
 | 
			
		||||
                if (sample1 >= magnitudeRangeMinimum
 | 
			
		||||
                     && sample1 <= magnitudeRangeMaximum)
 | 
			
		||||
                {
 | 
			
		||||
                    matches = true;
 | 
			
		||||
                }
 | 
			
		||||
                else if (numChannels > 1)
 | 
			
		||||
                {
 | 
			
		||||
                    const float sample2 = std::abs (((float*) tempBuffer[1]) [index]);
 | 
			
		||||
 | 
			
		||||
                    matches = (sample2 >= magnitudeRangeMinimum
 | 
			
		||||
                                 && sample2 <= magnitudeRangeMaximum);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                const int sample1 = std::abs (tempBuffer[0] [index]);
 | 
			
		||||
 | 
			
		||||
                if (sample1 >= intMagnitudeRangeMinimum
 | 
			
		||||
                     && sample1 <= intMagnitudeRangeMaximum)
 | 
			
		||||
                {
 | 
			
		||||
                    matches = true;
 | 
			
		||||
                }
 | 
			
		||||
                else if (numChannels > 1)
 | 
			
		||||
                {
 | 
			
		||||
                    const int sample2 = std::abs (tempBuffer[1][index]);
 | 
			
		||||
 | 
			
		||||
                    matches = (sample2 >= intMagnitudeRangeMinimum
 | 
			
		||||
                                 && sample2 <= intMagnitudeRangeMaximum);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (matches)
 | 
			
		||||
            {
 | 
			
		||||
                if (firstMatchPos < 0)
 | 
			
		||||
                    firstMatchPos = startSample;
 | 
			
		||||
 | 
			
		||||
                if (++consecutive >= minimumConsecutiveSamples)
 | 
			
		||||
                {
 | 
			
		||||
                    if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples)
 | 
			
		||||
                        return -1;
 | 
			
		||||
 | 
			
		||||
                    return firstMatchPos;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                consecutive = 0;
 | 
			
		||||
                firstMatchPos = -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (numSamplesToSearch > 0)
 | 
			
		||||
                ++startSample;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (numSamplesToSearch > 0)
 | 
			
		||||
            numSamplesToSearch -= numThisTime;
 | 
			
		||||
        else
 | 
			
		||||
            numSamplesToSearch += numThisTime;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioChannelSet AudioFormatReader::getChannelLayout()
 | 
			
		||||
{
 | 
			
		||||
    return AudioChannelSet::canonicalChannelSet (static_cast<int> (numChannels));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
MemoryMappedAudioFormatReader::MemoryMappedAudioFormatReader (const File& f, const AudioFormatReader& reader,
 | 
			
		||||
                                                              int64 start, int64 length, int frameSize)
 | 
			
		||||
    : AudioFormatReader (nullptr, reader.getFormatName()), file (f),
 | 
			
		||||
      dataChunkStart (start), dataLength (length), bytesPerFrame (frameSize)
 | 
			
		||||
{
 | 
			
		||||
    sampleRate      = reader.sampleRate;
 | 
			
		||||
    bitsPerSample   = reader.bitsPerSample;
 | 
			
		||||
    lengthInSamples = reader.lengthInSamples;
 | 
			
		||||
    numChannels     = reader.numChannels;
 | 
			
		||||
    metadataValues  = reader.metadataValues;
 | 
			
		||||
    usesFloatingPointData = reader.usesFloatingPointData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MemoryMappedAudioFormatReader::mapEntireFile()
 | 
			
		||||
{
 | 
			
		||||
    return mapSectionOfFile (Range<int64> (0, lengthInSamples));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MemoryMappedAudioFormatReader::mapSectionOfFile (Range<int64> samplesToMap)
 | 
			
		||||
{
 | 
			
		||||
    if (map == nullptr || samplesToMap != mappedSection)
 | 
			
		||||
    {
 | 
			
		||||
        map.reset();
 | 
			
		||||
 | 
			
		||||
        const Range<int64> fileRange (sampleToFilePos (samplesToMap.getStart()),
 | 
			
		||||
                                      sampleToFilePos (samplesToMap.getEnd()));
 | 
			
		||||
 | 
			
		||||
        map.reset (new MemoryMappedFile (file, fileRange, MemoryMappedFile::readOnly));
 | 
			
		||||
 | 
			
		||||
        if (map->getData() == nullptr)
 | 
			
		||||
            map.reset();
 | 
			
		||||
        else
 | 
			
		||||
            mappedSection = Range<int64> (jmax ((int64) 0, filePosToSample (map->getRange().getStart() + (bytesPerFrame - 1))),
 | 
			
		||||
                                          jmin (lengthInSamples, filePosToSample (map->getRange().getEnd())));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return map != nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int memoryReadDummyVariable; // used to force the compiler not to optimise-away the read operation
 | 
			
		||||
 | 
			
		||||
void MemoryMappedAudioFormatReader::touchSample (int64 sample) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (map != nullptr && mappedSection.contains (sample))
 | 
			
		||||
        memoryReadDummyVariable += *(char*) sampleToPointer (sample);
 | 
			
		||||
    else
 | 
			
		||||
        jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,333 +1,337 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class AudioFormat;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Reads samples from an audio file stream.
 | 
			
		||||
 | 
			
		||||
    A subclass that reads a specific type of audio format will be created by
 | 
			
		||||
    an AudioFormat object.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormat, AudioFormatWriter
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormatReader
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioFormatReader object.
 | 
			
		||||
 | 
			
		||||
        @param sourceStream     the stream to read from - this will be deleted
 | 
			
		||||
                                by this object when it is no longer needed. (Some
 | 
			
		||||
                                specialised readers might not use this parameter and
 | 
			
		||||
                                can leave it as nullptr).
 | 
			
		||||
        @param formatName       the description that will be returned by the getFormatName()
 | 
			
		||||
                                method
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatReader (InputStream* sourceStream,
 | 
			
		||||
                       const String& formatName);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~AudioFormatReader();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns a description of what type of format this is.
 | 
			
		||||
 | 
			
		||||
        E.g. "AIFF"
 | 
			
		||||
    */
 | 
			
		||||
    const String& getFormatName() const noexcept    { return formatName; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Reads samples from the stream.
 | 
			
		||||
 | 
			
		||||
        @param destChannels         an array of float buffers into which the sample data for each
 | 
			
		||||
                                    channel will be written. Channels that aren't needed can be null
 | 
			
		||||
        @param numDestChannels      the number of array elements in the destChannels array
 | 
			
		||||
        @param startSampleInSource  the position in the audio file or stream at which the samples
 | 
			
		||||
                                    should be read, as a number of samples from the start of the
 | 
			
		||||
                                    stream. It's ok for this to be beyond the start or end of the
 | 
			
		||||
                                    available data - any samples that are out-of-range will be returned
 | 
			
		||||
                                    as zeros.
 | 
			
		||||
        @param numSamplesToRead     the number of samples to read. If this is greater than the number
 | 
			
		||||
                                    of samples that the file or stream contains. the result will be padded
 | 
			
		||||
                                    with zeros
 | 
			
		||||
        @returns                    true if the operation succeeded, false if there was an error. Note
 | 
			
		||||
                                    that reading sections of data beyond the extent of the stream isn't an
 | 
			
		||||
                                    error - the reader should just return zeros for these regions
 | 
			
		||||
        @see readMaxLevels
 | 
			
		||||
    */
 | 
			
		||||
    bool read (float* const* destChannels, int numDestChannels,
 | 
			
		||||
               int64 startSampleInSource, int numSamplesToRead);
 | 
			
		||||
 | 
			
		||||
    /** Reads samples from the stream.
 | 
			
		||||
 | 
			
		||||
        @param destChannels         an array of buffers into which the sample data for each
 | 
			
		||||
                                    channel will be written.
 | 
			
		||||
                                    If the format is fixed-point, each channel will be written
 | 
			
		||||
                                    as an array of 32-bit signed integers using the full
 | 
			
		||||
                                    range -0x80000000 to 0x7fffffff, regardless of the source's
 | 
			
		||||
                                    bit-depth. If it is a floating-point format, you should cast
 | 
			
		||||
                                    the resulting array to a (float**) to get the values (in the
 | 
			
		||||
                                    range -1.0 to 1.0 or beyond)
 | 
			
		||||
                                    If the format is stereo, then destChannels[0] is the left channel
 | 
			
		||||
                                    data, and destChannels[1] is the right channel.
 | 
			
		||||
                                    The numDestChannels parameter indicates how many pointers this array
 | 
			
		||||
                                    contains, but some of these pointers can be null if you don't want to
 | 
			
		||||
                                    read data for some of the channels
 | 
			
		||||
        @param numDestChannels      the number of array elements in the destChannels array
 | 
			
		||||
        @param startSampleInSource  the position in the audio file or stream at which the samples
 | 
			
		||||
                                    should be read, as a number of samples from the start of the
 | 
			
		||||
                                    stream. It's ok for this to be beyond the start or end of the
 | 
			
		||||
                                    available data - any samples that are out-of-range will be returned
 | 
			
		||||
                                    as zeros.
 | 
			
		||||
        @param numSamplesToRead     the number of samples to read. If this is greater than the number
 | 
			
		||||
                                    of samples that the file or stream contains. the result will be padded
 | 
			
		||||
                                    with zeros
 | 
			
		||||
        @param fillLeftoverChannelsWithCopies   if true, this indicates that if there's no source data available
 | 
			
		||||
                                    for some of the channels that you pass in, then they should be filled with
 | 
			
		||||
                                    copies of valid source channels.
 | 
			
		||||
                                    E.g. if you're reading a mono file and you pass 2 channels to this method, then
 | 
			
		||||
                                    if fillLeftoverChannelsWithCopies is true, both destination channels will be filled
 | 
			
		||||
                                    with the same data from the file's single channel. If fillLeftoverChannelsWithCopies
 | 
			
		||||
                                    was false, then only the first channel would be filled with the file's contents, and
 | 
			
		||||
                                    the second would be cleared. If there are many channels, e.g. you try to read 4 channels
 | 
			
		||||
                                    from a stereo file, then the last 3 would all end up with copies of the same data.
 | 
			
		||||
        @returns                    true if the operation succeeded, false if there was an error. Note
 | 
			
		||||
                                    that reading sections of data beyond the extent of the stream isn't an
 | 
			
		||||
                                    error - the reader should just return zeros for these regions
 | 
			
		||||
        @see readMaxLevels
 | 
			
		||||
    */
 | 
			
		||||
    bool read (int* const* destChannels,
 | 
			
		||||
               int numDestChannels,
 | 
			
		||||
               int64 startSampleInSource,
 | 
			
		||||
               int numSamplesToRead,
 | 
			
		||||
               bool fillLeftoverChannelsWithCopies);
 | 
			
		||||
 | 
			
		||||
    /** Fills a section of an AudioBuffer from this reader.
 | 
			
		||||
 | 
			
		||||
        This will convert the reader's fixed- or floating-point data to
 | 
			
		||||
        the buffer's floating-point format, and will try to intelligently
 | 
			
		||||
        cope with mismatches between the number of channels in the reader
 | 
			
		||||
        and the buffer.
 | 
			
		||||
    */
 | 
			
		||||
    void read (AudioBuffer<float>* buffer,
 | 
			
		||||
               int startSampleInDestBuffer,
 | 
			
		||||
               int numSamples,
 | 
			
		||||
               int64 readerStartSample,
 | 
			
		||||
               bool useReaderLeftChan,
 | 
			
		||||
               bool useReaderRightChan);
 | 
			
		||||
 | 
			
		||||
    /** Finds the highest and lowest sample levels from a section of the audio stream.
 | 
			
		||||
 | 
			
		||||
        This will read a block of samples from the stream, and measure the
 | 
			
		||||
        highest and lowest sample levels from the channels in that section, returning
 | 
			
		||||
        these as normalised floating-point levels.
 | 
			
		||||
 | 
			
		||||
        @param startSample  the offset into the audio stream to start reading from. It's
 | 
			
		||||
                            ok for this to be beyond the start or end of the stream.
 | 
			
		||||
        @param numSamples   how many samples to read
 | 
			
		||||
        @param results      this array will be filled with Range values for each channel.
 | 
			
		||||
                            The array must contain numChannels elements.
 | 
			
		||||
        @param numChannelsToRead  the number of channels of data to scan. This must be
 | 
			
		||||
                            more than zero, but not more than the total number of channels
 | 
			
		||||
                            that the reader contains
 | 
			
		||||
        @see read
 | 
			
		||||
    */
 | 
			
		||||
    virtual void readMaxLevels (int64 startSample, int64 numSamples,
 | 
			
		||||
                                Range<float>* results, int numChannelsToRead);
 | 
			
		||||
 | 
			
		||||
    /** Finds the highest and lowest sample levels from a section of the audio stream.
 | 
			
		||||
 | 
			
		||||
        This will read a block of samples from the stream, and measure the
 | 
			
		||||
        highest and lowest sample levels from the channels in that section, returning
 | 
			
		||||
        these as normalised floating-point levels.
 | 
			
		||||
 | 
			
		||||
        @param startSample          the offset into the audio stream to start reading from. It's
 | 
			
		||||
                                    ok for this to be beyond the start or end of the stream.
 | 
			
		||||
        @param numSamples           how many samples to read
 | 
			
		||||
        @param lowestLeft           on return, this is the lowest absolute sample from the left channel
 | 
			
		||||
        @param highestLeft          on return, this is the highest absolute sample from the left channel
 | 
			
		||||
        @param lowestRight          on return, this is the lowest absolute sample from the right
 | 
			
		||||
                                    channel (if there is one)
 | 
			
		||||
        @param highestRight         on return, this is the highest absolute sample from the right
 | 
			
		||||
                                    channel (if there is one)
 | 
			
		||||
        @see read
 | 
			
		||||
    */
 | 
			
		||||
    virtual void readMaxLevels (int64 startSample, int64 numSamples,
 | 
			
		||||
                                float& lowestLeft,  float& highestLeft,
 | 
			
		||||
                                float& lowestRight, float& highestRight);
 | 
			
		||||
 | 
			
		||||
    /** Scans the source looking for a sample whose magnitude is in a specified range.
 | 
			
		||||
 | 
			
		||||
        This will read from the source, either forwards or backwards between two sample
 | 
			
		||||
        positions, until it finds a sample whose magnitude lies between two specified levels.
 | 
			
		||||
 | 
			
		||||
        If it finds a suitable sample, it returns its position; if not, it will return -1.
 | 
			
		||||
 | 
			
		||||
        There's also a minimumConsecutiveSamples setting to help avoid spikes or zero-crossing
 | 
			
		||||
        points when you're searching for a continuous range of samples
 | 
			
		||||
 | 
			
		||||
        @param startSample              the first sample to look at
 | 
			
		||||
        @param numSamplesToSearch       the number of samples to scan. If this value is negative,
 | 
			
		||||
                                        the search will go backwards
 | 
			
		||||
        @param magnitudeRangeMinimum    the lowest magnitude (inclusive) that is considered a hit, from 0 to 1.0
 | 
			
		||||
        @param magnitudeRangeMaximum    the highest magnitude (inclusive) that is considered a hit, from 0 to 1.0
 | 
			
		||||
        @param minimumConsecutiveSamples    if this is > 0, the method will only look for a sequence
 | 
			
		||||
                                            of this many consecutive samples, all of which lie
 | 
			
		||||
                                            within the target range. When it finds such a sequence,
 | 
			
		||||
                                            it returns the position of the first in-range sample
 | 
			
		||||
                                            it found (i.e. the earliest one if scanning forwards, the
 | 
			
		||||
                                            latest one if scanning backwards)
 | 
			
		||||
    */
 | 
			
		||||
    int64 searchForLevel (int64 startSample,
 | 
			
		||||
                          int64 numSamplesToSearch,
 | 
			
		||||
                          double magnitudeRangeMinimum,
 | 
			
		||||
                          double magnitudeRangeMaximum,
 | 
			
		||||
                          int minimumConsecutiveSamples);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The sample-rate of the stream. */
 | 
			
		||||
    double sampleRate = 0;
 | 
			
		||||
 | 
			
		||||
    /** The number of bits per sample, e.g. 16, 24, 32. */
 | 
			
		||||
    unsigned int bitsPerSample = 0;
 | 
			
		||||
 | 
			
		||||
    /** The total number of samples in the audio stream. */
 | 
			
		||||
    int64 lengthInSamples = 0;
 | 
			
		||||
 | 
			
		||||
    /** The total number of channels in the audio stream. */
 | 
			
		||||
    unsigned int numChannels = 0;
 | 
			
		||||
 | 
			
		||||
    /** Indicates whether the data is floating-point or fixed. */
 | 
			
		||||
    bool usesFloatingPointData = false;
 | 
			
		||||
 | 
			
		||||
    /** A set of metadata values that the reader has pulled out of the stream.
 | 
			
		||||
 | 
			
		||||
        Exactly what these values are depends on the format, so you can
 | 
			
		||||
        check out the format implementation code to see what kind of stuff
 | 
			
		||||
        they understand.
 | 
			
		||||
    */
 | 
			
		||||
    StringPairArray metadataValues;
 | 
			
		||||
 | 
			
		||||
    /** The input stream, for use by subclasses. */
 | 
			
		||||
    InputStream* input;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Get the channel layout of the audio stream. */
 | 
			
		||||
    virtual AudioChannelSet getChannelLayout();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Subclasses must implement this method to perform the low-level read operation.
 | 
			
		||||
 | 
			
		||||
        Callers should use read() instead of calling this directly.
 | 
			
		||||
 | 
			
		||||
        @param destChannels              the array of destination buffers to fill. Some of these
 | 
			
		||||
                                         pointers may be null
 | 
			
		||||
        @param numDestChannels           the number of items in the destChannels array. This
 | 
			
		||||
                                         value is guaranteed not to be greater than the number of
 | 
			
		||||
                                         channels that this reader object contains
 | 
			
		||||
        @param startOffsetInDestBuffer   the number of samples from the start of the
 | 
			
		||||
                                         dest data at which to begin writing
 | 
			
		||||
        @param startSampleInFile         the number of samples into the source data at which
 | 
			
		||||
                                         to begin reading. This value is guaranteed to be >= 0.
 | 
			
		||||
        @param numSamples                the number of samples to read
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool readSamples (int** destChannels,
 | 
			
		||||
                              int numDestChannels,
 | 
			
		||||
                              int startOffsetInDestBuffer,
 | 
			
		||||
                              int64 startSampleInFile,
 | 
			
		||||
                              int numSamples) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Used by AudioFormatReader subclasses to copy data to different formats. */
 | 
			
		||||
    template <class DestSampleType, class SourceSampleType, class SourceEndianness>
 | 
			
		||||
    struct ReadHelper
 | 
			
		||||
    {
 | 
			
		||||
        using DestType   = AudioData::Pointer<DestSampleType,   AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
 | 
			
		||||
        using SourceType = AudioData::Pointer<SourceSampleType, SourceEndianness, AudioData::Interleaved, AudioData::Const>;
 | 
			
		||||
 | 
			
		||||
        template <typename TargetType>
 | 
			
		||||
        static void read (TargetType* const* destData, int destOffset, int numDestChannels,
 | 
			
		||||
                          const void* sourceData, int numSourceChannels, int numSamples) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < numDestChannels; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                if (void* targetChan = destData[i])
 | 
			
		||||
                {
 | 
			
		||||
                    DestType dest (targetChan);
 | 
			
		||||
                    dest += destOffset;
 | 
			
		||||
 | 
			
		||||
                    if (i < numSourceChannels)
 | 
			
		||||
                        dest.convertSamples (SourceType (addBytesToPointer (sourceData, i * SourceType::getBytesPerSample()), numSourceChannels), numSamples);
 | 
			
		||||
                    else
 | 
			
		||||
                        dest.clearSamples (numSamples);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie
 | 
			
		||||
        beyond the end of their available length.
 | 
			
		||||
    */
 | 
			
		||||
    static void clearSamplesBeyondAvailableLength (int** destChannels, int numDestChannels,
 | 
			
		||||
                                                   int startOffsetInDestBuffer, int64 startSampleInFile,
 | 
			
		||||
                                                   int& numSamples, int64 fileLengthInSamples)
 | 
			
		||||
    {
 | 
			
		||||
        if (destChannels == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            jassertfalse;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const int64 samplesAvailable = fileLengthInSamples - startSampleInFile;
 | 
			
		||||
 | 
			
		||||
        if (samplesAvailable < numSamples)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = numDestChannels; --i >= 0;)
 | 
			
		||||
                if (destChannels[i] != nullptr)
 | 
			
		||||
                    zeromem (destChannels[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int));
 | 
			
		||||
 | 
			
		||||
            numSamples = (int) samplesAvailable;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    String formatName;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class AudioFormat;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Reads samples from an audio file stream.
 | 
			
		||||
 | 
			
		||||
    A subclass that reads a specific type of audio format will be created by
 | 
			
		||||
    an AudioFormat object.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormat, AudioFormatWriter
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormatReader
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioFormatReader object.
 | 
			
		||||
 | 
			
		||||
        @param sourceStream     the stream to read from - this will be deleted
 | 
			
		||||
                                by this object when it is no longer needed. (Some
 | 
			
		||||
                                specialised readers might not use this parameter and
 | 
			
		||||
                                can leave it as nullptr).
 | 
			
		||||
        @param formatName       the description that will be returned by the getFormatName()
 | 
			
		||||
                                method
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatReader (InputStream* sourceStream,
 | 
			
		||||
                       const String& formatName);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~AudioFormatReader();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns a description of what type of format this is.
 | 
			
		||||
 | 
			
		||||
        E.g. "AIFF"
 | 
			
		||||
    */
 | 
			
		||||
    const String& getFormatName() const noexcept    { return formatName; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Reads samples from the stream.
 | 
			
		||||
 | 
			
		||||
        @param destChannels         an array of float buffers into which the sample data for each
 | 
			
		||||
                                    channel will be written. Channels that aren't needed can be null
 | 
			
		||||
        @param numDestChannels      the number of array elements in the destChannels array
 | 
			
		||||
        @param startSampleInSource  the position in the audio file or stream at which the samples
 | 
			
		||||
                                    should be read, as a number of samples from the start of the
 | 
			
		||||
                                    stream. It's ok for this to be beyond the start or end of the
 | 
			
		||||
                                    available data - any samples that are out-of-range will be returned
 | 
			
		||||
                                    as zeros.
 | 
			
		||||
        @param numSamplesToRead     the number of samples to read. If this is greater than the number
 | 
			
		||||
                                    of samples that the file or stream contains. the result will be padded
 | 
			
		||||
                                    with zeros
 | 
			
		||||
        @returns                    true if the operation succeeded, false if there was an error. Note
 | 
			
		||||
                                    that reading sections of data beyond the extent of the stream isn't an
 | 
			
		||||
                                    error - the reader should just return zeros for these regions
 | 
			
		||||
        @see readMaxLevels
 | 
			
		||||
    */
 | 
			
		||||
    bool read (float* const* destChannels, int numDestChannels,
 | 
			
		||||
               int64 startSampleInSource, int numSamplesToRead);
 | 
			
		||||
 | 
			
		||||
    /** Reads samples from the stream.
 | 
			
		||||
 | 
			
		||||
        @param destChannels         an array of buffers into which the sample data for each
 | 
			
		||||
                                    channel will be written.
 | 
			
		||||
                                    If the format is fixed-point, each channel will be written
 | 
			
		||||
                                    as an array of 32-bit signed integers using the full
 | 
			
		||||
                                    range -0x80000000 to 0x7fffffff, regardless of the source's
 | 
			
		||||
                                    bit-depth. If it is a floating-point format, you should cast
 | 
			
		||||
                                    the resulting array to a (float**) to get the values (in the
 | 
			
		||||
                                    range -1.0 to 1.0 or beyond)
 | 
			
		||||
                                    If the format is stereo, then destChannels[0] is the left channel
 | 
			
		||||
                                    data, and destChannels[1] is the right channel.
 | 
			
		||||
                                    The numDestChannels parameter indicates how many pointers this array
 | 
			
		||||
                                    contains, but some of these pointers can be null if you don't want to
 | 
			
		||||
                                    read data for some of the channels
 | 
			
		||||
        @param numDestChannels      the number of array elements in the destChannels array
 | 
			
		||||
        @param startSampleInSource  the position in the audio file or stream at which the samples
 | 
			
		||||
                                    should be read, as a number of samples from the start of the
 | 
			
		||||
                                    stream. It's ok for this to be beyond the start or end of the
 | 
			
		||||
                                    available data - any samples that are out-of-range will be returned
 | 
			
		||||
                                    as zeros.
 | 
			
		||||
        @param numSamplesToRead     the number of samples to read. If this is greater than the number
 | 
			
		||||
                                    of samples that the file or stream contains. the result will be padded
 | 
			
		||||
                                    with zeros
 | 
			
		||||
        @param fillLeftoverChannelsWithCopies   if true, this indicates that if there's no source data available
 | 
			
		||||
                                    for some of the channels that you pass in, then they should be filled with
 | 
			
		||||
                                    copies of valid source channels.
 | 
			
		||||
                                    E.g. if you're reading a mono file and you pass 2 channels to this method, then
 | 
			
		||||
                                    if fillLeftoverChannelsWithCopies is true, both destination channels will be filled
 | 
			
		||||
                                    with the same data from the file's single channel. If fillLeftoverChannelsWithCopies
 | 
			
		||||
                                    was false, then only the first channel would be filled with the file's contents, and
 | 
			
		||||
                                    the second would be cleared. If there are many channels, e.g. you try to read 4 channels
 | 
			
		||||
                                    from a stereo file, then the last 3 would all end up with copies of the same data.
 | 
			
		||||
        @returns                    true if the operation succeeded, false if there was an error. Note
 | 
			
		||||
                                    that reading sections of data beyond the extent of the stream isn't an
 | 
			
		||||
                                    error - the reader should just return zeros for these regions
 | 
			
		||||
        @see readMaxLevels
 | 
			
		||||
    */
 | 
			
		||||
    bool read (int* const* destChannels,
 | 
			
		||||
               int numDestChannels,
 | 
			
		||||
               int64 startSampleInSource,
 | 
			
		||||
               int numSamplesToRead,
 | 
			
		||||
               bool fillLeftoverChannelsWithCopies);
 | 
			
		||||
 | 
			
		||||
    /** Fills a section of an AudioBuffer from this reader.
 | 
			
		||||
 | 
			
		||||
        This will convert the reader's fixed- or floating-point data to
 | 
			
		||||
        the buffer's floating-point format, and will try to intelligently
 | 
			
		||||
        cope with mismatches between the number of channels in the reader
 | 
			
		||||
        and the buffer.
 | 
			
		||||
 | 
			
		||||
        @returns    true if the operation succeeded, false if there was an error. Note
 | 
			
		||||
                    that reading sections of data beyond the extent of the stream isn't an
 | 
			
		||||
                    error - the reader should just return zeros for these regions
 | 
			
		||||
    */
 | 
			
		||||
    bool read (AudioBuffer<float>* buffer,
 | 
			
		||||
               int startSampleInDestBuffer,
 | 
			
		||||
               int numSamples,
 | 
			
		||||
               int64 readerStartSample,
 | 
			
		||||
               bool useReaderLeftChan,
 | 
			
		||||
               bool useReaderRightChan);
 | 
			
		||||
 | 
			
		||||
    /** Finds the highest and lowest sample levels from a section of the audio stream.
 | 
			
		||||
 | 
			
		||||
        This will read a block of samples from the stream, and measure the
 | 
			
		||||
        highest and lowest sample levels from the channels in that section, returning
 | 
			
		||||
        these as normalised floating-point levels.
 | 
			
		||||
 | 
			
		||||
        @param startSample  the offset into the audio stream to start reading from. It's
 | 
			
		||||
                            ok for this to be beyond the start or end of the stream.
 | 
			
		||||
        @param numSamples   how many samples to read
 | 
			
		||||
        @param results      this array will be filled with Range values for each channel.
 | 
			
		||||
                            The array must contain numChannels elements.
 | 
			
		||||
        @param numChannelsToRead  the number of channels of data to scan. This must be
 | 
			
		||||
                            more than zero, but not more than the total number of channels
 | 
			
		||||
                            that the reader contains
 | 
			
		||||
        @see read
 | 
			
		||||
    */
 | 
			
		||||
    virtual void readMaxLevels (int64 startSample, int64 numSamples,
 | 
			
		||||
                                Range<float>* results, int numChannelsToRead);
 | 
			
		||||
 | 
			
		||||
    /** Finds the highest and lowest sample levels from a section of the audio stream.
 | 
			
		||||
 | 
			
		||||
        This will read a block of samples from the stream, and measure the
 | 
			
		||||
        highest and lowest sample levels from the channels in that section, returning
 | 
			
		||||
        these as normalised floating-point levels.
 | 
			
		||||
 | 
			
		||||
        @param startSample          the offset into the audio stream to start reading from. It's
 | 
			
		||||
                                    ok for this to be beyond the start or end of the stream.
 | 
			
		||||
        @param numSamples           how many samples to read
 | 
			
		||||
        @param lowestLeft           on return, this is the lowest absolute sample from the left channel
 | 
			
		||||
        @param highestLeft          on return, this is the highest absolute sample from the left channel
 | 
			
		||||
        @param lowestRight          on return, this is the lowest absolute sample from the right
 | 
			
		||||
                                    channel (if there is one)
 | 
			
		||||
        @param highestRight         on return, this is the highest absolute sample from the right
 | 
			
		||||
                                    channel (if there is one)
 | 
			
		||||
        @see read
 | 
			
		||||
    */
 | 
			
		||||
    virtual void readMaxLevels (int64 startSample, int64 numSamples,
 | 
			
		||||
                                float& lowestLeft,  float& highestLeft,
 | 
			
		||||
                                float& lowestRight, float& highestRight);
 | 
			
		||||
 | 
			
		||||
    /** Scans the source looking for a sample whose magnitude is in a specified range.
 | 
			
		||||
 | 
			
		||||
        This will read from the source, either forwards or backwards between two sample
 | 
			
		||||
        positions, until it finds a sample whose magnitude lies between two specified levels.
 | 
			
		||||
 | 
			
		||||
        If it finds a suitable sample, it returns its position; if not, it will return -1.
 | 
			
		||||
 | 
			
		||||
        There's also a minimumConsecutiveSamples setting to help avoid spikes or zero-crossing
 | 
			
		||||
        points when you're searching for a continuous range of samples
 | 
			
		||||
 | 
			
		||||
        @param startSample              the first sample to look at
 | 
			
		||||
        @param numSamplesToSearch       the number of samples to scan. If this value is negative,
 | 
			
		||||
                                        the search will go backwards
 | 
			
		||||
        @param magnitudeRangeMinimum    the lowest magnitude (inclusive) that is considered a hit, from 0 to 1.0
 | 
			
		||||
        @param magnitudeRangeMaximum    the highest magnitude (inclusive) that is considered a hit, from 0 to 1.0
 | 
			
		||||
        @param minimumConsecutiveSamples    if this is > 0, the method will only look for a sequence
 | 
			
		||||
                                            of this many consecutive samples, all of which lie
 | 
			
		||||
                                            within the target range. When it finds such a sequence,
 | 
			
		||||
                                            it returns the position of the first in-range sample
 | 
			
		||||
                                            it found (i.e. the earliest one if scanning forwards, the
 | 
			
		||||
                                            latest one if scanning backwards)
 | 
			
		||||
    */
 | 
			
		||||
    int64 searchForLevel (int64 startSample,
 | 
			
		||||
                          int64 numSamplesToSearch,
 | 
			
		||||
                          double magnitudeRangeMinimum,
 | 
			
		||||
                          double magnitudeRangeMaximum,
 | 
			
		||||
                          int minimumConsecutiveSamples);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The sample-rate of the stream. */
 | 
			
		||||
    double sampleRate = 0;
 | 
			
		||||
 | 
			
		||||
    /** The number of bits per sample, e.g. 16, 24, 32. */
 | 
			
		||||
    unsigned int bitsPerSample = 0;
 | 
			
		||||
 | 
			
		||||
    /** The total number of samples in the audio stream. */
 | 
			
		||||
    int64 lengthInSamples = 0;
 | 
			
		||||
 | 
			
		||||
    /** The total number of channels in the audio stream. */
 | 
			
		||||
    unsigned int numChannels = 0;
 | 
			
		||||
 | 
			
		||||
    /** Indicates whether the data is floating-point or fixed. */
 | 
			
		||||
    bool usesFloatingPointData = false;
 | 
			
		||||
 | 
			
		||||
    /** A set of metadata values that the reader has pulled out of the stream.
 | 
			
		||||
 | 
			
		||||
        Exactly what these values are depends on the format, so you can
 | 
			
		||||
        check out the format implementation code to see what kind of stuff
 | 
			
		||||
        they understand.
 | 
			
		||||
    */
 | 
			
		||||
    StringPairArray metadataValues;
 | 
			
		||||
 | 
			
		||||
    /** The input stream, for use by subclasses. */
 | 
			
		||||
    InputStream* input;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Get the channel layout of the audio stream. */
 | 
			
		||||
    virtual AudioChannelSet getChannelLayout();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Subclasses must implement this method to perform the low-level read operation.
 | 
			
		||||
 | 
			
		||||
        Callers should use read() instead of calling this directly.
 | 
			
		||||
 | 
			
		||||
        @param destChannels              the array of destination buffers to fill. Some of these
 | 
			
		||||
                                         pointers may be null
 | 
			
		||||
        @param numDestChannels           the number of items in the destChannels array. This
 | 
			
		||||
                                         value is guaranteed not to be greater than the number of
 | 
			
		||||
                                         channels that this reader object contains
 | 
			
		||||
        @param startOffsetInDestBuffer   the number of samples from the start of the
 | 
			
		||||
                                         dest data at which to begin writing
 | 
			
		||||
        @param startSampleInFile         the number of samples into the source data at which
 | 
			
		||||
                                         to begin reading. This value is guaranteed to be >= 0.
 | 
			
		||||
        @param numSamples                the number of samples to read
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool readSamples (int** destChannels,
 | 
			
		||||
                              int numDestChannels,
 | 
			
		||||
                              int startOffsetInDestBuffer,
 | 
			
		||||
                              int64 startSampleInFile,
 | 
			
		||||
                              int numSamples) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Used by AudioFormatReader subclasses to copy data to different formats. */
 | 
			
		||||
    template <class DestSampleType, class SourceSampleType, class SourceEndianness>
 | 
			
		||||
    struct ReadHelper
 | 
			
		||||
    {
 | 
			
		||||
        using DestType   = AudioData::Pointer<DestSampleType,   AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
 | 
			
		||||
        using SourceType = AudioData::Pointer<SourceSampleType, SourceEndianness, AudioData::Interleaved, AudioData::Const>;
 | 
			
		||||
 | 
			
		||||
        template <typename TargetType>
 | 
			
		||||
        static void read (TargetType* const* destData, int destOffset, int numDestChannels,
 | 
			
		||||
                          const void* sourceData, int numSourceChannels, int numSamples) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < numDestChannels; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                if (void* targetChan = destData[i])
 | 
			
		||||
                {
 | 
			
		||||
                    DestType dest (targetChan);
 | 
			
		||||
                    dest += destOffset;
 | 
			
		||||
 | 
			
		||||
                    if (i < numSourceChannels)
 | 
			
		||||
                        dest.convertSamples (SourceType (addBytesToPointer (sourceData, i * SourceType::getBytesPerSample()), numSourceChannels), numSamples);
 | 
			
		||||
                    else
 | 
			
		||||
                        dest.clearSamples (numSamples);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie
 | 
			
		||||
        beyond the end of their available length.
 | 
			
		||||
    */
 | 
			
		||||
    static void clearSamplesBeyondAvailableLength (int** destChannels, int numDestChannels,
 | 
			
		||||
                                                   int startOffsetInDestBuffer, int64 startSampleInFile,
 | 
			
		||||
                                                   int& numSamples, int64 fileLengthInSamples)
 | 
			
		||||
    {
 | 
			
		||||
        if (destChannels == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            jassertfalse;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const int64 samplesAvailable = fileLengthInSamples - startSampleInFile;
 | 
			
		||||
 | 
			
		||||
        if (samplesAvailable < numSamples)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = numDestChannels; --i >= 0;)
 | 
			
		||||
                if (destChannels[i] != nullptr)
 | 
			
		||||
                    zeromem (destChannels[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int));
 | 
			
		||||
 | 
			
		||||
            numSamples = (int) samplesAvailable;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    String formatName;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,103 +1,91 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioFormatReaderSource::AudioFormatReaderSource (AudioFormatReader* const r,
 | 
			
		||||
                                                  const bool deleteReaderWhenThisIsDeleted)
 | 
			
		||||
    : reader (r, deleteReaderWhenThisIsDeleted),
 | 
			
		||||
      nextPlayPos (0),
 | 
			
		||||
      looping (false),
 | 
			
		||||
      loopStartPos(0), loopLen(reader->lengthInSamples)
 | 
			
		||||
{
 | 
			
		||||
    jassert (reader != nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatReaderSource::~AudioFormatReaderSource() {}
 | 
			
		||||
 | 
			
		||||
int64 AudioFormatReaderSource::getTotalLength() const                   { return reader->lengthInSamples; }
 | 
			
		||||
void AudioFormatReaderSource::setNextReadPosition (int64 newPosition)   { nextPlayPos = newPosition; }
 | 
			
		||||
void AudioFormatReaderSource::setLooping (bool shouldLoop)              { looping = shouldLoop; }
 | 
			
		||||
 | 
			
		||||
void AudioFormatReaderSource::setLoopRange (int64 loopStart, int64 loopLength)
 | 
			
		||||
{
 | 
			
		||||
    loopStartPos = jmax((int64)0, jmin(loopStart, reader->lengthInSamples - 1));
 | 
			
		||||
    loopLen =  jmax((int64)1, jmin(reader->lengthInSamples - loopStartPos, loopLength));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64 AudioFormatReaderSource::getNextReadPosition() const
 | 
			
		||||
{
 | 
			
		||||
    if (looping) {
 | 
			
		||||
        return nextPlayPos > loopStartPos ? ((nextPlayPos - loopStartPos) % loopLen) + loopStartPos : nextPlayPos;
 | 
			
		||||
    }
 | 
			
		||||
    else return nextPlayPos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReaderSource::prepareToPlay (int /*samplesPerBlockExpected*/, double /*sampleRate*/) {}
 | 
			
		||||
void AudioFormatReaderSource::releaseResources() {}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
 | 
			
		||||
{
 | 
			
		||||
    if (info.numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int64 start = nextPlayPos;
 | 
			
		||||
 | 
			
		||||
        if (looping)
 | 
			
		||||
        {
 | 
			
		||||
            // TODO - crossfade loop boundary if possible
 | 
			
		||||
            const int64 loopstart = loopStartPos;
 | 
			
		||||
            const int64 newStart = start > loopstart ? ((start - loopstart) % loopLen) + loopstart : start;
 | 
			
		||||
            const int64 newEnd = start + info.numSamples > loopstart ? ((start + info.numSamples - loopstart) % loopLen) + loopstart : start + info.numSamples;
 | 
			
		||||
 | 
			
		||||
            if (newEnd > newStart)
 | 
			
		||||
            {
 | 
			
		||||
                reader->read (info.buffer, info.startSample,
 | 
			
		||||
                              (int) (newEnd - newStart), newStart, true, true);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                const int endSamps = (int) ((loopstart + loopLen) - newStart);
 | 
			
		||||
 | 
			
		||||
                reader->read (info.buffer, info.startSample,
 | 
			
		||||
                              endSamps, newStart, true, true);
 | 
			
		||||
 | 
			
		||||
                reader->read (info.buffer, info.startSample + endSamps,
 | 
			
		||||
                              (int) (newEnd - loopstart), loopstart, true, true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            nextPlayPos = newEnd;
 | 
			
		||||
            // DBG(String::formatted("Next playpos: %Ld", nextPlayPos));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            reader->read (info.buffer, info.startSample,
 | 
			
		||||
                          info.numSamples, start, true, true);
 | 
			
		||||
            nextPlayPos += info.numSamples;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioFormatReaderSource::AudioFormatReaderSource (AudioFormatReader* const r,
 | 
			
		||||
                                                  const bool deleteReaderWhenThisIsDeleted)
 | 
			
		||||
    : reader (r, deleteReaderWhenThisIsDeleted),
 | 
			
		||||
      nextPlayPos (0),
 | 
			
		||||
      looping (false)
 | 
			
		||||
{
 | 
			
		||||
    jassert (reader != nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatReaderSource::~AudioFormatReaderSource() {}
 | 
			
		||||
 | 
			
		||||
int64 AudioFormatReaderSource::getTotalLength() const                   { return reader->lengthInSamples; }
 | 
			
		||||
void AudioFormatReaderSource::setNextReadPosition (int64 newPosition)   { nextPlayPos = newPosition; }
 | 
			
		||||
void AudioFormatReaderSource::setLooping (bool shouldLoop)              { looping = shouldLoop; }
 | 
			
		||||
 | 
			
		||||
int64 AudioFormatReaderSource::getNextReadPosition() const
 | 
			
		||||
{
 | 
			
		||||
    return looping ? nextPlayPos % reader->lengthInSamples
 | 
			
		||||
                   : nextPlayPos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReaderSource::prepareToPlay (int /*samplesPerBlockExpected*/, double /*sampleRate*/) {}
 | 
			
		||||
void AudioFormatReaderSource::releaseResources() {}
 | 
			
		||||
 | 
			
		||||
void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
 | 
			
		||||
{
 | 
			
		||||
    if (info.numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int64 start = nextPlayPos;
 | 
			
		||||
 | 
			
		||||
        if (looping)
 | 
			
		||||
        {
 | 
			
		||||
            const int64 newStart = start % reader->lengthInSamples;
 | 
			
		||||
            const int64 newEnd = (start + info.numSamples) % reader->lengthInSamples;
 | 
			
		||||
 | 
			
		||||
            if (newEnd > newStart)
 | 
			
		||||
            {
 | 
			
		||||
                reader->read (info.buffer, info.startSample,
 | 
			
		||||
                              (int) (newEnd - newStart), newStart, true, true);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                const int endSamps = (int) (reader->lengthInSamples - newStart);
 | 
			
		||||
 | 
			
		||||
                reader->read (info.buffer, info.startSample,
 | 
			
		||||
                              endSamps, newStart, true, true);
 | 
			
		||||
 | 
			
		||||
                reader->read (info.buffer, info.startSample + endSamps,
 | 
			
		||||
                              (int) newEnd, 0, true, true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            nextPlayPos = newEnd;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            reader->read (info.buffer, info.startSample,
 | 
			
		||||
                          info.numSamples, start, true, true);
 | 
			
		||||
            nextPlayPos += info.numSamples;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,109 +1,101 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A type of AudioSource that will read from an AudioFormatReader.
 | 
			
		||||
 | 
			
		||||
    @see PositionableAudioSource, AudioTransportSource, BufferingAudioSource
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormatReaderSource  : public PositionableAudioSource
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioFormatReaderSource for a given reader.
 | 
			
		||||
 | 
			
		||||
        @param sourceReader                     the reader to use as the data source - this must
 | 
			
		||||
                                                not be null
 | 
			
		||||
        @param deleteReaderWhenThisIsDeleted    if true, the reader passed-in will be deleted
 | 
			
		||||
                                                when this object is deleted; if false it will be
 | 
			
		||||
                                                left up to the caller to manage its lifetime
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatReaderSource (AudioFormatReader* sourceReader,
 | 
			
		||||
                             bool deleteReaderWhenThisIsDeleted);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~AudioFormatReaderSource() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Toggles loop-mode.
 | 
			
		||||
 | 
			
		||||
        If set to true, it will continuously loop the input source. If false,
 | 
			
		||||
        it will just emit silence after the source has finished.
 | 
			
		||||
 | 
			
		||||
        @see isLooping
 | 
			
		||||
    */
 | 
			
		||||
    void setLooping (bool shouldLoop) override;
 | 
			
		||||
 | 
			
		||||
    /** Returns whether loop-mode is turned on or not. */
 | 
			
		||||
    bool isLooping() const override                             { return looping; }
 | 
			
		||||
 | 
			
		||||
    /** Sets the start position of the looping in samples. */
 | 
			
		||||
    void setLoopRange (int64 loopStart, int64 loopLength) override;
 | 
			
		||||
    
 | 
			
		||||
    /** Returns the position where the loop playback starts.  */
 | 
			
		||||
    void getLoopRange(int64 & loopStart, int64 & loopLength) const override { loopStart = loopStartPos; loopLength = loopLen; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the reader that's being used. */
 | 
			
		||||
    AudioFormatReader* getAudioFormatReader() const noexcept    { return reader; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Implementation of the AudioSource method. */
 | 
			
		||||
    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
 | 
			
		||||
 | 
			
		||||
    /** Implementation of the AudioSource method. */
 | 
			
		||||
    void releaseResources() override;
 | 
			
		||||
 | 
			
		||||
    /** Implementation of the AudioSource method. */
 | 
			
		||||
    void getNextAudioBlock (const AudioSourceChannelInfo&) override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Implements the PositionableAudioSource method. */
 | 
			
		||||
    void setNextReadPosition (int64 newPosition) override;
 | 
			
		||||
 | 
			
		||||
    /** Implements the PositionableAudioSource method. */
 | 
			
		||||
    int64 getNextReadPosition() const override;
 | 
			
		||||
 | 
			
		||||
    /** Implements the PositionableAudioSource method. */
 | 
			
		||||
    int64 getTotalLength() const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OptionalScopedPointer<AudioFormatReader> reader;
 | 
			
		||||
 | 
			
		||||
    int64 nextPlayPos;
 | 
			
		||||
    bool looping;
 | 
			
		||||
    int64 loopStartPos;
 | 
			
		||||
    int64 loopLen;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReaderSource)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A type of AudioSource that will read from an AudioFormatReader.
 | 
			
		||||
 | 
			
		||||
    @see PositionableAudioSource, AudioTransportSource, BufferingAudioSource
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormatReaderSource  : public PositionableAudioSource
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioFormatReaderSource for a given reader.
 | 
			
		||||
 | 
			
		||||
        @param sourceReader                     the reader to use as the data source - this must
 | 
			
		||||
                                                not be null
 | 
			
		||||
        @param deleteReaderWhenThisIsDeleted    if true, the reader passed-in will be deleted
 | 
			
		||||
                                                when this object is deleted; if false it will be
 | 
			
		||||
                                                left up to the caller to manage its lifetime
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatReaderSource (AudioFormatReader* sourceReader,
 | 
			
		||||
                             bool deleteReaderWhenThisIsDeleted);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~AudioFormatReaderSource() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Toggles loop-mode.
 | 
			
		||||
 | 
			
		||||
        If set to true, it will continuously loop the input source. If false,
 | 
			
		||||
        it will just emit silence after the source has finished.
 | 
			
		||||
 | 
			
		||||
        @see isLooping
 | 
			
		||||
    */
 | 
			
		||||
    void setLooping (bool shouldLoop) override;
 | 
			
		||||
 | 
			
		||||
    /** Returns whether loop-mode is turned on or not. */
 | 
			
		||||
    bool isLooping() const override                             { return looping; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the reader that's being used. */
 | 
			
		||||
    AudioFormatReader* getAudioFormatReader() const noexcept    { return reader; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Implementation of the AudioSource method. */
 | 
			
		||||
    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
 | 
			
		||||
 | 
			
		||||
    /** Implementation of the AudioSource method. */
 | 
			
		||||
    void releaseResources() override;
 | 
			
		||||
 | 
			
		||||
    /** Implementation of the AudioSource method. */
 | 
			
		||||
    void getNextAudioBlock (const AudioSourceChannelInfo&) override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Implements the PositionableAudioSource method. */
 | 
			
		||||
    void setNextReadPosition (int64 newPosition) override;
 | 
			
		||||
 | 
			
		||||
    /** Implements the PositionableAudioSource method. */
 | 
			
		||||
    int64 getNextReadPosition() const override;
 | 
			
		||||
 | 
			
		||||
    /** Implements the PositionableAudioSource method. */
 | 
			
		||||
    int64 getTotalLength() const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OptionalScopedPointer<AudioFormatReader> reader;
 | 
			
		||||
 | 
			
		||||
    int64 nextPlayPos;
 | 
			
		||||
    bool looping;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReaderSource)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,371 +1,362 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
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),
 | 
			
		||||
    channelLayout (AudioChannelSet::canonicalChannelSet(static_cast<int> (numChannels_))),
 | 
			
		||||
    output (out),
 | 
			
		||||
    formatName (formatName_)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter::AudioFormatWriter (OutputStream* const out,
 | 
			
		||||
                                      const String& formatName_,
 | 
			
		||||
                                      const double rate,
 | 
			
		||||
                                      const AudioChannelSet& channelLayout_,
 | 
			
		||||
                                      const unsigned int bitsPerSample_)
 | 
			
		||||
  : sampleRate (rate),
 | 
			
		||||
    numChannels (static_cast<unsigned int> (channelLayout_.size())),
 | 
			
		||||
    bitsPerSample (bitsPerSample_),
 | 
			
		||||
    usesFloatingPointData (false),
 | 
			
		||||
    channelLayout (channelLayout_),
 | 
			
		||||
    output (out),
 | 
			
		||||
    formatName (formatName_)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter::~AudioFormatWriter()
 | 
			
		||||
{
 | 
			
		||||
    delete output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void convertFloatsToInts (int* dest, const float* src, int numSamples) noexcept
 | 
			
		||||
{
 | 
			
		||||
    while (--numSamples >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        const double samp = *src++;
 | 
			
		||||
 | 
			
		||||
        if (samp <= -1.0)
 | 
			
		||||
            *dest = std::numeric_limits<int>::min();
 | 
			
		||||
        else if (samp >= 1.0)
 | 
			
		||||
            *dest = std::numeric_limits<int>::max();
 | 
			
		||||
        else
 | 
			
		||||
            *dest = roundToInt (std::numeric_limits<int>::max() * samp);
 | 
			
		||||
 | 
			
		||||
        ++dest;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader,
 | 
			
		||||
                                              int64 startSample,
 | 
			
		||||
                                              int64 numSamplesToRead)
 | 
			
		||||
{
 | 
			
		||||
    const int bufferSize = 16384;
 | 
			
		||||
    AudioBuffer<float> tempBuffer ((int) numChannels, bufferSize);
 | 
			
		||||
 | 
			
		||||
    int* buffers[128] = { nullptr };
 | 
			
		||||
 | 
			
		||||
    for (int i = tempBuffer.getNumChannels(); --i >= 0;)
 | 
			
		||||
        buffers[i] = reinterpret_cast<int*> (tempBuffer.getWritePointer (i, 0));
 | 
			
		||||
 | 
			
		||||
    if (numSamplesToRead < 0)
 | 
			
		||||
        numSamplesToRead = reader.lengthInSamples;
 | 
			
		||||
 | 
			
		||||
    while (numSamplesToRead > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize);
 | 
			
		||||
 | 
			
		||||
        if (! reader.read (buffers, (int) numChannels, startSample, numToDo, false))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (reader.usesFloatingPointData != isFloatingPoint())
 | 
			
		||||
        {
 | 
			
		||||
            int** bufferChan = buffers;
 | 
			
		||||
 | 
			
		||||
            while (*bufferChan != nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                void* const b = *bufferChan++;
 | 
			
		||||
 | 
			
		||||
                constexpr auto scaleFactor = 1.0f / static_cast<float> (0x7fffffff);
 | 
			
		||||
 | 
			
		||||
                if (isFloatingPoint())
 | 
			
		||||
                    FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, scaleFactor, numToDo);
 | 
			
		||||
                else
 | 
			
		||||
                    convertFloatsToInts ((int*) b, (float*) b, numToDo);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (! write (const_cast<const int**> (buffers), numToDo))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        numSamplesToRead -= numToDo;
 | 
			
		||||
        startSample += numToDo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock)
 | 
			
		||||
{
 | 
			
		||||
    AudioBuffer<float> tempBuffer (getNumChannels(), samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
    while (numSamplesToRead > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numToDo = jmin (numSamplesToRead, samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
        AudioSourceChannelInfo info (&tempBuffer, 0, numToDo);
 | 
			
		||||
        info.clearActiveBufferRegion();
 | 
			
		||||
 | 
			
		||||
        source.getNextAudioBlock (info);
 | 
			
		||||
 | 
			
		||||
        if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        numSamplesToRead -= numToDo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int numSourceChannels, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    if (numSamples <= 0)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    if (isFloatingPoint())
 | 
			
		||||
        return write ((const int**) channels, numSamples);
 | 
			
		||||
 | 
			
		||||
    std::vector<int*> chans (256);
 | 
			
		||||
    std::vector<int> scratch (4096);
 | 
			
		||||
 | 
			
		||||
    jassert (numSourceChannels < (int) chans.size());
 | 
			
		||||
    const int maxSamples = (int) scratch.size() / numSourceChannels;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numSourceChannels; ++i)
 | 
			
		||||
        chans[(size_t) i] = scratch.data() + (i * maxSamples);
 | 
			
		||||
 | 
			
		||||
    chans[(size_t) numSourceChannels] = nullptr;
 | 
			
		||||
    int startSample = 0;
 | 
			
		||||
 | 
			
		||||
    while (numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numToDo = jmin (numSamples, maxSamples);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numSourceChannels; ++i)
 | 
			
		||||
            convertFloatsToInts (chans[(size_t) i], channels[(size_t) i] + startSample, numToDo);
 | 
			
		||||
 | 
			
		||||
        if (! write ((const int**) chans.data(), numToDo))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        startSample += numToDo;
 | 
			
		||||
        numSamples  -= numToDo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioBuffer<float>& source, int startSample, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    auto numSourceChannels = source.getNumChannels();
 | 
			
		||||
    jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0);
 | 
			
		||||
 | 
			
		||||
    if (startSample == 0)
 | 
			
		||||
        return writeFromFloatArrays (source.getArrayOfReadPointers(), numSourceChannels, numSamples);
 | 
			
		||||
 | 
			
		||||
    const float* chans[256];
 | 
			
		||||
    jassert ((int) numChannels < numElementsInArray (chans));
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numSourceChannels; ++i)
 | 
			
		||||
        chans[i] = source.getReadPointer (i, startSample);
 | 
			
		||||
 | 
			
		||||
    chans[numSourceChannels] = nullptr;
 | 
			
		||||
 | 
			
		||||
    return writeFromFloatArrays (chans, numSourceChannels, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::flush()
 | 
			
		||||
{
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class AudioFormatWriter::ThreadedWriter::Buffer   : private TimeSliceClient
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples)
 | 
			
		||||
        : fifo (numSamples),
 | 
			
		||||
          buffer (channels, numSamples),
 | 
			
		||||
          timeSliceThread (tst),
 | 
			
		||||
          writer (w)
 | 
			
		||||
    {
 | 
			
		||||
        timeSliceThread.addTimeSliceClient (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~Buffer() override
 | 
			
		||||
    {
 | 
			
		||||
        isRunning = false;
 | 
			
		||||
        timeSliceThread.removeTimeSliceClient (this);
 | 
			
		||||
 | 
			
		||||
        while (writePendingData() == 0)
 | 
			
		||||
        {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool write (const float* const* 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;
 | 
			
		||||
        fifo.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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fifo.finishedWrite (size1 + size2);
 | 
			
		||||
        timeSliceThread.notify();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int useTimeSlice() override
 | 
			
		||||
    {
 | 
			
		||||
        return writePendingData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int writePendingData()
 | 
			
		||||
    {
 | 
			
		||||
        auto numToDo = fifo.getTotalSize() / 4;
 | 
			
		||||
 | 
			
		||||
        int start1, size1, start2, size2;
 | 
			
		||||
        fifo.prepareToRead (numToDo, start1, size1, start2, size2);
 | 
			
		||||
 | 
			
		||||
        if (size1 <= 0)
 | 
			
		||||
            return 10;
 | 
			
		||||
 | 
			
		||||
        writer->writeFromAudioSampleBuffer (buffer, start1, size1);
 | 
			
		||||
 | 
			
		||||
        const ScopedLock sl (thumbnailLock);
 | 
			
		||||
 | 
			
		||||
        if (receiver != nullptr)
 | 
			
		||||
            receiver->addBlock (samplesWritten, buffer, start1, size1);
 | 
			
		||||
 | 
			
		||||
        samplesWritten += size1;
 | 
			
		||||
 | 
			
		||||
        if (size2 > 0)
 | 
			
		||||
        {
 | 
			
		||||
            writer->writeFromAudioSampleBuffer (buffer, start2, size2);
 | 
			
		||||
 | 
			
		||||
            if (receiver != nullptr)
 | 
			
		||||
                receiver->addBlock (samplesWritten, buffer, start2, size2);
 | 
			
		||||
 | 
			
		||||
            samplesWritten += size2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fifo.finishedRead (size1 + size2);
 | 
			
		||||
 | 
			
		||||
        if (samplesPerFlush > 0)
 | 
			
		||||
        {
 | 
			
		||||
            flushSampleCounter -= size1 + size2;
 | 
			
		||||
 | 
			
		||||
            if (flushSampleCounter <= 0)
 | 
			
		||||
            {
 | 
			
		||||
                flushSampleCounter = samplesPerFlush;
 | 
			
		||||
                writer->flush();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setDataReceiver (IncomingDataReceiver* newReceiver)
 | 
			
		||||
    {
 | 
			
		||||
        if (newReceiver != nullptr)
 | 
			
		||||
            newReceiver->reset (buffer.getNumChannels(), writer->getSampleRate(), 0);
 | 
			
		||||
 | 
			
		||||
        const ScopedLock sl (thumbnailLock);
 | 
			
		||||
        receiver = newReceiver;
 | 
			
		||||
        samplesWritten = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setFlushInterval (int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        samplesPerFlush = numSamples;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    AudioFormatWriter * getWriter() const { return writer.get(); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    AbstractFifo fifo;
 | 
			
		||||
    AudioBuffer<float> buffer;
 | 
			
		||||
    TimeSliceThread& timeSliceThread;
 | 
			
		||||
    std::unique_ptr<AudioFormatWriter> writer;
 | 
			
		||||
    CriticalSection thumbnailLock;
 | 
			
		||||
    IncomingDataReceiver* receiver = {};
 | 
			
		||||
    int64 samplesWritten = 0;
 | 
			
		||||
    int samplesPerFlush = 0, flushSampleCounter = 0;
 | 
			
		||||
    std::atomic<bool> isRunning { true };
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE (Buffer)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer)
 | 
			
		||||
    : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, (int) writer->numChannels, numSamplesToBuffer))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter::ThreadedWriter::~ThreadedWriter()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    return buffer->write (data, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatWriter::ThreadedWriter::setDataReceiver (AudioFormatWriter::ThreadedWriter::IncomingDataReceiver* receiver)
 | 
			
		||||
{
 | 
			
		||||
    buffer->setDataReceiver (receiver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept
 | 
			
		||||
{
 | 
			
		||||
    buffer->setFlushInterval (numSamplesPerFlush);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter * AudioFormatWriter::ThreadedWriter::getWriter() const
 | 
			
		||||
{
 | 
			
		||||
    return buffer->getWriter();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
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),
 | 
			
		||||
    channelLayout (AudioChannelSet::canonicalChannelSet(static_cast<int> (numChannels_))),
 | 
			
		||||
    output (out),
 | 
			
		||||
    formatName (formatName_)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter::AudioFormatWriter (OutputStream* const out,
 | 
			
		||||
                                      const String& formatName_,
 | 
			
		||||
                                      const double rate,
 | 
			
		||||
                                      const AudioChannelSet& channelLayout_,
 | 
			
		||||
                                      const unsigned int bitsPerSample_)
 | 
			
		||||
  : sampleRate (rate),
 | 
			
		||||
    numChannels (static_cast<unsigned int> (channelLayout_.size())),
 | 
			
		||||
    bitsPerSample (bitsPerSample_),
 | 
			
		||||
    usesFloatingPointData (false),
 | 
			
		||||
    channelLayout (channelLayout_),
 | 
			
		||||
    output (out),
 | 
			
		||||
    formatName (formatName_)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter::~AudioFormatWriter()
 | 
			
		||||
{
 | 
			
		||||
    delete output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void convertFloatsToInts (int* dest, const float* src, int numSamples) noexcept
 | 
			
		||||
{
 | 
			
		||||
    while (--numSamples >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        const double samp = *src++;
 | 
			
		||||
 | 
			
		||||
        if (samp <= -1.0)
 | 
			
		||||
            *dest = std::numeric_limits<int>::min();
 | 
			
		||||
        else if (samp >= 1.0)
 | 
			
		||||
            *dest = std::numeric_limits<int>::max();
 | 
			
		||||
        else
 | 
			
		||||
            *dest = roundToInt (std::numeric_limits<int>::max() * samp);
 | 
			
		||||
 | 
			
		||||
        ++dest;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader,
 | 
			
		||||
                                              int64 startSample,
 | 
			
		||||
                                              int64 numSamplesToRead)
 | 
			
		||||
{
 | 
			
		||||
    const int bufferSize = 16384;
 | 
			
		||||
    AudioBuffer<float> tempBuffer ((int) numChannels, bufferSize);
 | 
			
		||||
 | 
			
		||||
    int* buffers[128] = { nullptr };
 | 
			
		||||
 | 
			
		||||
    for (int i = tempBuffer.getNumChannels(); --i >= 0;)
 | 
			
		||||
        buffers[i] = reinterpret_cast<int*> (tempBuffer.getWritePointer (i, 0));
 | 
			
		||||
 | 
			
		||||
    if (numSamplesToRead < 0)
 | 
			
		||||
        numSamplesToRead = reader.lengthInSamples;
 | 
			
		||||
 | 
			
		||||
    while (numSamplesToRead > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize);
 | 
			
		||||
 | 
			
		||||
        if (! reader.read (buffers, (int) numChannels, startSample, numToDo, false))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (reader.usesFloatingPointData != isFloatingPoint())
 | 
			
		||||
        {
 | 
			
		||||
            int** bufferChan = buffers;
 | 
			
		||||
 | 
			
		||||
            while (*bufferChan != nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                void* const b = *bufferChan++;
 | 
			
		||||
 | 
			
		||||
                constexpr auto scaleFactor = 1.0f / static_cast<float> (0x7fffffff);
 | 
			
		||||
 | 
			
		||||
                if (isFloatingPoint())
 | 
			
		||||
                    FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, scaleFactor, numToDo);
 | 
			
		||||
                else
 | 
			
		||||
                    convertFloatsToInts ((int*) b, (float*) b, numToDo);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (! write (const_cast<const int**> (buffers), numToDo))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        numSamplesToRead -= numToDo;
 | 
			
		||||
        startSample += numToDo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock)
 | 
			
		||||
{
 | 
			
		||||
    AudioBuffer<float> tempBuffer (getNumChannels(), samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
    while (numSamplesToRead > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numToDo = jmin (numSamplesToRead, samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
        AudioSourceChannelInfo info (&tempBuffer, 0, numToDo);
 | 
			
		||||
        info.clearActiveBufferRegion();
 | 
			
		||||
 | 
			
		||||
        source.getNextAudioBlock (info);
 | 
			
		||||
 | 
			
		||||
        if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        numSamplesToRead -= numToDo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int numSourceChannels, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    if (numSamples <= 0)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    if (isFloatingPoint())
 | 
			
		||||
        return write ((const int**) channels, numSamples);
 | 
			
		||||
 | 
			
		||||
    std::vector<int*> chans (256);
 | 
			
		||||
    std::vector<int> scratch (4096);
 | 
			
		||||
 | 
			
		||||
    jassert (numSourceChannels < (int) chans.size());
 | 
			
		||||
    const int maxSamples = (int) scratch.size() / numSourceChannels;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numSourceChannels; ++i)
 | 
			
		||||
        chans[(size_t) i] = scratch.data() + (i * maxSamples);
 | 
			
		||||
 | 
			
		||||
    chans[(size_t) numSourceChannels] = nullptr;
 | 
			
		||||
    int startSample = 0;
 | 
			
		||||
 | 
			
		||||
    while (numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto numToDo = jmin (numSamples, maxSamples);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numSourceChannels; ++i)
 | 
			
		||||
            convertFloatsToInts (chans[(size_t) i], channels[(size_t) i] + startSample, numToDo);
 | 
			
		||||
 | 
			
		||||
        if (! write ((const int**) chans.data(), numToDo))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        startSample += numToDo;
 | 
			
		||||
        numSamples  -= numToDo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioBuffer<float>& source, int startSample, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    auto numSourceChannels = source.getNumChannels();
 | 
			
		||||
    jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0);
 | 
			
		||||
 | 
			
		||||
    if (startSample == 0)
 | 
			
		||||
        return writeFromFloatArrays (source.getArrayOfReadPointers(), numSourceChannels, numSamples);
 | 
			
		||||
 | 
			
		||||
    const float* chans[256];
 | 
			
		||||
    jassert ((int) numChannels < numElementsInArray (chans));
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numSourceChannels; ++i)
 | 
			
		||||
        chans[i] = source.getReadPointer (i, startSample);
 | 
			
		||||
 | 
			
		||||
    chans[numSourceChannels] = nullptr;
 | 
			
		||||
 | 
			
		||||
    return writeFromFloatArrays (chans, numSourceChannels, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::flush()
 | 
			
		||||
{
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class AudioFormatWriter::ThreadedWriter::Buffer   : private TimeSliceClient
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples)
 | 
			
		||||
        : fifo (numSamples),
 | 
			
		||||
          buffer (channels, numSamples),
 | 
			
		||||
          timeSliceThread (tst),
 | 
			
		||||
          writer (w)
 | 
			
		||||
    {
 | 
			
		||||
        timeSliceThread.addTimeSliceClient (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~Buffer() override
 | 
			
		||||
    {
 | 
			
		||||
        isRunning = false;
 | 
			
		||||
        timeSliceThread.removeTimeSliceClient (this);
 | 
			
		||||
 | 
			
		||||
        while (writePendingData() == 0)
 | 
			
		||||
        {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool write (const float* const* 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;
 | 
			
		||||
        fifo.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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fifo.finishedWrite (size1 + size2);
 | 
			
		||||
        timeSliceThread.notify();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int useTimeSlice() override
 | 
			
		||||
    {
 | 
			
		||||
        return writePendingData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int writePendingData()
 | 
			
		||||
    {
 | 
			
		||||
        auto numToDo = fifo.getTotalSize() / 4;
 | 
			
		||||
 | 
			
		||||
        int start1, size1, start2, size2;
 | 
			
		||||
        fifo.prepareToRead (numToDo, start1, size1, start2, size2);
 | 
			
		||||
 | 
			
		||||
        if (size1 <= 0)
 | 
			
		||||
            return 10;
 | 
			
		||||
 | 
			
		||||
        writer->writeFromAudioSampleBuffer (buffer, start1, size1);
 | 
			
		||||
 | 
			
		||||
        const ScopedLock sl (thumbnailLock);
 | 
			
		||||
 | 
			
		||||
        if (receiver != nullptr)
 | 
			
		||||
            receiver->addBlock (samplesWritten, buffer, start1, size1);
 | 
			
		||||
 | 
			
		||||
        samplesWritten += size1;
 | 
			
		||||
 | 
			
		||||
        if (size2 > 0)
 | 
			
		||||
        {
 | 
			
		||||
            writer->writeFromAudioSampleBuffer (buffer, start2, size2);
 | 
			
		||||
 | 
			
		||||
            if (receiver != nullptr)
 | 
			
		||||
                receiver->addBlock (samplesWritten, buffer, start2, size2);
 | 
			
		||||
 | 
			
		||||
            samplesWritten += size2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fifo.finishedRead (size1 + size2);
 | 
			
		||||
 | 
			
		||||
        if (samplesPerFlush > 0)
 | 
			
		||||
        {
 | 
			
		||||
            flushSampleCounter -= size1 + size2;
 | 
			
		||||
 | 
			
		||||
            if (flushSampleCounter <= 0)
 | 
			
		||||
            {
 | 
			
		||||
                flushSampleCounter = samplesPerFlush;
 | 
			
		||||
                writer->flush();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setDataReceiver (IncomingDataReceiver* newReceiver)
 | 
			
		||||
    {
 | 
			
		||||
        if (newReceiver != nullptr)
 | 
			
		||||
            newReceiver->reset (buffer.getNumChannels(), writer->getSampleRate(), 0);
 | 
			
		||||
 | 
			
		||||
        const ScopedLock sl (thumbnailLock);
 | 
			
		||||
        receiver = newReceiver;
 | 
			
		||||
        samplesWritten = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setFlushInterval (int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        samplesPerFlush = numSamples;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    AbstractFifo fifo;
 | 
			
		||||
    AudioBuffer<float> buffer;
 | 
			
		||||
    TimeSliceThread& timeSliceThread;
 | 
			
		||||
    std::unique_ptr<AudioFormatWriter> writer;
 | 
			
		||||
    CriticalSection thumbnailLock;
 | 
			
		||||
    IncomingDataReceiver* receiver = {};
 | 
			
		||||
    int64 samplesWritten = 0;
 | 
			
		||||
    int samplesPerFlush = 0, flushSampleCounter = 0;
 | 
			
		||||
    std::atomic<bool> isRunning { true };
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE (Buffer)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer)
 | 
			
		||||
    : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, (int) writer->numChannels, numSamplesToBuffer))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioFormatWriter::ThreadedWriter::~ThreadedWriter()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    return buffer->write (data, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatWriter::ThreadedWriter::setDataReceiver (AudioFormatWriter::ThreadedWriter::IncomingDataReceiver* receiver)
 | 
			
		||||
{
 | 
			
		||||
    buffer->setDataReceiver (receiver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept
 | 
			
		||||
{
 | 
			
		||||
    buffer->setFlushInterval (numSamplesPerFlush);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,303 +1,299 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Writes samples to an audio file stream.
 | 
			
		||||
 | 
			
		||||
    A subclass that writes a specific type of audio format will be created by
 | 
			
		||||
    an AudioFormat object.
 | 
			
		||||
 | 
			
		||||
    After creating one of these with the AudioFormat::createWriterFor() method
 | 
			
		||||
    you can call its write() method to store the samples, and then delete it.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormat, AudioFormatReader
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormatWriter
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioFormatWriter object.
 | 
			
		||||
 | 
			
		||||
        @param destStream       the stream to write to - this will be deleted
 | 
			
		||||
                                by this object when it is no longer needed
 | 
			
		||||
        @param formatName       the description that will be returned by the getFormatName()
 | 
			
		||||
                                method
 | 
			
		||||
        @param sampleRate       the sample rate to use - the base class just stores
 | 
			
		||||
                                this value, it doesn't do anything with it
 | 
			
		||||
        @param numberOfChannels the number of channels to write - the base class just stores
 | 
			
		||||
                                this value, it doesn't do anything with it
 | 
			
		||||
        @param bitsPerSample    the bit depth of the stream - the base class just stores
 | 
			
		||||
                                this value, it doesn't do anything with it
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatWriter (OutputStream* destStream,
 | 
			
		||||
                       const String& formatName,
 | 
			
		||||
                       double sampleRate,
 | 
			
		||||
                       unsigned int numberOfChannels,
 | 
			
		||||
                       unsigned int bitsPerSample);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioFormatWriter object.
 | 
			
		||||
 | 
			
		||||
        @param destStream            the stream to write to - this will be deleted
 | 
			
		||||
                                     by this object when it is no longer needed
 | 
			
		||||
        @param formatName            the description that will be returned by the getFormatName()
 | 
			
		||||
                                     method
 | 
			
		||||
        @param sampleRate            the sample rate to use - the base class just stores
 | 
			
		||||
                                     this value, it doesn't do anything with it
 | 
			
		||||
        @param audioChannelLayout    the channel layout to use for the writer - the base class
 | 
			
		||||
                                     just stores this value, it doesn't do anything with it
 | 
			
		||||
        @param bitsPerSample         the bit depth of the stream - the base class just stores
 | 
			
		||||
                                     this value, it doesn't do anything with it
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatWriter (OutputStream* destStream,
 | 
			
		||||
                       const String& formatName,
 | 
			
		||||
                       double sampleRate,
 | 
			
		||||
                       const AudioChannelSet& audioChannelLayout,
 | 
			
		||||
                       unsigned int bitsPerSample);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~AudioFormatWriter();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns a description of what type of format this is.
 | 
			
		||||
 | 
			
		||||
        E.g. "AIFF file"
 | 
			
		||||
    */
 | 
			
		||||
    const String& getFormatName() const noexcept        { return formatName; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Writes a set of samples to the audio stream.
 | 
			
		||||
 | 
			
		||||
        Note that if you're trying to write the contents of an AudioBuffer, you
 | 
			
		||||
        can use writeFromAudioSampleBuffer().
 | 
			
		||||
 | 
			
		||||
        @param samplesToWrite   an array of arrays containing the sample data for
 | 
			
		||||
                                each channel to write. This is a zero-terminated
 | 
			
		||||
                                array of arrays, and can contain a different number
 | 
			
		||||
                                of channels than the actual stream uses, and the
 | 
			
		||||
                                writer should do its best to cope with this.
 | 
			
		||||
                                If the format is fixed-point, each channel will be formatted
 | 
			
		||||
                                as an array of signed integers using the full 32-bit
 | 
			
		||||
                                range -0x80000000 to 0x7fffffff, regardless of the source's
 | 
			
		||||
                                bit-depth. If it is a floating-point format, you should treat
 | 
			
		||||
                                the arrays as arrays of floats, and just cast it to an (int**)
 | 
			
		||||
                                to pass it into the method.
 | 
			
		||||
        @param numSamples       the number of samples to write
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool write (const int** samplesToWrite, int numSamples) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Some formats may support a flush operation that makes sure the file is in a
 | 
			
		||||
        valid state before carrying on.
 | 
			
		||||
        If supported, this means that by calling flush periodically when writing data
 | 
			
		||||
        to a large file, then it should still be left in a readable state if your program
 | 
			
		||||
        crashes.
 | 
			
		||||
        It goes without saying that this method must be called from the same thread that's
 | 
			
		||||
        calling write()!
 | 
			
		||||
        If the format supports flushing and the operation succeeds, this returns true.
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool flush();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Reads a section of samples from an AudioFormatReader, and writes these to
 | 
			
		||||
        the output.
 | 
			
		||||
 | 
			
		||||
        This will take care of any floating-point conversion that's required to convert
 | 
			
		||||
        between the two formats. It won't deal with sample-rate conversion, though.
 | 
			
		||||
 | 
			
		||||
        If numSamplesToRead < 0, it will write the entire length of the reader.
 | 
			
		||||
 | 
			
		||||
        @returns false if it can't read or write properly during the operation
 | 
			
		||||
    */
 | 
			
		||||
    bool writeFromAudioReader (AudioFormatReader& reader,
 | 
			
		||||
                               int64 startSample,
 | 
			
		||||
                               int64 numSamplesToRead);
 | 
			
		||||
 | 
			
		||||
    /** Reads some samples from an AudioSource, and writes these to the output.
 | 
			
		||||
 | 
			
		||||
        The source must already have been initialised with the AudioSource::prepareToPlay() method
 | 
			
		||||
 | 
			
		||||
        @param source               the source to read from
 | 
			
		||||
        @param numSamplesToRead     total number of samples to read and write
 | 
			
		||||
        @param samplesPerBlock      the maximum number of samples to fetch from the source
 | 
			
		||||
        @returns false if it can't read or write properly during the operation
 | 
			
		||||
    */
 | 
			
		||||
    bool writeFromAudioSource (AudioSource& source,
 | 
			
		||||
                               int numSamplesToRead,
 | 
			
		||||
                               int samplesPerBlock = 2048);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /** Writes some samples from an AudioBuffer. */
 | 
			
		||||
    bool writeFromAudioSampleBuffer (const AudioBuffer<float>& source,
 | 
			
		||||
                                     int startSample, int numSamples);
 | 
			
		||||
 | 
			
		||||
    /** Writes some samples from a set of float data channels. */
 | 
			
		||||
    bool writeFromFloatArrays (const float* const* channels, int numChannels, int numSamples);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the sample rate being used. */
 | 
			
		||||
    double getSampleRate() const noexcept       { return sampleRate; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of channels being written. */
 | 
			
		||||
    int getNumChannels() const noexcept         { return (int) numChannels; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the bit-depth of the data being written. */
 | 
			
		||||
    int getBitsPerSample() const noexcept       { return (int) bitsPerSample; }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if it's a floating-point format, false if it's fixed-point. */
 | 
			
		||||
    bool isFloatingPoint() const noexcept       { return usesFloatingPointData; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
        Provides a FIFO for an AudioFormatWriter, allowing you to push incoming
 | 
			
		||||
        data into a buffer which will be flushed to disk by a background thread.
 | 
			
		||||
    */
 | 
			
		||||
    class ThreadedWriter
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        /** Creates a ThreadedWriter for a given writer and a thread.
 | 
			
		||||
 | 
			
		||||
            The writer object which is passed in here will be owned and deleted by
 | 
			
		||||
            the ThreadedWriter when it is no longer needed.
 | 
			
		||||
 | 
			
		||||
            To stop the writer and flush the buffer to disk, simply delete this object.
 | 
			
		||||
        */
 | 
			
		||||
        ThreadedWriter (AudioFormatWriter* writer,
 | 
			
		||||
                        TimeSliceThread& backgroundThread,
 | 
			
		||||
                        int numSamplesToBuffer);
 | 
			
		||||
 | 
			
		||||
        /** Destructor. */
 | 
			
		||||
        ~ThreadedWriter();
 | 
			
		||||
 | 
			
		||||
        /** Pushes some incoming audio data into the FIFO.
 | 
			
		||||
 | 
			
		||||
            If there's enough free space in the buffer, this will add the data to it,
 | 
			
		||||
 | 
			
		||||
            If the FIFO is too full to accept this many samples, the method will return
 | 
			
		||||
            false - then you could either wait until the background thread has had time to
 | 
			
		||||
            consume some of the buffered data and try again, or you can give up
 | 
			
		||||
            and lost this block.
 | 
			
		||||
 | 
			
		||||
            The data must be an array containing the same number of channels as the
 | 
			
		||||
            AudioFormatWriter object is using. None of these channels can be null.
 | 
			
		||||
        */
 | 
			
		||||
        bool write (const float* const* data, int numSamples);
 | 
			
		||||
 | 
			
		||||
        /** Receiver for incoming data. */
 | 
			
		||||
        class JUCE_API  IncomingDataReceiver
 | 
			
		||||
        {
 | 
			
		||||
        public:
 | 
			
		||||
            IncomingDataReceiver() = default;
 | 
			
		||||
            virtual ~IncomingDataReceiver() = default;
 | 
			
		||||
 | 
			
		||||
            virtual void reset (int numChannels, double sampleRate, int64 totalSamplesInSource) = 0;
 | 
			
		||||
            virtual void addBlock (int64 sampleNumberInSource, const AudioBuffer<float>& newData,
 | 
			
		||||
                                   int startOffsetInBuffer, int numSamples) = 0;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /** Allows you to specify a callback that this writer should update with the
 | 
			
		||||
            incoming data.
 | 
			
		||||
            The receiver will be cleared and the writer will begin adding data to it
 | 
			
		||||
            as the data arrives. Pass a null pointer to remove the current receiver.
 | 
			
		||||
 | 
			
		||||
            The object passed-in must not be deleted while this writer is still using it.
 | 
			
		||||
        */
 | 
			
		||||
        void setDataReceiver (IncomingDataReceiver*);
 | 
			
		||||
 | 
			
		||||
        /** Sets how many samples should be written before calling the AudioFormatWriter::flush method.
 | 
			
		||||
            Set this to 0 to disable flushing (this is the default).
 | 
			
		||||
        */
 | 
			
		||||
        void setFlushInterval (int numSamplesPerFlush) noexcept;
 | 
			
		||||
 | 
			
		||||
        /** return pointer to writer */
 | 
			
		||||
        AudioFormatWriter * getWriter() const;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        class Buffer;
 | 
			
		||||
        std::unique_ptr<Buffer> buffer;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The sample rate of the stream. */
 | 
			
		||||
    double sampleRate;
 | 
			
		||||
 | 
			
		||||
    /** The number of channels being written to the stream. */
 | 
			
		||||
    unsigned int numChannels;
 | 
			
		||||
 | 
			
		||||
    /** The bit depth of the file. */
 | 
			
		||||
    unsigned int bitsPerSample;
 | 
			
		||||
 | 
			
		||||
    /** True if it's a floating-point format, false if it's fixed-point. */
 | 
			
		||||
    bool usesFloatingPointData;
 | 
			
		||||
 | 
			
		||||
    /** The audio channel layout that the writer should use */
 | 
			
		||||
    AudioChannelSet channelLayout;
 | 
			
		||||
 | 
			
		||||
    /** The output stream for use by subclasses. */
 | 
			
		||||
    OutputStream* output;
 | 
			
		||||
 | 
			
		||||
    /** Used by AudioFormatWriter subclasses to copy data to different formats. */
 | 
			
		||||
    template <class DestSampleType, class SourceSampleType, class DestEndianness>
 | 
			
		||||
    struct WriteHelper
 | 
			
		||||
    {
 | 
			
		||||
        using DestType   = AudioData::Pointer <DestSampleType,   DestEndianness,          AudioData::Interleaved,    AudioData::NonConst>;
 | 
			
		||||
        using SourceType = AudioData::Pointer <SourceSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
 | 
			
		||||
 | 
			
		||||
        static void write (void* destData, int numDestChannels, const int* const* source,
 | 
			
		||||
                           int numSamples, const int sourceOffset = 0) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < numDestChannels; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                const DestType dest (addBytesToPointer (destData, i * DestType::getBytesPerSample()), numDestChannels);
 | 
			
		||||
 | 
			
		||||
                if (*source != nullptr)
 | 
			
		||||
                {
 | 
			
		||||
                    dest.convertSamples (SourceType (*source + sourceOffset), numSamples);
 | 
			
		||||
                    ++source;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    dest.clearSamples (numSamples);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    String formatName;
 | 
			
		||||
    friend class ThreadedWriter;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatWriter)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Writes samples to an audio file stream.
 | 
			
		||||
 | 
			
		||||
    A subclass that writes a specific type of audio format will be created by
 | 
			
		||||
    an AudioFormat object.
 | 
			
		||||
 | 
			
		||||
    After creating one of these with the AudioFormat::createWriterFor() method
 | 
			
		||||
    you can call its write() method to store the samples, and then delete it.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormat, AudioFormatReader
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioFormatWriter
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioFormatWriter object.
 | 
			
		||||
 | 
			
		||||
        @param destStream       the stream to write to - this will be deleted
 | 
			
		||||
                                by this object when it is no longer needed
 | 
			
		||||
        @param formatName       the description that will be returned by the getFormatName()
 | 
			
		||||
                                method
 | 
			
		||||
        @param sampleRate       the sample rate to use - the base class just stores
 | 
			
		||||
                                this value, it doesn't do anything with it
 | 
			
		||||
        @param numberOfChannels the number of channels to write - the base class just stores
 | 
			
		||||
                                this value, it doesn't do anything with it
 | 
			
		||||
        @param bitsPerSample    the bit depth of the stream - the base class just stores
 | 
			
		||||
                                this value, it doesn't do anything with it
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatWriter (OutputStream* destStream,
 | 
			
		||||
                       const String& formatName,
 | 
			
		||||
                       double sampleRate,
 | 
			
		||||
                       unsigned int numberOfChannels,
 | 
			
		||||
                       unsigned int bitsPerSample);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioFormatWriter object.
 | 
			
		||||
 | 
			
		||||
        @param destStream            the stream to write to - this will be deleted
 | 
			
		||||
                                     by this object when it is no longer needed
 | 
			
		||||
        @param formatName            the description that will be returned by the getFormatName()
 | 
			
		||||
                                     method
 | 
			
		||||
        @param sampleRate            the sample rate to use - the base class just stores
 | 
			
		||||
                                     this value, it doesn't do anything with it
 | 
			
		||||
        @param audioChannelLayout    the channel layout to use for the writer - the base class
 | 
			
		||||
                                     just stores this value, it doesn't do anything with it
 | 
			
		||||
        @param bitsPerSample         the bit depth of the stream - the base class just stores
 | 
			
		||||
                                     this value, it doesn't do anything with it
 | 
			
		||||
    */
 | 
			
		||||
    AudioFormatWriter (OutputStream* destStream,
 | 
			
		||||
                       const String& formatName,
 | 
			
		||||
                       double sampleRate,
 | 
			
		||||
                       const AudioChannelSet& audioChannelLayout,
 | 
			
		||||
                       unsigned int bitsPerSample);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~AudioFormatWriter();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns a description of what type of format this is.
 | 
			
		||||
 | 
			
		||||
        E.g. "AIFF file"
 | 
			
		||||
    */
 | 
			
		||||
    const String& getFormatName() const noexcept        { return formatName; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Writes a set of samples to the audio stream.
 | 
			
		||||
 | 
			
		||||
        Note that if you're trying to write the contents of an AudioBuffer, you
 | 
			
		||||
        can use writeFromAudioSampleBuffer().
 | 
			
		||||
 | 
			
		||||
        @param samplesToWrite   an array of arrays containing the sample data for
 | 
			
		||||
                                each channel to write. This is a zero-terminated
 | 
			
		||||
                                array of arrays, and can contain a different number
 | 
			
		||||
                                of channels than the actual stream uses, and the
 | 
			
		||||
                                writer should do its best to cope with this.
 | 
			
		||||
                                If the format is fixed-point, each channel will be formatted
 | 
			
		||||
                                as an array of signed integers using the full 32-bit
 | 
			
		||||
                                range -0x80000000 to 0x7fffffff, regardless of the source's
 | 
			
		||||
                                bit-depth. If it is a floating-point format, you should treat
 | 
			
		||||
                                the arrays as arrays of floats, and just cast it to an (int**)
 | 
			
		||||
                                to pass it into the method.
 | 
			
		||||
        @param numSamples       the number of samples to write
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool write (const int** samplesToWrite, int numSamples) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Some formats may support a flush operation that makes sure the file is in a
 | 
			
		||||
        valid state before carrying on.
 | 
			
		||||
        If supported, this means that by calling flush periodically when writing data
 | 
			
		||||
        to a large file, then it should still be left in a readable state if your program
 | 
			
		||||
        crashes.
 | 
			
		||||
        It goes without saying that this method must be called from the same thread that's
 | 
			
		||||
        calling write()!
 | 
			
		||||
        If the format supports flushing and the operation succeeds, this returns true.
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool flush();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Reads a section of samples from an AudioFormatReader, and writes these to
 | 
			
		||||
        the output.
 | 
			
		||||
 | 
			
		||||
        This will take care of any floating-point conversion that's required to convert
 | 
			
		||||
        between the two formats. It won't deal with sample-rate conversion, though.
 | 
			
		||||
 | 
			
		||||
        If numSamplesToRead < 0, it will write the entire length of the reader.
 | 
			
		||||
 | 
			
		||||
        @returns false if it can't read or write properly during the operation
 | 
			
		||||
    */
 | 
			
		||||
    bool writeFromAudioReader (AudioFormatReader& reader,
 | 
			
		||||
                               int64 startSample,
 | 
			
		||||
                               int64 numSamplesToRead);
 | 
			
		||||
 | 
			
		||||
    /** Reads some samples from an AudioSource, and writes these to the output.
 | 
			
		||||
 | 
			
		||||
        The source must already have been initialised with the AudioSource::prepareToPlay() method
 | 
			
		||||
 | 
			
		||||
        @param source               the source to read from
 | 
			
		||||
        @param numSamplesToRead     total number of samples to read and write
 | 
			
		||||
        @param samplesPerBlock      the maximum number of samples to fetch from the source
 | 
			
		||||
        @returns false if it can't read or write properly during the operation
 | 
			
		||||
    */
 | 
			
		||||
    bool writeFromAudioSource (AudioSource& source,
 | 
			
		||||
                               int numSamplesToRead,
 | 
			
		||||
                               int samplesPerBlock = 2048);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /** Writes some samples from an AudioBuffer. */
 | 
			
		||||
    bool writeFromAudioSampleBuffer (const AudioBuffer<float>& source,
 | 
			
		||||
                                     int startSample, int numSamples);
 | 
			
		||||
 | 
			
		||||
    /** Writes some samples from a set of float data channels. */
 | 
			
		||||
    bool writeFromFloatArrays (const float* const* channels, int numChannels, int numSamples);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the sample rate being used. */
 | 
			
		||||
    double getSampleRate() const noexcept       { return sampleRate; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of channels being written. */
 | 
			
		||||
    int getNumChannels() const noexcept         { return (int) numChannels; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the bit-depth of the data being written. */
 | 
			
		||||
    int getBitsPerSample() const noexcept       { return (int) bitsPerSample; }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if it's a floating-point format, false if it's fixed-point. */
 | 
			
		||||
    bool isFloatingPoint() const noexcept       { return usesFloatingPointData; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
        Provides a FIFO for an AudioFormatWriter, allowing you to push incoming
 | 
			
		||||
        data into a buffer which will be flushed to disk by a background thread.
 | 
			
		||||
    */
 | 
			
		||||
    class ThreadedWriter
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        /** Creates a ThreadedWriter for a given writer and a thread.
 | 
			
		||||
 | 
			
		||||
            The writer object which is passed in here will be owned and deleted by
 | 
			
		||||
            the ThreadedWriter when it is no longer needed.
 | 
			
		||||
 | 
			
		||||
            To stop the writer and flush the buffer to disk, simply delete this object.
 | 
			
		||||
        */
 | 
			
		||||
        ThreadedWriter (AudioFormatWriter* writer,
 | 
			
		||||
                        TimeSliceThread& backgroundThread,
 | 
			
		||||
                        int numSamplesToBuffer);
 | 
			
		||||
 | 
			
		||||
        /** Destructor. */
 | 
			
		||||
        ~ThreadedWriter();
 | 
			
		||||
 | 
			
		||||
        /** Pushes some incoming audio data into the FIFO.
 | 
			
		||||
 | 
			
		||||
            If there's enough free space in the buffer, this will add the data to it,
 | 
			
		||||
 | 
			
		||||
            If the FIFO is too full to accept this many samples, the method will return
 | 
			
		||||
            false - then you could either wait until the background thread has had time to
 | 
			
		||||
            consume some of the buffered data and try again, or you can give up
 | 
			
		||||
            and lost this block.
 | 
			
		||||
 | 
			
		||||
            The data must be an array containing the same number of channels as the
 | 
			
		||||
            AudioFormatWriter object is using. None of these channels can be null.
 | 
			
		||||
        */
 | 
			
		||||
        bool write (const float* const* data, int numSamples);
 | 
			
		||||
 | 
			
		||||
        /** Receiver for incoming data. */
 | 
			
		||||
        class JUCE_API  IncomingDataReceiver
 | 
			
		||||
        {
 | 
			
		||||
        public:
 | 
			
		||||
            IncomingDataReceiver() = default;
 | 
			
		||||
            virtual ~IncomingDataReceiver() = default;
 | 
			
		||||
 | 
			
		||||
            virtual void reset (int numChannels, double sampleRate, int64 totalSamplesInSource) = 0;
 | 
			
		||||
            virtual void addBlock (int64 sampleNumberInSource, const AudioBuffer<float>& newData,
 | 
			
		||||
                                   int startOffsetInBuffer, int numSamples) = 0;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /** Allows you to specify a callback that this writer should update with the
 | 
			
		||||
            incoming data.
 | 
			
		||||
            The receiver will be cleared and the writer will begin adding data to it
 | 
			
		||||
            as the data arrives. Pass a null pointer to remove the current receiver.
 | 
			
		||||
 | 
			
		||||
            The object passed-in must not be deleted while this writer is still using it.
 | 
			
		||||
        */
 | 
			
		||||
        void setDataReceiver (IncomingDataReceiver*);
 | 
			
		||||
 | 
			
		||||
        /** Sets how many samples should be written before calling the AudioFormatWriter::flush method.
 | 
			
		||||
            Set this to 0 to disable flushing (this is the default).
 | 
			
		||||
        */
 | 
			
		||||
        void setFlushInterval (int numSamplesPerFlush) noexcept;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        class Buffer;
 | 
			
		||||
        std::unique_ptr<Buffer> buffer;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The sample rate of the stream. */
 | 
			
		||||
    double sampleRate;
 | 
			
		||||
 | 
			
		||||
    /** The number of channels being written to the stream. */
 | 
			
		||||
    unsigned int numChannels;
 | 
			
		||||
 | 
			
		||||
    /** The bit depth of the file. */
 | 
			
		||||
    unsigned int bitsPerSample;
 | 
			
		||||
 | 
			
		||||
    /** True if it's a floating-point format, false if it's fixed-point. */
 | 
			
		||||
    bool usesFloatingPointData;
 | 
			
		||||
 | 
			
		||||
    /** The audio channel layout that the writer should use */
 | 
			
		||||
    AudioChannelSet channelLayout;
 | 
			
		||||
 | 
			
		||||
    /** The output stream for use by subclasses. */
 | 
			
		||||
    OutputStream* output;
 | 
			
		||||
 | 
			
		||||
    /** Used by AudioFormatWriter subclasses to copy data to different formats. */
 | 
			
		||||
    template <class DestSampleType, class SourceSampleType, class DestEndianness>
 | 
			
		||||
    struct WriteHelper
 | 
			
		||||
    {
 | 
			
		||||
        using DestType   = AudioData::Pointer <DestSampleType,   DestEndianness,          AudioData::Interleaved,    AudioData::NonConst>;
 | 
			
		||||
        using SourceType = AudioData::Pointer <SourceSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
 | 
			
		||||
 | 
			
		||||
        static void write (void* destData, int numDestChannels, const int* const* source,
 | 
			
		||||
                           int numSamples, const int sourceOffset = 0) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < numDestChannels; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                const DestType dest (addBytesToPointer (destData, i * DestType::getBytesPerSample()), numDestChannels);
 | 
			
		||||
 | 
			
		||||
                if (*source != nullptr)
 | 
			
		||||
                {
 | 
			
		||||
                    dest.convertSamples (SourceType (*source + sourceOffset), numSamples);
 | 
			
		||||
                    ++source;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    dest.clearSamples (numSamples);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    String formatName;
 | 
			
		||||
    friend class ThreadedWriter;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatWriter)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,71 +1,71 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioSubsectionReader::AudioSubsectionReader (AudioFormatReader* sourceToUse,
 | 
			
		||||
                                              int64 startSampleToUse, int64 lengthToUse,
 | 
			
		||||
                                              bool deleteSource)
 | 
			
		||||
   : AudioFormatReader (nullptr, sourceToUse->getFormatName()),
 | 
			
		||||
     source (sourceToUse),
 | 
			
		||||
     startSample (startSampleToUse),
 | 
			
		||||
     deleteSourceWhenDeleted (deleteSource)
 | 
			
		||||
{
 | 
			
		||||
    length = jmin (jmax ((int64) 0, source->lengthInSamples - startSample), lengthToUse);
 | 
			
		||||
 | 
			
		||||
    sampleRate = source->sampleRate;
 | 
			
		||||
    bitsPerSample = source->bitsPerSample;
 | 
			
		||||
    lengthInSamples = length;
 | 
			
		||||
    numChannels = source->numChannels;
 | 
			
		||||
    usesFloatingPointData = source->usesFloatingPointData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioSubsectionReader::~AudioSubsectionReader()
 | 
			
		||||
{
 | 
			
		||||
    if (deleteSourceWhenDeleted)
 | 
			
		||||
        delete source;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                                         int64 startSampleInFile, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
 | 
			
		||||
                                       startSampleInFile, numSamples, length);
 | 
			
		||||
 | 
			
		||||
    return source->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer,
 | 
			
		||||
                                startSampleInFile + startSample, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioSubsectionReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, Range<float>* results, int numChannelsToRead)
 | 
			
		||||
{
 | 
			
		||||
    startSampleInFile = jmax ((int64) 0, startSampleInFile);
 | 
			
		||||
    numSamples = jmax ((int64) 0, jmin (numSamples, length - startSampleInFile));
 | 
			
		||||
 | 
			
		||||
    source->readMaxLevels (startSampleInFile + startSample, numSamples, results, numChannelsToRead);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AudioSubsectionReader::AudioSubsectionReader (AudioFormatReader* sourceToUse,
 | 
			
		||||
                                              int64 startSampleToUse, int64 lengthToUse,
 | 
			
		||||
                                              bool deleteSource)
 | 
			
		||||
   : AudioFormatReader (nullptr, sourceToUse->getFormatName()),
 | 
			
		||||
     source (sourceToUse),
 | 
			
		||||
     startSample (startSampleToUse),
 | 
			
		||||
     deleteSourceWhenDeleted (deleteSource)
 | 
			
		||||
{
 | 
			
		||||
    length = jmin (jmax ((int64) 0, source->lengthInSamples - startSample), lengthToUse);
 | 
			
		||||
 | 
			
		||||
    sampleRate = source->sampleRate;
 | 
			
		||||
    bitsPerSample = source->bitsPerSample;
 | 
			
		||||
    lengthInSamples = length;
 | 
			
		||||
    numChannels = source->numChannels;
 | 
			
		||||
    usesFloatingPointData = source->usesFloatingPointData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioSubsectionReader::~AudioSubsectionReader()
 | 
			
		||||
{
 | 
			
		||||
    if (deleteSourceWhenDeleted)
 | 
			
		||||
        delete source;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                                         int64 startSampleInFile, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
 | 
			
		||||
                                       startSampleInFile, numSamples, length);
 | 
			
		||||
 | 
			
		||||
    return source->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer,
 | 
			
		||||
                                startSampleInFile + startSample, numSamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioSubsectionReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, Range<float>* results, int numChannelsToRead)
 | 
			
		||||
{
 | 
			
		||||
    startSampleInFile = jmax ((int64) 0, startSampleInFile);
 | 
			
		||||
    numSamples = jmax ((int64) 0, jmin (numSamples, length - startSampleInFile));
 | 
			
		||||
 | 
			
		||||
    source->readMaxLevels (startSampleInFile + startSample, numSamples, results, numChannelsToRead);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,86 +1,86 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    This class is used to wrap an AudioFormatReader and only read from a
 | 
			
		||||
    subsection of the file.
 | 
			
		||||
 | 
			
		||||
    So if you have a reader which can read a 1000 sample file, you could wrap it
 | 
			
		||||
    in one of these to only access, e.g. samples 100 to 200, and any samples
 | 
			
		||||
    outside that will come back as 0. Accessing sample 0 from this reader will
 | 
			
		||||
    actually read the first sample from the other's subsection, which might
 | 
			
		||||
    be at a non-zero position.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormatReader
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioSubsectionReader  : public AudioFormatReader
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioSubsectionReader for a given data source.
 | 
			
		||||
 | 
			
		||||
        @param sourceReader             the source reader from which we'll be taking data
 | 
			
		||||
        @param subsectionStartSample    the sample within the source reader which will be
 | 
			
		||||
                                        mapped onto sample 0 for this reader.
 | 
			
		||||
        @param subsectionLength         the number of samples from the source that will
 | 
			
		||||
                                        make up the subsection. If this reader is asked for
 | 
			
		||||
                                        any samples beyond this region, it will return zero.
 | 
			
		||||
        @param deleteSourceWhenDeleted  if true, the sourceReader object will be deleted when
 | 
			
		||||
                                        this object is deleted.
 | 
			
		||||
    */
 | 
			
		||||
    AudioSubsectionReader (AudioFormatReader* sourceReader,
 | 
			
		||||
                           int64 subsectionStartSample,
 | 
			
		||||
                           int64 subsectionLength,
 | 
			
		||||
                           bool deleteSourceWhenDeleted);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~AudioSubsectionReader() override;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                      int64 startSampleInFile, int numSamples) override;
 | 
			
		||||
 | 
			
		||||
    void readMaxLevels (int64 startSample, int64 numSamples,
 | 
			
		||||
                        Range<float>* results, int numChannelsToRead) override;
 | 
			
		||||
 | 
			
		||||
    using AudioFormatReader::readMaxLevels;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioFormatReader* const source;
 | 
			
		||||
    int64 startSample, length;
 | 
			
		||||
    const bool deleteSourceWhenDeleted;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSubsectionReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    This class is used to wrap an AudioFormatReader and only read from a
 | 
			
		||||
    subsection of the file.
 | 
			
		||||
 | 
			
		||||
    So if you have a reader which can read a 1000 sample file, you could wrap it
 | 
			
		||||
    in one of these to only access, e.g. samples 100 to 200, and any samples
 | 
			
		||||
    outside that will come back as 0. Accessing sample 0 from this reader will
 | 
			
		||||
    actually read the first sample from the other's subsection, which might
 | 
			
		||||
    be at a non-zero position.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormatReader
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  AudioSubsectionReader  : public AudioFormatReader
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an AudioSubsectionReader for a given data source.
 | 
			
		||||
 | 
			
		||||
        @param sourceReader             the source reader from which we'll be taking data
 | 
			
		||||
        @param subsectionStartSample    the sample within the source reader which will be
 | 
			
		||||
                                        mapped onto sample 0 for this reader.
 | 
			
		||||
        @param subsectionLength         the number of samples from the source that will
 | 
			
		||||
                                        make up the subsection. If this reader is asked for
 | 
			
		||||
                                        any samples beyond this region, it will return zero.
 | 
			
		||||
        @param deleteSourceWhenDeleted  if true, the sourceReader object will be deleted when
 | 
			
		||||
                                        this object is deleted.
 | 
			
		||||
    */
 | 
			
		||||
    AudioSubsectionReader (AudioFormatReader* sourceReader,
 | 
			
		||||
                           int64 subsectionStartSample,
 | 
			
		||||
                           int64 subsectionLength,
 | 
			
		||||
                           bool deleteSourceWhenDeleted);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~AudioSubsectionReader() override;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                      int64 startSampleInFile, int numSamples) override;
 | 
			
		||||
 | 
			
		||||
    void readMaxLevels (int64 startSample, int64 numSamples,
 | 
			
		||||
                        Range<float>* results, int numChannelsToRead) override;
 | 
			
		||||
 | 
			
		||||
    using AudioFormatReader::readMaxLevels;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioFormatReader* const source;
 | 
			
		||||
    int64 startSample, length;
 | 
			
		||||
    const bool deleteSourceWhenDeleted;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSubsectionReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,339 +1,346 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
BufferingAudioReader::BufferingAudioReader (AudioFormatReader* sourceReader,
 | 
			
		||||
                                            TimeSliceThread& timeSliceThread,
 | 
			
		||||
                                            int samplesToBuffer)
 | 
			
		||||
    : AudioFormatReader (nullptr, sourceReader->getFormatName()),
 | 
			
		||||
      source (sourceReader), thread (timeSliceThread),
 | 
			
		||||
      numBlocks (1 + (samplesToBuffer / samplesPerBlock))
 | 
			
		||||
{
 | 
			
		||||
    sampleRate            = source->sampleRate;
 | 
			
		||||
    lengthInSamples       = source->lengthInSamples;
 | 
			
		||||
    numChannels           = source->numChannels;
 | 
			
		||||
    metadataValues        = source->metadataValues;
 | 
			
		||||
    bitsPerSample         = 32;
 | 
			
		||||
    usesFloatingPointData = true;
 | 
			
		||||
 | 
			
		||||
    timeSliceThread.addTimeSliceClient (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BufferingAudioReader::~BufferingAudioReader()
 | 
			
		||||
{
 | 
			
		||||
    thread.removeTimeSliceClient (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BufferingAudioReader::setReadTimeout (int timeoutMilliseconds) noexcept
 | 
			
		||||
{
 | 
			
		||||
    timeoutMs = timeoutMilliseconds;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                                        int64 startSampleInFile, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    auto startTime = Time::getMillisecondCounter();
 | 
			
		||||
    clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
 | 
			
		||||
                                       startSampleInFile, numSamples, lengthInSamples);
 | 
			
		||||
 | 
			
		||||
    const ScopedLock sl (lock);
 | 
			
		||||
    nextReadPosition = startSampleInFile;
 | 
			
		||||
 | 
			
		||||
    while (numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto block = getBlockContaining (startSampleInFile))
 | 
			
		||||
        {
 | 
			
		||||
            auto offset = (int) (startSampleInFile - block->range.getStart());
 | 
			
		||||
            auto numToDo = jmin (numSamples, (int) (block->range.getEnd() - startSampleInFile));
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < numDestChannels; ++j)
 | 
			
		||||
            {
 | 
			
		||||
                if (auto* dest = (float*) destSamples[j])
 | 
			
		||||
                {
 | 
			
		||||
                    dest += startOffsetInDestBuffer;
 | 
			
		||||
 | 
			
		||||
                    if (j < (int) numChannels)
 | 
			
		||||
                        FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo);
 | 
			
		||||
                    else
 | 
			
		||||
                        FloatVectorOperations::clear (dest, numToDo);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            startOffsetInDestBuffer += numToDo;
 | 
			
		||||
            startSampleInFile += numToDo;
 | 
			
		||||
            numSamples -= numToDo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (timeoutMs >= 0 && Time::getMillisecondCounter() >= startTime + (uint32) timeoutMs)
 | 
			
		||||
            {
 | 
			
		||||
                for (int j = 0; j < numDestChannels; ++j)
 | 
			
		||||
                    if (auto* dest = (float*) destSamples[j])
 | 
			
		||||
                        FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples);
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                ScopedUnlock ul (lock);
 | 
			
		||||
                Thread::yield();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BufferingAudioReader::BufferedBlock::BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples)
 | 
			
		||||
    : range (pos, pos + numSamples),
 | 
			
		||||
      buffer ((int) reader.numChannels, numSamples)
 | 
			
		||||
{
 | 
			
		||||
    reader.read (&buffer, 0, numSamples, pos, true, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BufferingAudioReader::BufferedBlock* BufferingAudioReader::getBlockContaining (int64 pos) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (auto* b : blocks)
 | 
			
		||||
        if (b->range.contains (pos))
 | 
			
		||||
            return b;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BufferingAudioReader::useTimeSlice()
 | 
			
		||||
{
 | 
			
		||||
    return readNextBufferChunk() ? 1 : 100;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BufferingAudioReader::readNextBufferChunk()
 | 
			
		||||
{
 | 
			
		||||
    auto pos = (nextReadPosition.load() / samplesPerBlock) * samplesPerBlock;
 | 
			
		||||
    auto endPos = jmin (lengthInSamples, pos + numBlocks * samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
    OwnedArray<BufferedBlock> newBlocks;
 | 
			
		||||
 | 
			
		||||
    for (int i = blocks.size(); --i >= 0;)
 | 
			
		||||
        if (blocks.getUnchecked (i)->range.intersects (Range<int64> (pos, endPos)))
 | 
			
		||||
            newBlocks.add (blocks.getUnchecked (i));
 | 
			
		||||
 | 
			
		||||
    if (newBlocks.size() == numBlocks)
 | 
			
		||||
    {
 | 
			
		||||
        newBlocks.clear (false);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto p = pos; p < endPos; p += samplesPerBlock)
 | 
			
		||||
    {
 | 
			
		||||
        if (getBlockContaining (p) == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            newBlocks.add (new BufferedBlock (*source, p, samplesPerBlock));
 | 
			
		||||
            break; // just do one block
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (lock);
 | 
			
		||||
        newBlocks.swapWith (blocks);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (int i = blocks.size(); --i >= 0;)
 | 
			
		||||
        newBlocks.removeObject (blocks.getUnchecked (i), false);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
//==============================================================================
 | 
			
		||||
#if JUCE_UNIT_TESTS
 | 
			
		||||
 | 
			
		||||
static bool operator== (const AudioBuffer<float>& a, const AudioBuffer<float>& b)
 | 
			
		||||
{
 | 
			
		||||
    if (a.getNumChannels() != b.getNumChannels() || a.getNumSamples() != b.getNumSamples())
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    for (int channel = 0; channel < a.getNumChannels(); ++channel)
 | 
			
		||||
    {
 | 
			
		||||
        auto* aPtr = a.getReadPointer (channel);
 | 
			
		||||
        auto* bPtr = b.getReadPointer (channel);
 | 
			
		||||
 | 
			
		||||
        if (std::vector<float> (aPtr, aPtr + a.getNumSamples())
 | 
			
		||||
            != std::vector<float> (bPtr, bPtr + b.getNumSamples()))
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool isSilent (const AudioBuffer<float>& b)
 | 
			
		||||
{
 | 
			
		||||
    for (int channel = 0; channel < b.getNumChannels(); ++channel)
 | 
			
		||||
        if (b.findMinMax (channel, 0, b.getNumSamples()) != Range<float>{})
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct TestAudioFormatReader  : public AudioFormatReader
 | 
			
		||||
{
 | 
			
		||||
    explicit TestAudioFormatReader (AudioBuffer<float>& b)
 | 
			
		||||
        : AudioFormatReader (nullptr, {}),
 | 
			
		||||
          buffer (b)
 | 
			
		||||
    {
 | 
			
		||||
        sampleRate            = 44100.0f;
 | 
			
		||||
        bitsPerSample         = 32;
 | 
			
		||||
        usesFloatingPointData = true;
 | 
			
		||||
        lengthInSamples       = buffer.getNumSamples();
 | 
			
		||||
        numChannels           = (unsigned int) buffer.getNumChannels();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool readSamples (int** destChannels, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                      int64 startSampleInFile, int numSamples) override
 | 
			
		||||
    {
 | 
			
		||||
        clearSamplesBeyondAvailableLength (destChannels, numDestChannels, startOffsetInDestBuffer,
 | 
			
		||||
                                           startSampleInFile, numSamples, lengthInSamples);
 | 
			
		||||
 | 
			
		||||
        for (int j = 0; j < numDestChannels; ++j)
 | 
			
		||||
        {
 | 
			
		||||
            static_assert (sizeof (int) == sizeof (float),
 | 
			
		||||
                           "Int and float size must match in order for pointer arithmetic to work correctly");
 | 
			
		||||
 | 
			
		||||
            if (auto* dest = reinterpret_cast<float*> (destChannels[j]))
 | 
			
		||||
            {
 | 
			
		||||
                dest += startOffsetInDestBuffer;
 | 
			
		||||
 | 
			
		||||
                if (j < (int) numChannels)
 | 
			
		||||
                    FloatVectorOperations::copy (dest, buffer.getReadPointer (j, (int) startSampleInFile), numSamples);
 | 
			
		||||
                else
 | 
			
		||||
                    FloatVectorOperations::clear (dest, numSamples);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const AudioBuffer<float>& buffer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BufferingAudioReaderTests  : public UnitTest
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    BufferingAudioReaderTests()  : UnitTest ("BufferingAudioReader", UnitTestCategories::audio)  {}
 | 
			
		||||
 | 
			
		||||
    void runTest() override
 | 
			
		||||
    {
 | 
			
		||||
        TimeSliceThread timeSlice ("TestBackgroundThread");
 | 
			
		||||
        timeSlice.startThread (5);
 | 
			
		||||
 | 
			
		||||
        beginTest ("Timeout");
 | 
			
		||||
        {
 | 
			
		||||
            struct BlockingReader  : public AudioFormatReader
 | 
			
		||||
            {
 | 
			
		||||
                BlockingReader()
 | 
			
		||||
                    : AudioFormatReader (nullptr, {})
 | 
			
		||||
                {
 | 
			
		||||
                    sampleRate            = 44100.0f;
 | 
			
		||||
                    bitsPerSample         = 32;
 | 
			
		||||
                    usesFloatingPointData = true;
 | 
			
		||||
                    lengthInSamples       = 1024;
 | 
			
		||||
                    numChannels           = 2;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                bool readSamples (int**, int, int, int64, int) override
 | 
			
		||||
                {
 | 
			
		||||
                    Thread::sleep (100);
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            BufferingAudioReader bufferingReader (new BlockingReader(), timeSlice, 64);
 | 
			
		||||
            bufferingReader.setReadTimeout (10);
 | 
			
		||||
 | 
			
		||||
            AudioBuffer<float> readBuffer { 2, 1024 };
 | 
			
		||||
            read (bufferingReader, readBuffer);
 | 
			
		||||
 | 
			
		||||
            expect (isSilent (readBuffer));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        beginTest ("Read samples");
 | 
			
		||||
        {
 | 
			
		||||
            for (auto i = 4; i < 18; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                const auto backgroundBufferSize = 1 << i;
 | 
			
		||||
                auto buffer = generateTestBuffer (backgroundBufferSize);
 | 
			
		||||
 | 
			
		||||
                BufferingAudioReader bufferingReader (new TestAudioFormatReader (buffer), timeSlice, backgroundBufferSize);
 | 
			
		||||
                bufferingReader.setReadTimeout (-1);
 | 
			
		||||
 | 
			
		||||
                AudioBuffer<float> readBuffer { buffer.getNumChannels(), buffer.getNumSamples() };
 | 
			
		||||
                read (bufferingReader, readBuffer);
 | 
			
		||||
 | 
			
		||||
                expect (buffer == readBuffer);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    AudioBuffer<float> generateTestBuffer (int bufferSize) const
 | 
			
		||||
    {
 | 
			
		||||
        auto random = getRandom();
 | 
			
		||||
 | 
			
		||||
        AudioBuffer<float> buffer { 2, random.nextInt ({ bufferSize, bufferSize * 10 }) };
 | 
			
		||||
 | 
			
		||||
        for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
 | 
			
		||||
            for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
 | 
			
		||||
                buffer.setSample (channel, sample, random.nextFloat());
 | 
			
		||||
 | 
			
		||||
        return buffer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void read (BufferingAudioReader& reader, AudioBuffer<float>& readBuffer)
 | 
			
		||||
    {
 | 
			
		||||
        constexpr int blockSize = 1024;
 | 
			
		||||
 | 
			
		||||
        const auto numSamples = readBuffer.getNumSamples();
 | 
			
		||||
        int readPos = 0;
 | 
			
		||||
 | 
			
		||||
        for (;;)
 | 
			
		||||
        {
 | 
			
		||||
            reader.read (&readBuffer, readPos, jmin (blockSize, numSamples - readPos), readPos, true, true);
 | 
			
		||||
 | 
			
		||||
            readPos += blockSize;
 | 
			
		||||
 | 
			
		||||
            if (readPos >= numSamples)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BufferingAudioReaderTests bufferingAudioReaderTests;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
BufferingAudioReader::BufferingAudioReader (AudioFormatReader* sourceReader,
 | 
			
		||||
                                            TimeSliceThread& timeSliceThread,
 | 
			
		||||
                                            int samplesToBuffer)
 | 
			
		||||
    : AudioFormatReader (nullptr, sourceReader->getFormatName()),
 | 
			
		||||
      source (sourceReader), thread (timeSliceThread),
 | 
			
		||||
      numBlocks (1 + (samplesToBuffer / samplesPerBlock))
 | 
			
		||||
{
 | 
			
		||||
    sampleRate            = source->sampleRate;
 | 
			
		||||
    lengthInSamples       = source->lengthInSamples;
 | 
			
		||||
    numChannels           = source->numChannels;
 | 
			
		||||
    metadataValues        = source->metadataValues;
 | 
			
		||||
    bitsPerSample         = 32;
 | 
			
		||||
    usesFloatingPointData = true;
 | 
			
		||||
 | 
			
		||||
    timeSliceThread.addTimeSliceClient (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BufferingAudioReader::~BufferingAudioReader()
 | 
			
		||||
{
 | 
			
		||||
    thread.removeTimeSliceClient (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BufferingAudioReader::setReadTimeout (int timeoutMilliseconds) noexcept
 | 
			
		||||
{
 | 
			
		||||
    timeoutMs = timeoutMilliseconds;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                                        int64 startSampleInFile, int numSamples)
 | 
			
		||||
{
 | 
			
		||||
    auto startTime = Time::getMillisecondCounter();
 | 
			
		||||
    clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
 | 
			
		||||
                                       startSampleInFile, numSamples, lengthInSamples);
 | 
			
		||||
 | 
			
		||||
    const ScopedLock sl (lock);
 | 
			
		||||
    nextReadPosition = startSampleInFile;
 | 
			
		||||
 | 
			
		||||
    bool allSamplesRead = true;
 | 
			
		||||
 | 
			
		||||
    while (numSamples > 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto block = getBlockContaining (startSampleInFile))
 | 
			
		||||
        {
 | 
			
		||||
            auto offset = (int) (startSampleInFile - block->range.getStart());
 | 
			
		||||
            auto numToDo = jmin (numSamples, (int) (block->range.getEnd() - startSampleInFile));
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < numDestChannels; ++j)
 | 
			
		||||
            {
 | 
			
		||||
                if (auto* dest = (float*) destSamples[j])
 | 
			
		||||
                {
 | 
			
		||||
                    dest += startOffsetInDestBuffer;
 | 
			
		||||
 | 
			
		||||
                    if (j < (int) numChannels)
 | 
			
		||||
                        FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo);
 | 
			
		||||
                    else
 | 
			
		||||
                        FloatVectorOperations::clear (dest, numToDo);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            startOffsetInDestBuffer += numToDo;
 | 
			
		||||
            startSampleInFile += numToDo;
 | 
			
		||||
            numSamples -= numToDo;
 | 
			
		||||
 | 
			
		||||
            allSamplesRead = allSamplesRead && block->allSamplesRead;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (timeoutMs >= 0 && Time::getMillisecondCounter() >= startTime + (uint32) timeoutMs)
 | 
			
		||||
            {
 | 
			
		||||
                for (int j = 0; j < numDestChannels; ++j)
 | 
			
		||||
                    if (auto* dest = (float*) destSamples[j])
 | 
			
		||||
                        FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples);
 | 
			
		||||
 | 
			
		||||
                allSamplesRead = false;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                ScopedUnlock ul (lock);
 | 
			
		||||
                Thread::yield();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return allSamplesRead;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BufferingAudioReader::BufferedBlock::BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples)
 | 
			
		||||
    : range (pos, pos + numSamples),
 | 
			
		||||
      buffer ((int) reader.numChannels, numSamples),
 | 
			
		||||
      allSamplesRead (reader.read (&buffer, 0, numSamples, pos, true, true))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BufferingAudioReader::BufferedBlock* BufferingAudioReader::getBlockContaining (int64 pos) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (auto* b : blocks)
 | 
			
		||||
        if (b->range.contains (pos))
 | 
			
		||||
            return b;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BufferingAudioReader::useTimeSlice()
 | 
			
		||||
{
 | 
			
		||||
    return readNextBufferChunk() ? 1 : 100;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BufferingAudioReader::readNextBufferChunk()
 | 
			
		||||
{
 | 
			
		||||
    auto pos = (nextReadPosition.load() / samplesPerBlock) * samplesPerBlock;
 | 
			
		||||
    auto endPos = jmin (lengthInSamples, pos + numBlocks * samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
    OwnedArray<BufferedBlock> newBlocks;
 | 
			
		||||
 | 
			
		||||
    for (int i = blocks.size(); --i >= 0;)
 | 
			
		||||
        if (blocks.getUnchecked (i)->range.intersects (Range<int64> (pos, endPos)))
 | 
			
		||||
            newBlocks.add (blocks.getUnchecked (i));
 | 
			
		||||
 | 
			
		||||
    if (newBlocks.size() == numBlocks)
 | 
			
		||||
    {
 | 
			
		||||
        newBlocks.clear (false);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto p = pos; p < endPos; p += samplesPerBlock)
 | 
			
		||||
    {
 | 
			
		||||
        if (getBlockContaining (p) == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            newBlocks.add (new BufferedBlock (*source, p, samplesPerBlock));
 | 
			
		||||
            break; // just do one block
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (lock);
 | 
			
		||||
        newBlocks.swapWith (blocks);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (int i = blocks.size(); --i >= 0;)
 | 
			
		||||
        newBlocks.removeObject (blocks.getUnchecked (i), false);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
//==============================================================================
 | 
			
		||||
#if JUCE_UNIT_TESTS
 | 
			
		||||
 | 
			
		||||
static bool operator== (const AudioBuffer<float>& a, const AudioBuffer<float>& b)
 | 
			
		||||
{
 | 
			
		||||
    if (a.getNumChannels() != b.getNumChannels() || a.getNumSamples() != b.getNumSamples())
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    for (int channel = 0; channel < a.getNumChannels(); ++channel)
 | 
			
		||||
    {
 | 
			
		||||
        auto* aPtr = a.getReadPointer (channel);
 | 
			
		||||
        auto* bPtr = b.getReadPointer (channel);
 | 
			
		||||
 | 
			
		||||
        if (std::vector<float> (aPtr, aPtr + a.getNumSamples())
 | 
			
		||||
            != std::vector<float> (bPtr, bPtr + b.getNumSamples()))
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool isSilent (const AudioBuffer<float>& b)
 | 
			
		||||
{
 | 
			
		||||
    for (int channel = 0; channel < b.getNumChannels(); ++channel)
 | 
			
		||||
        if (b.findMinMax (channel, 0, b.getNumSamples()) != Range<float>{})
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct TestAudioFormatReader  : public AudioFormatReader
 | 
			
		||||
{
 | 
			
		||||
    explicit TestAudioFormatReader (AudioBuffer<float>& b)
 | 
			
		||||
        : AudioFormatReader (nullptr, {}),
 | 
			
		||||
          buffer (b)
 | 
			
		||||
    {
 | 
			
		||||
        sampleRate            = 44100.0f;
 | 
			
		||||
        bitsPerSample         = 32;
 | 
			
		||||
        usesFloatingPointData = true;
 | 
			
		||||
        lengthInSamples       = buffer.getNumSamples();
 | 
			
		||||
        numChannels           = (unsigned int) buffer.getNumChannels();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool readSamples (int** destChannels, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                      int64 startSampleInFile, int numSamples) override
 | 
			
		||||
    {
 | 
			
		||||
        clearSamplesBeyondAvailableLength (destChannels, numDestChannels, startOffsetInDestBuffer,
 | 
			
		||||
                                           startSampleInFile, numSamples, lengthInSamples);
 | 
			
		||||
 | 
			
		||||
        for (int j = 0; j < numDestChannels; ++j)
 | 
			
		||||
        {
 | 
			
		||||
            static_assert (sizeof (int) == sizeof (float),
 | 
			
		||||
                           "Int and float size must match in order for pointer arithmetic to work correctly");
 | 
			
		||||
 | 
			
		||||
            if (auto* dest = reinterpret_cast<float*> (destChannels[j]))
 | 
			
		||||
            {
 | 
			
		||||
                dest += startOffsetInDestBuffer;
 | 
			
		||||
 | 
			
		||||
                if (j < (int) numChannels)
 | 
			
		||||
                    FloatVectorOperations::copy (dest, buffer.getReadPointer (j, (int) startSampleInFile), numSamples);
 | 
			
		||||
                else
 | 
			
		||||
                    FloatVectorOperations::clear (dest, numSamples);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const AudioBuffer<float>& buffer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BufferingAudioReaderTests  : public UnitTest
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    BufferingAudioReaderTests()  : UnitTest ("BufferingAudioReader", UnitTestCategories::audio)  {}
 | 
			
		||||
 | 
			
		||||
    void runTest() override
 | 
			
		||||
    {
 | 
			
		||||
        TimeSliceThread timeSlice ("TestBackgroundThread");
 | 
			
		||||
        timeSlice.startThread (5);
 | 
			
		||||
 | 
			
		||||
        beginTest ("Timeout");
 | 
			
		||||
        {
 | 
			
		||||
            struct BlockingReader  : public AudioFormatReader
 | 
			
		||||
            {
 | 
			
		||||
                BlockingReader()
 | 
			
		||||
                    : AudioFormatReader (nullptr, {})
 | 
			
		||||
                {
 | 
			
		||||
                    sampleRate            = 44100.0f;
 | 
			
		||||
                    bitsPerSample         = 32;
 | 
			
		||||
                    usesFloatingPointData = true;
 | 
			
		||||
                    lengthInSamples       = 1024;
 | 
			
		||||
                    numChannels           = 2;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                bool readSamples (int**, int, int, int64, int) override
 | 
			
		||||
                {
 | 
			
		||||
                    Thread::sleep (100);
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            BufferingAudioReader bufferingReader (new BlockingReader(), timeSlice, 64);
 | 
			
		||||
            bufferingReader.setReadTimeout (10);
 | 
			
		||||
 | 
			
		||||
            AudioBuffer<float> readBuffer { 2, 1024 };
 | 
			
		||||
 | 
			
		||||
            readBuffer.clear();
 | 
			
		||||
            read (bufferingReader, readBuffer);
 | 
			
		||||
 | 
			
		||||
            expect (isSilent (readBuffer));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        beginTest ("Read samples");
 | 
			
		||||
        {
 | 
			
		||||
            for (auto i = 4; i < 18; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                const auto backgroundBufferSize = 1 << i;
 | 
			
		||||
                auto buffer = generateTestBuffer (backgroundBufferSize);
 | 
			
		||||
 | 
			
		||||
                BufferingAudioReader bufferingReader (new TestAudioFormatReader (buffer), timeSlice, backgroundBufferSize);
 | 
			
		||||
                bufferingReader.setReadTimeout (-1);
 | 
			
		||||
 | 
			
		||||
                AudioBuffer<float> readBuffer { buffer.getNumChannels(), buffer.getNumSamples() };
 | 
			
		||||
                read (bufferingReader, readBuffer);
 | 
			
		||||
 | 
			
		||||
                expect (buffer == readBuffer);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    AudioBuffer<float> generateTestBuffer (int bufferSize) const
 | 
			
		||||
    {
 | 
			
		||||
        auto random = getRandom();
 | 
			
		||||
 | 
			
		||||
        AudioBuffer<float> buffer { 2, random.nextInt ({ bufferSize, bufferSize * 10 }) };
 | 
			
		||||
 | 
			
		||||
        for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
 | 
			
		||||
            for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
 | 
			
		||||
                buffer.setSample (channel, sample, random.nextFloat());
 | 
			
		||||
 | 
			
		||||
        return buffer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void read (BufferingAudioReader& reader, AudioBuffer<float>& readBuffer)
 | 
			
		||||
    {
 | 
			
		||||
        constexpr int blockSize = 1024;
 | 
			
		||||
 | 
			
		||||
        const auto numSamples = readBuffer.getNumSamples();
 | 
			
		||||
        int readPos = 0;
 | 
			
		||||
 | 
			
		||||
        for (;;)
 | 
			
		||||
        {
 | 
			
		||||
            reader.read (&readBuffer, readPos, jmin (blockSize, numSamples - readPos), readPos, true, true);
 | 
			
		||||
 | 
			
		||||
            readPos += blockSize;
 | 
			
		||||
 | 
			
		||||
            if (readPos >= numSamples)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BufferingAudioReaderTests bufferingAudioReaderTests;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,96 +1,97 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    An AudioFormatReader that uses a background thread to pre-read data from
 | 
			
		||||
    another reader.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormatReader
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  BufferingAudioReader  : public AudioFormatReader,
 | 
			
		||||
                                        private TimeSliceClient
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Creates a reader.
 | 
			
		||||
 | 
			
		||||
        @param sourceReader     the source reader to wrap. This BufferingAudioReader
 | 
			
		||||
                                takes ownership of this object and will delete it later
 | 
			
		||||
                                when no longer needed
 | 
			
		||||
        @param timeSliceThread  the thread that should be used to do the background reading.
 | 
			
		||||
                                Make sure that the thread you supply is running, and won't
 | 
			
		||||
                                be deleted while the reader object still exists.
 | 
			
		||||
        @param samplesToBuffer  the total number of samples to buffer ahead.
 | 
			
		||||
    */
 | 
			
		||||
    BufferingAudioReader (AudioFormatReader* sourceReader,
 | 
			
		||||
                          TimeSliceThread& timeSliceThread,
 | 
			
		||||
                          int samplesToBuffer);
 | 
			
		||||
 | 
			
		||||
    ~BufferingAudioReader() override;
 | 
			
		||||
 | 
			
		||||
    /** Sets a number of milliseconds that the reader can block for in its readSamples()
 | 
			
		||||
        method before giving up and returning silence.
 | 
			
		||||
 | 
			
		||||
        A value of less that 0 means "wait forever". The default timeout is 0.
 | 
			
		||||
    */
 | 
			
		||||
    void setReadTimeout (int timeoutMilliseconds) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                      int64 startSampleInFile, int numSamples) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct BufferedBlock
 | 
			
		||||
    {
 | 
			
		||||
        BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples);
 | 
			
		||||
 | 
			
		||||
        Range<int64> range;
 | 
			
		||||
        AudioBuffer<float> buffer;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    int useTimeSlice() override;
 | 
			
		||||
    BufferedBlock* getBlockContaining (int64 pos) const noexcept;
 | 
			
		||||
    bool readNextBufferChunk();
 | 
			
		||||
 | 
			
		||||
    static constexpr int samplesPerBlock = 32768;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AudioFormatReader> source;
 | 
			
		||||
    TimeSliceThread& thread;
 | 
			
		||||
    std::atomic<int64> nextReadPosition { 0 };
 | 
			
		||||
    const int numBlocks;
 | 
			
		||||
    int timeoutMs = 0;
 | 
			
		||||
 | 
			
		||||
    CriticalSection lock;
 | 
			
		||||
    OwnedArray<BufferedBlock> blocks;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    An AudioFormatReader that uses a background thread to pre-read data from
 | 
			
		||||
    another reader.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormatReader
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  BufferingAudioReader  : public AudioFormatReader,
 | 
			
		||||
                                        private TimeSliceClient
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Creates a reader.
 | 
			
		||||
 | 
			
		||||
        @param sourceReader     the source reader to wrap. This BufferingAudioReader
 | 
			
		||||
                                takes ownership of this object and will delete it later
 | 
			
		||||
                                when no longer needed
 | 
			
		||||
        @param timeSliceThread  the thread that should be used to do the background reading.
 | 
			
		||||
                                Make sure that the thread you supply is running, and won't
 | 
			
		||||
                                be deleted while the reader object still exists.
 | 
			
		||||
        @param samplesToBuffer  the total number of samples to buffer ahead.
 | 
			
		||||
    */
 | 
			
		||||
    BufferingAudioReader (AudioFormatReader* sourceReader,
 | 
			
		||||
                          TimeSliceThread& timeSliceThread,
 | 
			
		||||
                          int samplesToBuffer);
 | 
			
		||||
 | 
			
		||||
    ~BufferingAudioReader() override;
 | 
			
		||||
 | 
			
		||||
    /** Sets a number of milliseconds that the reader can block for in its readSamples()
 | 
			
		||||
        method before giving up and returning silence.
 | 
			
		||||
 | 
			
		||||
        A value of less that 0 means "wait forever". The default timeout is 0.
 | 
			
		||||
    */
 | 
			
		||||
    void setReadTimeout (int timeoutMilliseconds) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
 | 
			
		||||
                      int64 startSampleInFile, int numSamples) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct BufferedBlock
 | 
			
		||||
    {
 | 
			
		||||
        BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples);
 | 
			
		||||
 | 
			
		||||
        Range<int64> range;
 | 
			
		||||
        AudioBuffer<float> buffer;
 | 
			
		||||
        bool allSamplesRead = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    int useTimeSlice() override;
 | 
			
		||||
    BufferedBlock* getBlockContaining (int64 pos) const noexcept;
 | 
			
		||||
    bool readNextBufferChunk();
 | 
			
		||||
 | 
			
		||||
    static constexpr int samplesPerBlock = 32768;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AudioFormatReader> source;
 | 
			
		||||
    TimeSliceThread& thread;
 | 
			
		||||
    std::atomic<int64> nextReadPosition { 0 };
 | 
			
		||||
    const int numBlocks;
 | 
			
		||||
    int timeoutMs = 0;
 | 
			
		||||
 | 
			
		||||
    CriticalSection lock;
 | 
			
		||||
    OwnedArray<BufferedBlock> blocks;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,113 +1,113 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A specialised type of AudioFormatReader that uses a MemoryMappedFile to read
 | 
			
		||||
    directly from an audio file.
 | 
			
		||||
 | 
			
		||||
    This allows for incredibly fast random-access to sample data in the mapped
 | 
			
		||||
    region of the file, but not all audio formats support it - see
 | 
			
		||||
    AudioFormat::createMemoryMappedReader().
 | 
			
		||||
 | 
			
		||||
    Note that before reading samples from a MemoryMappedAudioFormatReader, you must first
 | 
			
		||||
    call mapEntireFile() or mapSectionOfFile() to ensure that the region you want to
 | 
			
		||||
    read has been mapped.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormat::createMemoryMappedReader, AudioFormatReader
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MemoryMappedAudioFormatReader  : public AudioFormatReader
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an MemoryMappedAudioFormatReader object.
 | 
			
		||||
 | 
			
		||||
        Note that before attempting to read any data, you must call mapEntireFile()
 | 
			
		||||
        or mapSectionOfFile() to ensure that the region you want to read has
 | 
			
		||||
        been mapped.
 | 
			
		||||
    */
 | 
			
		||||
    MemoryMappedAudioFormatReader (const File& file, const AudioFormatReader& details,
 | 
			
		||||
                                   int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /** Returns the file that is being mapped */
 | 
			
		||||
    const File& getFile() const noexcept                    { return file; }
 | 
			
		||||
 | 
			
		||||
    /** Attempts to map the entire file into memory. */
 | 
			
		||||
    bool mapEntireFile();
 | 
			
		||||
 | 
			
		||||
    /** Attempts to map a section of the file into memory. */
 | 
			
		||||
    virtual bool mapSectionOfFile (Range<int64> samplesToMap);
 | 
			
		||||
 | 
			
		||||
    /** Returns the sample range that's currently memory-mapped and available for reading. */
 | 
			
		||||
    Range<int64> getMappedSection() const noexcept          { return mappedSection; }
 | 
			
		||||
 | 
			
		||||
    /** Touches the memory for the given sample, to force it to be loaded into active memory. */
 | 
			
		||||
    void touchSample (int64 sample) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the samples for all channels at a given sample position.
 | 
			
		||||
        The result array must be large enough to hold a value for each channel
 | 
			
		||||
        that this reader contains.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void getSample (int64 sampleIndex, float* result) const noexcept = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of bytes currently being mapped */
 | 
			
		||||
    size_t getNumBytesUsed() const                          { return map != nullptr ? map->getSize() : 0; }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    File file;
 | 
			
		||||
    Range<int64> mappedSection;
 | 
			
		||||
    std::unique_ptr<MemoryMappedFile> map;
 | 
			
		||||
    int64 dataChunkStart, dataLength;
 | 
			
		||||
    int bytesPerFrame;
 | 
			
		||||
 | 
			
		||||
    /** Converts a sample index to a byte position in the file. */
 | 
			
		||||
    inline int64 sampleToFilePos (int64 sample) const noexcept       { return dataChunkStart + sample * bytesPerFrame; }
 | 
			
		||||
 | 
			
		||||
    /** Converts a byte position in the file to a sample index. */
 | 
			
		||||
    inline int64 filePosToSample (int64 filePos) const noexcept      { return (filePos - dataChunkStart) / bytesPerFrame; }
 | 
			
		||||
 | 
			
		||||
    /** Converts a sample index to a pointer to the mapped file memory. */
 | 
			
		||||
    inline const void* sampleToPointer (int64 sample) const noexcept { return addBytesToPointer (map->getData(), sampleToFilePos (sample) - map->getRange().getStart()); }
 | 
			
		||||
 | 
			
		||||
    /** Used by AudioFormatReader subclasses to scan for min/max ranges in interleaved data. */
 | 
			
		||||
    template <typename SampleType, typename Endianness>
 | 
			
		||||
    Range<float> scanMinAndMaxInterleaved (int channel, int64 startSampleInFile, int64 numSamples) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        using SourceType = AudioData::Pointer <SampleType, Endianness, AudioData::Interleaved, AudioData::Const>;
 | 
			
		||||
 | 
			
		||||
        return SourceType (addBytesToPointer (sampleToPointer (startSampleInFile), ((int) bitsPerSample / 8) * channel), (int) numChannels)
 | 
			
		||||
                .findMinAndMax ((size_t) numSamples);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAudioFormatReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A specialised type of AudioFormatReader that uses a MemoryMappedFile to read
 | 
			
		||||
    directly from an audio file.
 | 
			
		||||
 | 
			
		||||
    This allows for incredibly fast random-access to sample data in the mapped
 | 
			
		||||
    region of the file, but not all audio formats support it - see
 | 
			
		||||
    AudioFormat::createMemoryMappedReader().
 | 
			
		||||
 | 
			
		||||
    Note that before reading samples from a MemoryMappedAudioFormatReader, you must first
 | 
			
		||||
    call mapEntireFile() or mapSectionOfFile() to ensure that the region you want to
 | 
			
		||||
    read has been mapped.
 | 
			
		||||
 | 
			
		||||
    @see AudioFormat::createMemoryMappedReader, AudioFormatReader
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MemoryMappedAudioFormatReader  : public AudioFormatReader
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an MemoryMappedAudioFormatReader object.
 | 
			
		||||
 | 
			
		||||
        Note that before attempting to read any data, you must call mapEntireFile()
 | 
			
		||||
        or mapSectionOfFile() to ensure that the region you want to read has
 | 
			
		||||
        been mapped.
 | 
			
		||||
    */
 | 
			
		||||
    MemoryMappedAudioFormatReader (const File& file, const AudioFormatReader& details,
 | 
			
		||||
                                   int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /** Returns the file that is being mapped */
 | 
			
		||||
    const File& getFile() const noexcept                    { return file; }
 | 
			
		||||
 | 
			
		||||
    /** Attempts to map the entire file into memory. */
 | 
			
		||||
    bool mapEntireFile();
 | 
			
		||||
 | 
			
		||||
    /** Attempts to map a section of the file into memory. */
 | 
			
		||||
    virtual bool mapSectionOfFile (Range<int64> samplesToMap);
 | 
			
		||||
 | 
			
		||||
    /** Returns the sample range that's currently memory-mapped and available for reading. */
 | 
			
		||||
    Range<int64> getMappedSection() const noexcept          { return mappedSection; }
 | 
			
		||||
 | 
			
		||||
    /** Touches the memory for the given sample, to force it to be loaded into active memory. */
 | 
			
		||||
    void touchSample (int64 sample) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the samples for all channels at a given sample position.
 | 
			
		||||
        The result array must be large enough to hold a value for each channel
 | 
			
		||||
        that this reader contains.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void getSample (int64 sampleIndex, float* result) const noexcept = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of bytes currently being mapped */
 | 
			
		||||
    size_t getNumBytesUsed() const                          { return map != nullptr ? map->getSize() : 0; }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    File file;
 | 
			
		||||
    Range<int64> mappedSection;
 | 
			
		||||
    std::unique_ptr<MemoryMappedFile> map;
 | 
			
		||||
    int64 dataChunkStart, dataLength;
 | 
			
		||||
    int bytesPerFrame;
 | 
			
		||||
 | 
			
		||||
    /** Converts a sample index to a byte position in the file. */
 | 
			
		||||
    inline int64 sampleToFilePos (int64 sample) const noexcept       { return dataChunkStart + sample * bytesPerFrame; }
 | 
			
		||||
 | 
			
		||||
    /** Converts a byte position in the file to a sample index. */
 | 
			
		||||
    inline int64 filePosToSample (int64 filePos) const noexcept      { return (filePos - dataChunkStart) / bytesPerFrame; }
 | 
			
		||||
 | 
			
		||||
    /** Converts a sample index to a pointer to the mapped file memory. */
 | 
			
		||||
    inline const void* sampleToPointer (int64 sample) const noexcept { return addBytesToPointer (map->getData(), sampleToFilePos (sample) - map->getRange().getStart()); }
 | 
			
		||||
 | 
			
		||||
    /** Used by AudioFormatReader subclasses to scan for min/max ranges in interleaved data. */
 | 
			
		||||
    template <typename SampleType, typename Endianness>
 | 
			
		||||
    Range<float> scanMinAndMaxInterleaved (int channel, int64 startSampleInFile, int64 numSamples) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        using SourceType = AudioData::Pointer <SampleType, Endianness, AudioData::Interleaved, AudioData::Const>;
 | 
			
		||||
 | 
			
		||||
        return SourceType (addBytesToPointer (sampleToPointer (startSampleInFile), ((int) bitsPerSample / 8) * channel), (int) numChannels)
 | 
			
		||||
                .findMinAndMax ((size_t) numSamples);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAudioFormatReader)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user