git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
91
deps/juce/modules/juce_audio_formats/format/juce_AudioFormat.cpp
vendored
Normal file
91
deps/juce/modules/juce_audio_formats/format/juce_AudioFormat.cpp
vendored
Normal file
@ -0,0 +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
|
216
deps/juce/modules/juce_audio_formats/format/juce_AudioFormat.h
vendored
Normal file
216
deps/juce/modules/juce_audio_formats/format/juce_AudioFormat.h
vendored
Normal file
@ -0,0 +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
|
164
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp
vendored
Normal file
164
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp
vendored
Normal file
@ -0,0 +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
|
150
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatManager.h
vendored
Normal file
150
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatManager.h
vendored
Normal file
@ -0,0 +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
|
447
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp
vendored
Normal file
447
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp
vendored
Normal file
@ -0,0 +1,447 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
333
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatReader.h
vendored
Normal file
333
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatReader.h
vendored
Normal file
@ -0,0 +1,333 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
103
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp
vendored
Normal file
103
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
109
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h
vendored
Normal file
109
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
371
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp
vendored
Normal file
371
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp
vendored
Normal file
@ -0,0 +1,371 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
303
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatWriter.h
vendored
Normal file
303
deps/juce/modules/juce_audio_formats/format/juce_AudioFormatWriter.h
vendored
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
71
deps/juce/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp
vendored
Normal file
71
deps/juce/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp
vendored
Normal file
@ -0,0 +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
|
86
deps/juce/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h
vendored
Normal file
86
deps/juce/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h
vendored
Normal file
@ -0,0 +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
|
339
deps/juce/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp
vendored
Normal file
339
deps/juce/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
96
deps/juce/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h
vendored
Normal file
96
deps/juce/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
113
deps/juce/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h
vendored
Normal file
113
deps/juce/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h
vendored
Normal file
@ -0,0 +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
|
Reference in New Issue
Block a user