mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-14 00:14:18 +00:00
Added Animated App template and examples
This commit is contained in:
parent
fefcf7aca6
commit
ff6520a89a
1141 changed files with 438491 additions and 94 deletions
|
|
@ -0,0 +1,600 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += destBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
*(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += destBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
*(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fffff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += destBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fffff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += destBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fffffff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += destBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
*(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
{
|
||||
const double maxVal = (double) 0x7fffffff;
|
||||
char* intData = static_cast<char*> (dest);
|
||||
|
||||
if (dest != (void*) source || destBytesPerSample <= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
intData += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += destBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= destBytesPerSample;
|
||||
*(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
{
|
||||
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
|
||||
|
||||
char* d = static_cast<char*> (dest);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(float*) d = source[i];
|
||||
|
||||
#if JUCE_BIG_ENDIAN
|
||||
*(uint32*) d = ByteOrder::swap (*(uint32*) d);
|
||||
#endif
|
||||
|
||||
d += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
|
||||
{
|
||||
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
|
||||
|
||||
char* d = static_cast<char*> (dest);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
*(float*) d = source[i];
|
||||
|
||||
#if JUCE_LITTLE_ENDIAN
|
||||
*(uint32*) d = ByteOrder::swap (*(uint32*) d);
|
||||
#endif
|
||||
|
||||
d += destBytesPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData);
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += srcBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData);
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += srcBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fffff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData);
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += srcBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fffff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData);
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += srcBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fffffff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData);
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += srcBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
{
|
||||
const float scale = 1.0f / 0x7fffffff;
|
||||
const char* intData = static_cast<const char*> (source);
|
||||
|
||||
if (source != (void*) dest || srcBytesPerSample >= 4)
|
||||
{
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData);
|
||||
intData += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
intData += srcBytesPerSample * numSamples;
|
||||
|
||||
for (int i = numSamples; --i >= 0;)
|
||||
{
|
||||
intData -= srcBytesPerSample;
|
||||
dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
{
|
||||
const char* s = static_cast<const char*> (source);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = *(float*)s;
|
||||
|
||||
#if JUCE_BIG_ENDIAN
|
||||
uint32* const d = (uint32*) (dest + i);
|
||||
*d = ByteOrder::swap (*d);
|
||||
#endif
|
||||
|
||||
s += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
|
||||
{
|
||||
const char* s = static_cast<const char*> (source);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
dest[i] = *(float*)s;
|
||||
|
||||
#if JUCE_LITTLE_ENDIAN
|
||||
uint32* const d = (uint32*) (dest + i);
|
||||
*d = ByteOrder::swap (*d);
|
||||
#endif
|
||||
|
||||
s += srcBytesPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void AudioDataConverters::convertFloatToFormat (const DataFormat destFormat,
|
||||
const float* const source,
|
||||
void* const dest,
|
||||
const int numSamples)
|
||||
{
|
||||
switch (destFormat)
|
||||
{
|
||||
case int16LE: convertFloatToInt16LE (source, dest, numSamples); break;
|
||||
case int16BE: convertFloatToInt16BE (source, dest, numSamples); break;
|
||||
case int24LE: convertFloatToInt24LE (source, dest, numSamples); break;
|
||||
case int24BE: convertFloatToInt24BE (source, dest, numSamples); break;
|
||||
case int32LE: convertFloatToInt32LE (source, dest, numSamples); break;
|
||||
case int32BE: convertFloatToInt32BE (source, dest, numSamples); break;
|
||||
case float32LE: convertFloatToFloat32LE (source, dest, numSamples); break;
|
||||
case float32BE: convertFloatToFloat32BE (source, dest, numSamples); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::convertFormatToFloat (const DataFormat sourceFormat,
|
||||
const void* const source,
|
||||
float* const dest,
|
||||
const int numSamples)
|
||||
{
|
||||
switch (sourceFormat)
|
||||
{
|
||||
case int16LE: convertInt16LEToFloat (source, dest, numSamples); break;
|
||||
case int16BE: convertInt16BEToFloat (source, dest, numSamples); break;
|
||||
case int24LE: convertInt24LEToFloat (source, dest, numSamples); break;
|
||||
case int24BE: convertInt24BEToFloat (source, dest, numSamples); break;
|
||||
case int32LE: convertInt32LEToFloat (source, dest, numSamples); break;
|
||||
case int32BE: convertInt32BEToFloat (source, dest, numSamples); break;
|
||||
case float32LE: convertFloat32LEToFloat (source, dest, numSamples); break;
|
||||
case float32BE: convertFloat32BEToFloat (source, dest, numSamples); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDataConverters::interleaveSamples (const float** const source,
|
||||
float* const dest,
|
||||
const int numSamples,
|
||||
const int numChannels)
|
||||
{
|
||||
for (int chan = 0; chan < numChannels; ++chan)
|
||||
{
|
||||
int i = chan;
|
||||
const float* src = source [chan];
|
||||
|
||||
for (int j = 0; j < numSamples; ++j)
|
||||
{
|
||||
dest [i] = src [j];
|
||||
i += numChannels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDataConverters::deinterleaveSamples (const float* const source,
|
||||
float** const dest,
|
||||
const int numSamples,
|
||||
const int numChannels)
|
||||
{
|
||||
for (int chan = 0; chan < numChannels; ++chan)
|
||||
{
|
||||
int i = chan;
|
||||
float* dst = dest [chan];
|
||||
|
||||
for (int j = 0; j < numSamples; ++j)
|
||||
{
|
||||
dst [j] = source [i];
|
||||
i += numChannels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class AudioConversionTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AudioConversionTests() : UnitTest ("Audio data conversion") {}
|
||||
|
||||
template <class F1, class E1, class F2, class E2>
|
||||
struct Test5
|
||||
{
|
||||
static void test (UnitTest& unitTest, Random& r)
|
||||
{
|
||||
test (unitTest, false, r);
|
||||
test (unitTest, true, r);
|
||||
}
|
||||
|
||||
static void test (UnitTest& unitTest, bool inPlace, Random& r)
|
||||
{
|
||||
const int numSamples = 2048;
|
||||
int32 original [numSamples], converted [numSamples], reversed [numSamples];
|
||||
|
||||
{
|
||||
AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst> d (original);
|
||||
bool clippingFailed = false;
|
||||
|
||||
for (int i = 0; i < numSamples / 2; ++i)
|
||||
{
|
||||
d.setAsFloat (r.nextFloat() * 2.2f - 1.1f);
|
||||
|
||||
if (! d.isFloatingPoint())
|
||||
clippingFailed = d.getAsFloat() > 1.0f || d.getAsFloat() < -1.0f || clippingFailed;
|
||||
|
||||
++d;
|
||||
d.setAsInt32 (r.nextInt());
|
||||
++d;
|
||||
}
|
||||
|
||||
unitTest.expect (! clippingFailed);
|
||||
}
|
||||
|
||||
// convert data from the source to dest format..
|
||||
ScopedPointer<AudioData::Converter> conv (new AudioData::ConverterInstance <AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const>,
|
||||
AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::NonConst> >());
|
||||
conv->convertSamples (inPlace ? reversed : converted, original, numSamples);
|
||||
|
||||
// ..and back again..
|
||||
conv = new AudioData::ConverterInstance <AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::Const>,
|
||||
AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst> >();
|
||||
if (! inPlace)
|
||||
zeromem (reversed, sizeof (reversed));
|
||||
|
||||
conv->convertSamples (reversed, inPlace ? reversed : converted, numSamples);
|
||||
|
||||
{
|
||||
int biggestDiff = 0;
|
||||
AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const> d1 (original);
|
||||
AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const> d2 (reversed);
|
||||
|
||||
const int errorMargin = 2 * AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const>::get32BitResolution()
|
||||
+ AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::Const>::get32BitResolution();
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
biggestDiff = jmax (biggestDiff, std::abs (d1.getAsInt32() - d2.getAsInt32()));
|
||||
++d1;
|
||||
++d2;
|
||||
}
|
||||
|
||||
unitTest.expect (biggestDiff <= errorMargin);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class F1, class E1, class FormatType>
|
||||
struct Test3
|
||||
{
|
||||
static void test (UnitTest& unitTest, Random& r)
|
||||
{
|
||||
Test5 <F1, E1, FormatType, AudioData::BigEndian>::test (unitTest, r);
|
||||
Test5 <F1, E1, FormatType, AudioData::LittleEndian>::test (unitTest, r);
|
||||
}
|
||||
};
|
||||
|
||||
template <class FormatType, class Endianness>
|
||||
struct Test2
|
||||
{
|
||||
static void test (UnitTest& unitTest, Random& r)
|
||||
{
|
||||
Test3 <FormatType, Endianness, AudioData::Int8>::test (unitTest, r);
|
||||
Test3 <FormatType, Endianness, AudioData::UInt8>::test (unitTest, r);
|
||||
Test3 <FormatType, Endianness, AudioData::Int16>::test (unitTest, r);
|
||||
Test3 <FormatType, Endianness, AudioData::Int24>::test (unitTest, r);
|
||||
Test3 <FormatType, Endianness, AudioData::Int32>::test (unitTest, r);
|
||||
Test3 <FormatType, Endianness, AudioData::Float32>::test (unitTest, r);
|
||||
}
|
||||
};
|
||||
|
||||
template <class FormatType>
|
||||
struct Test1
|
||||
{
|
||||
static void test (UnitTest& unitTest, Random& r)
|
||||
{
|
||||
Test2 <FormatType, AudioData::BigEndian>::test (unitTest, r);
|
||||
Test2 <FormatType, AudioData::LittleEndian>::test (unitTest, r);
|
||||
}
|
||||
};
|
||||
|
||||
void runTest()
|
||||
{
|
||||
Random r = getRandom();
|
||||
beginTest ("Round-trip conversion: Int8");
|
||||
Test1 <AudioData::Int8>::test (*this, r);
|
||||
beginTest ("Round-trip conversion: Int16");
|
||||
Test1 <AudioData::Int16>::test (*this, r);
|
||||
beginTest ("Round-trip conversion: Int24");
|
||||
Test1 <AudioData::Int24>::test (*this, r);
|
||||
beginTest ("Round-trip conversion: Int32");
|
||||
Test1 <AudioData::Int32>::test (*this, r);
|
||||
beginTest ("Round-trip conversion: Float32");
|
||||
Test1 <AudioData::Float32>::test (*this, r);
|
||||
}
|
||||
};
|
||||
|
||||
static AudioConversionTests audioConversionUnitTests;
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,710 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_AUDIODATACONVERTERS_H_INCLUDED
|
||||
#define JUCE_AUDIODATACONVERTERS_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class a container which holds all the classes pertaining to the AudioData::Pointer
|
||||
audio sample format class.
|
||||
|
||||
@see AudioData::Pointer.
|
||||
*/
|
||||
class JUCE_API AudioData
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
// These types can be used as the SampleFormat template parameter for the AudioData::Pointer class.
|
||||
|
||||
class Int8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit integer packed data format. */
|
||||
class UInt8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit unsigned integer packed data format. */
|
||||
class Int16; /**< Used as a template parameter for AudioData::Pointer. Indicates an 16-bit integer packed data format. */
|
||||
class Int24; /**< Used as a template parameter for AudioData::Pointer. Indicates an 24-bit integer packed data format. */
|
||||
class Int32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit integer packed data format. */
|
||||
class Float32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit float data format. */
|
||||
|
||||
//==============================================================================
|
||||
// These types can be used as the Endianness template parameter for the AudioData::Pointer class.
|
||||
|
||||
class BigEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in big-endian order. */
|
||||
class LittleEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in little-endian order. */
|
||||
class NativeEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in the CPU's native endianness. */
|
||||
|
||||
//==============================================================================
|
||||
// These types can be used as the InterleavingType template parameter for the AudioData::Pointer class.
|
||||
|
||||
class NonInterleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored contiguously. */
|
||||
class Interleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are interleaved with a number of other channels. */
|
||||
|
||||
//==============================================================================
|
||||
// These types can be used as the Constness template parameter for the AudioData::Pointer class.
|
||||
|
||||
class NonConst; /**< Used as a template parameter for AudioData::Pointer. Indicates that the pointer can be used for non-const data. */
|
||||
class Const; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples can only be used for const data.. */
|
||||
|
||||
#ifndef DOXYGEN
|
||||
//==============================================================================
|
||||
class BigEndian
|
||||
{
|
||||
public:
|
||||
template <class SampleFormatType> static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatBE(); }
|
||||
template <class SampleFormatType> static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); }
|
||||
template <class SampleFormatType> static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32BE(); }
|
||||
template <class SampleFormatType> static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); }
|
||||
template <class SourceType, class DestType> static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromBE (source); }
|
||||
enum { isBigEndian = 1 };
|
||||
};
|
||||
|
||||
class LittleEndian
|
||||
{
|
||||
public:
|
||||
template <class SampleFormatType> static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatLE(); }
|
||||
template <class SampleFormatType> static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); }
|
||||
template <class SampleFormatType> static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32LE(); }
|
||||
template <class SampleFormatType> static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); }
|
||||
template <class SourceType, class DestType> static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromLE (source); }
|
||||
enum { isBigEndian = 0 };
|
||||
};
|
||||
|
||||
#if JUCE_BIG_ENDIAN
|
||||
class NativeEndian : public BigEndian {};
|
||||
#else
|
||||
class NativeEndian : public LittleEndian {};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class Int8
|
||||
{
|
||||
public:
|
||||
inline Int8 (void* d) noexcept : data (static_cast <int8*> (d)) {}
|
||||
|
||||
inline void advance() noexcept { ++data; }
|
||||
inline void skip (int numSamples) noexcept { data += numSamples; }
|
||||
inline float getAsFloatLE() const noexcept { return (float) (*data * (1.0 / (1.0 + maxValue))); }
|
||||
inline float getAsFloatBE() const noexcept { return getAsFloatLE(); }
|
||||
inline void setAsFloatLE (float newValue) noexcept { *data = (int8) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))); }
|
||||
inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); }
|
||||
inline int32 getAsInt32LE() const noexcept { return (int) (*data << 24); }
|
||||
inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); }
|
||||
inline void setAsInt32LE (int newValue) noexcept { *data = (int8) (newValue >> 24); }
|
||||
inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); }
|
||||
inline void clear() noexcept { *data = 0; }
|
||||
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
|
||||
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
|
||||
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
|
||||
inline void copyFromSameType (Int8& source) noexcept { *data = *source.data; }
|
||||
|
||||
int8* data;
|
||||
enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 };
|
||||
};
|
||||
|
||||
class UInt8
|
||||
{
|
||||
public:
|
||||
inline UInt8 (void* d) noexcept : data (static_cast <uint8*> (d)) {}
|
||||
|
||||
inline void advance() noexcept { ++data; }
|
||||
inline void skip (int numSamples) noexcept { data += numSamples; }
|
||||
inline float getAsFloatLE() const noexcept { return (float) ((*data - 128) * (1.0 / (1.0 + maxValue))); }
|
||||
inline float getAsFloatBE() const noexcept { return getAsFloatLE(); }
|
||||
inline void setAsFloatLE (float newValue) noexcept { *data = (uint8) jlimit (0, 255, 128 + roundToInt (newValue * (1.0 + maxValue))); }
|
||||
inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); }
|
||||
inline int32 getAsInt32LE() const noexcept { return (int) ((*data - 128) << 24); }
|
||||
inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); }
|
||||
inline void setAsInt32LE (int newValue) noexcept { *data = (uint8) (128 + (newValue >> 24)); }
|
||||
inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); }
|
||||
inline void clear() noexcept { *data = 128; }
|
||||
inline void clearMultiple (int num) noexcept { memset (data, 128, (size_t) num) ;}
|
||||
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
|
||||
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
|
||||
inline void copyFromSameType (UInt8& source) noexcept { *data = *source.data; }
|
||||
|
||||
uint8* data;
|
||||
enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 };
|
||||
};
|
||||
|
||||
class Int16
|
||||
{
|
||||
public:
|
||||
inline Int16 (void* d) noexcept : data (static_cast <uint16*> (d)) {}
|
||||
|
||||
inline void advance() noexcept { ++data; }
|
||||
inline void skip (int numSamples) noexcept { data += numSamples; }
|
||||
inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfBigEndian (*data)); }
|
||||
inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfLittleEndian (*data)); }
|
||||
inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); }
|
||||
inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); }
|
||||
inline int32 getAsInt32LE() const noexcept { return (int32) (ByteOrder::swapIfBigEndian ((uint16) *data) << 16); }
|
||||
inline int32 getAsInt32BE() const noexcept { return (int32) (ByteOrder::swapIfLittleEndian ((uint16) *data) << 16); }
|
||||
inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) (newValue >> 16)); }
|
||||
inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) (newValue >> 16)); }
|
||||
inline void clear() noexcept { *data = 0; }
|
||||
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
|
||||
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
|
||||
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
|
||||
inline void copyFromSameType (Int16& source) noexcept { *data = *source.data; }
|
||||
|
||||
uint16* data;
|
||||
enum { bytesPerSample = 2, maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 };
|
||||
};
|
||||
|
||||
class Int24
|
||||
{
|
||||
public:
|
||||
inline Int24 (void* d) noexcept : data (static_cast <char*> (d)) {}
|
||||
|
||||
inline void advance() noexcept { data += 3; }
|
||||
inline void skip (int numSamples) noexcept { data += 3 * numSamples; }
|
||||
inline float getAsFloatLE() const noexcept { return (float) (ByteOrder::littleEndian24Bit (data) * (1.0 / (1.0 + maxValue))); }
|
||||
inline float getAsFloatBE() const noexcept { return (float) (ByteOrder::bigEndian24Bit (data) * (1.0 / (1.0 + maxValue))); }
|
||||
inline void setAsFloatLE (float newValue) noexcept { ByteOrder::littleEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); }
|
||||
inline void setAsFloatBE (float newValue) noexcept { ByteOrder::bigEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); }
|
||||
inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::littleEndian24Bit (data) << 8; }
|
||||
inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::bigEndian24Bit (data) << 8; }
|
||||
inline void setAsInt32LE (int32 newValue) noexcept { ByteOrder::littleEndian24BitToChars (newValue >> 8, data); }
|
||||
inline void setAsInt32BE (int32 newValue) noexcept { ByteOrder::bigEndian24BitToChars (newValue >> 8, data); }
|
||||
inline void clear() noexcept { data[0] = 0; data[1] = 0; data[2] = 0; }
|
||||
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
|
||||
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
|
||||
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
|
||||
inline void copyFromSameType (Int24& source) noexcept { data[0] = source.data[0]; data[1] = source.data[1]; data[2] = source.data[2]; }
|
||||
|
||||
char* data;
|
||||
enum { bytesPerSample = 3, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 };
|
||||
};
|
||||
|
||||
class Int32
|
||||
{
|
||||
public:
|
||||
inline Int32 (void* d) noexcept : data (static_cast <uint32*> (d)) {}
|
||||
|
||||
inline void advance() noexcept { ++data; }
|
||||
inline void skip (int numSamples) noexcept { data += numSamples; }
|
||||
inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); }
|
||||
inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); }
|
||||
inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
|
||||
inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
|
||||
inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data); }
|
||||
inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data); }
|
||||
inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); }
|
||||
inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue); }
|
||||
inline void clear() noexcept { *data = 0; }
|
||||
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
|
||||
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
|
||||
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
|
||||
inline void copyFromSameType (Int32& source) noexcept { *data = *source.data; }
|
||||
|
||||
uint32* data;
|
||||
enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = 1, isFloat = 0 };
|
||||
};
|
||||
|
||||
/** A 32-bit integer type, of which only the bottom 24 bits are used. */
|
||||
class Int24in32 : public Int32
|
||||
{
|
||||
public:
|
||||
inline Int24in32 (void* d) noexcept : Int32 (d) {}
|
||||
|
||||
inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); }
|
||||
inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); }
|
||||
inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
|
||||
inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
|
||||
inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data) << 8; }
|
||||
inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data) << 8; }
|
||||
inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue >> 8); }
|
||||
inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue >> 8); }
|
||||
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
|
||||
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
|
||||
inline void copyFromSameType (Int24in32& source) noexcept { *data = *source.data; }
|
||||
|
||||
enum { bytesPerSample = 4, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 };
|
||||
};
|
||||
|
||||
class Float32
|
||||
{
|
||||
public:
|
||||
inline Float32 (void* d) noexcept : data (static_cast <float*> (d)) {}
|
||||
|
||||
inline void advance() noexcept { ++data; }
|
||||
inline void skip (int numSamples) noexcept { data += numSamples; }
|
||||
#if JUCE_BIG_ENDIAN
|
||||
inline float getAsFloatBE() const noexcept { return *data; }
|
||||
inline void setAsFloatBE (float newValue) noexcept { *data = newValue; }
|
||||
inline float getAsFloatLE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; }
|
||||
inline void setAsFloatLE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); }
|
||||
#else
|
||||
inline float getAsFloatLE() const noexcept { return *data; }
|
||||
inline void setAsFloatLE (float newValue) noexcept { *data = newValue; }
|
||||
inline float getAsFloatBE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; }
|
||||
inline void setAsFloatBE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); }
|
||||
#endif
|
||||
inline int32 getAsInt32LE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatLE()) * (double) maxValue); }
|
||||
inline int32 getAsInt32BE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatBE()) * (double) maxValue); }
|
||||
inline void setAsInt32LE (int32 newValue) noexcept { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + maxValue)))); }
|
||||
inline void setAsInt32BE (int32 newValue) noexcept { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + maxValue)))); }
|
||||
inline void clear() noexcept { *data = 0; }
|
||||
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
|
||||
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsFloatLE (source.getAsFloat()); }
|
||||
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsFloatBE (source.getAsFloat()); }
|
||||
inline void copyFromSameType (Float32& source) noexcept { *data = *source.data; }
|
||||
|
||||
float* data;
|
||||
enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = (1 << 8), isFloat = 1 };
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class NonInterleaved
|
||||
{
|
||||
public:
|
||||
inline NonInterleaved() noexcept {}
|
||||
inline NonInterleaved (const NonInterleaved&) noexcept {}
|
||||
inline NonInterleaved (const int) noexcept {}
|
||||
inline void copyFrom (const NonInterleaved&) noexcept {}
|
||||
template <class SampleFormatType> inline void advanceData (SampleFormatType& s) noexcept { s.advance(); }
|
||||
template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numSamples); }
|
||||
template <class SampleFormatType> inline void clear (SampleFormatType& s, int numSamples) noexcept { s.clearMultiple (numSamples); }
|
||||
template <class SampleFormatType> inline static int getNumBytesBetweenSamples (const SampleFormatType&) noexcept { return SampleFormatType::bytesPerSample; }
|
||||
|
||||
enum { isInterleavedType = 0, numInterleavedChannels = 1 };
|
||||
};
|
||||
|
||||
class Interleaved
|
||||
{
|
||||
public:
|
||||
inline Interleaved() noexcept : numInterleavedChannels (1) {}
|
||||
inline Interleaved (const Interleaved& other) noexcept : numInterleavedChannels (other.numInterleavedChannels) {}
|
||||
inline Interleaved (const int numInterleavedChans) noexcept : numInterleavedChannels (numInterleavedChans) {}
|
||||
inline void copyFrom (const Interleaved& other) noexcept { numInterleavedChannels = other.numInterleavedChannels; }
|
||||
template <class SampleFormatType> inline void advanceData (SampleFormatType& s) noexcept { s.skip (numInterleavedChannels); }
|
||||
template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numInterleavedChannels * numSamples); }
|
||||
template <class SampleFormatType> inline void clear (SampleFormatType& s, int numSamples) noexcept { while (--numSamples >= 0) { s.clear(); s.skip (numInterleavedChannels); } }
|
||||
template <class SampleFormatType> inline int getNumBytesBetweenSamples (const SampleFormatType&) const noexcept { return numInterleavedChannels * SampleFormatType::bytesPerSample; }
|
||||
int numInterleavedChannels;
|
||||
enum { isInterleavedType = 1 };
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class NonConst
|
||||
{
|
||||
public:
|
||||
typedef void VoidType;
|
||||
static inline void* toVoidPtr (VoidType* v) noexcept { return v; }
|
||||
enum { isConst = 0 };
|
||||
};
|
||||
|
||||
class Const
|
||||
{
|
||||
public:
|
||||
typedef const void VoidType;
|
||||
static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast <void*> (v); }
|
||||
enum { isConst = 1 };
|
||||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A pointer to a block of audio data with a particular encoding.
|
||||
|
||||
This object can be used to read and write from blocks of encoded audio samples. To create one, you specify
|
||||
the audio format as a series of template parameters, e.g.
|
||||
@code
|
||||
// this creates a pointer for reading from a const array of 16-bit little-endian packed samples.
|
||||
AudioData::Pointer <AudioData::Int16,
|
||||
AudioData::LittleEndian,
|
||||
AudioData::NonInterleaved,
|
||||
AudioData::Const> pointer (someRawAudioData);
|
||||
|
||||
// These methods read the sample that is being pointed to
|
||||
float firstSampleAsFloat = pointer.getAsFloat();
|
||||
int32 firstSampleAsInt = pointer.getAsInt32();
|
||||
++pointer; // moves the pointer to the next sample.
|
||||
pointer += 3; // skips the next 3 samples.
|
||||
@endcode
|
||||
|
||||
The convertSamples() method lets you copy a range of samples from one format to another, automatically
|
||||
converting its format.
|
||||
|
||||
@see AudioData::Converter
|
||||
*/
|
||||
template <typename SampleFormat,
|
||||
typename Endianness,
|
||||
typename InterleavingType,
|
||||
typename Constness>
|
||||
class Pointer : private InterleavingType // (inherited for EBCO)
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a non-interleaved pointer from some raw data in the appropriate format.
|
||||
This constructor is only used if you've specified the AudioData::NonInterleaved option -
|
||||
for interleaved formats, use the constructor that also takes a number of channels.
|
||||
*/
|
||||
Pointer (typename Constness::VoidType* sourceData) noexcept
|
||||
: data (Constness::toVoidPtr (sourceData))
|
||||
{
|
||||
// If you're using interleaved data, call the other constructor! If you're using non-interleaved data,
|
||||
// you should pass NonInterleaved as the template parameter for the interleaving type!
|
||||
static_jassert (InterleavingType::isInterleavedType == 0);
|
||||
}
|
||||
|
||||
/** Creates a pointer from some raw data in the appropriate format with the specified number of interleaved channels.
|
||||
For non-interleaved data, use the other constructor.
|
||||
*/
|
||||
Pointer (typename Constness::VoidType* sourceData, int numInterleaved) noexcept
|
||||
: InterleavingType (numInterleaved), data (Constness::toVoidPtr (sourceData))
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another pointer. */
|
||||
Pointer (const Pointer& other) noexcept
|
||||
: InterleavingType (other), data (other.data)
|
||||
{
|
||||
}
|
||||
|
||||
Pointer& operator= (const Pointer& other) noexcept
|
||||
{
|
||||
InterleavingType::operator= (other);
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the value of the first sample as a floating point value.
|
||||
The value will be in the range -1.0 to 1.0 for integer formats. For floating point
|
||||
formats, the value could be outside that range, although -1 to 1 is the standard range.
|
||||
*/
|
||||
inline float getAsFloat() const noexcept { return Endianness::getAsFloat (data); }
|
||||
|
||||
/** Sets the value of the first sample as a floating point value.
|
||||
|
||||
(This method can only be used if the AudioData::NonConst option was used).
|
||||
The value should be in the range -1.0 to 1.0 - for integer formats, values outside that
|
||||
range will be clipped. For floating point formats, any value passed in here will be
|
||||
written directly, although -1 to 1 is the standard range.
|
||||
*/
|
||||
inline void setAsFloat (float newValue) noexcept
|
||||
{
|
||||
static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
|
||||
Endianness::setAsFloat (data, newValue);
|
||||
}
|
||||
|
||||
/** Returns the value of the first sample as a 32-bit integer.
|
||||
The value returned will be in the range 0x80000000 to 0x7fffffff, and shorter values will be
|
||||
shifted to fill this range (e.g. if you're reading from 24-bit data, the values will be shifted up
|
||||
by 8 bits when returned here). If the source data is floating point, values beyond -1.0 to 1.0 will
|
||||
be clipped so that -1.0 maps onto -0x7fffffff and 1.0 maps to 0x7fffffff.
|
||||
*/
|
||||
inline int32 getAsInt32() const noexcept { return Endianness::getAsInt32 (data); }
|
||||
|
||||
/** Sets the value of the first sample as a 32-bit integer.
|
||||
This will be mapped to the range of the format that is being written - see getAsInt32().
|
||||
*/
|
||||
inline void setAsInt32 (int32 newValue) noexcept
|
||||
{
|
||||
static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
|
||||
Endianness::setAsInt32 (data, newValue);
|
||||
}
|
||||
|
||||
/** Moves the pointer along to the next sample. */
|
||||
inline Pointer& operator++() noexcept { advance(); return *this; }
|
||||
|
||||
/** Moves the pointer back to the previous sample. */
|
||||
inline Pointer& operator--() noexcept { this->advanceDataBy (data, -1); return *this; }
|
||||
|
||||
/** Adds a number of samples to the pointer's position. */
|
||||
Pointer& operator+= (int samplesToJump) noexcept { this->advanceDataBy (data, samplesToJump); return *this; }
|
||||
|
||||
/** Writes a stream of samples into this pointer from another pointer.
|
||||
This will copy the specified number of samples, converting between formats appropriately.
|
||||
*/
|
||||
void convertSamples (Pointer source, int numSamples) const noexcept
|
||||
{
|
||||
static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
|
||||
|
||||
for (Pointer dest (*this); --numSamples >= 0;)
|
||||
{
|
||||
dest.data.copyFromSameType (source.data);
|
||||
dest.advance();
|
||||
source.advance();
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes a stream of samples into this pointer from another pointer.
|
||||
This will copy the specified number of samples, converting between formats appropriately.
|
||||
*/
|
||||
template <class OtherPointerType>
|
||||
void convertSamples (OtherPointerType source, int numSamples) const noexcept
|
||||
{
|
||||
static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
|
||||
|
||||
Pointer dest (*this);
|
||||
|
||||
if (source.getRawData() != getRawData() || source.getNumBytesBetweenSamples() >= getNumBytesBetweenSamples())
|
||||
{
|
||||
while (--numSamples >= 0)
|
||||
{
|
||||
Endianness::copyFrom (dest.data, source);
|
||||
dest.advance();
|
||||
++source;
|
||||
}
|
||||
}
|
||||
else // copy backwards if we're increasing the sample width..
|
||||
{
|
||||
dest += numSamples;
|
||||
source += numSamples;
|
||||
|
||||
while (--numSamples >= 0)
|
||||
Endianness::copyFrom ((--dest).data, --source);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets a number of samples to zero. */
|
||||
void clearSamples (int numSamples) const noexcept
|
||||
{
|
||||
Pointer dest (*this);
|
||||
dest.clear (dest.data, numSamples);
|
||||
}
|
||||
|
||||
/** Scans a block of data, returning the lowest and highest levels as floats */
|
||||
void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept
|
||||
{
|
||||
if (numSamples == 0)
|
||||
{
|
||||
minValue = maxValue = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Pointer dest (*this);
|
||||
|
||||
if (isFloatingPoint())
|
||||
{
|
||||
float mn = dest.getAsFloat();
|
||||
dest.advance();
|
||||
float mx = mn;
|
||||
|
||||
while (--numSamples > 0)
|
||||
{
|
||||
const float v = dest.getAsFloat();
|
||||
dest.advance();
|
||||
|
||||
if (mx < v) mx = v;
|
||||
if (v < mn) mn = v;
|
||||
}
|
||||
|
||||
minValue = mn;
|
||||
maxValue = mx;
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 mn = dest.getAsInt32();
|
||||
dest.advance();
|
||||
int32 mx = mn;
|
||||
|
||||
while (--numSamples > 0)
|
||||
{
|
||||
const int v = dest.getAsInt32();
|
||||
dest.advance();
|
||||
|
||||
if (mx < v) mx = v;
|
||||
if (v < mn) mn = v;
|
||||
}
|
||||
|
||||
minValue = mn * (float) (1.0 / (1.0 + Int32::maxValue));
|
||||
maxValue = mx * (float) (1.0 / (1.0 + Int32::maxValue));
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the pointer is using a floating-point format. */
|
||||
static bool isFloatingPoint() noexcept { return (bool) SampleFormat::isFloat; }
|
||||
|
||||
/** Returns true if the format is big-endian. */
|
||||
static bool isBigEndian() noexcept { return (bool) Endianness::isBigEndian; }
|
||||
|
||||
/** Returns the number of bytes in each sample (ignoring the number of interleaved channels). */
|
||||
static int getBytesPerSample() noexcept { return (int) SampleFormat::bytesPerSample; }
|
||||
|
||||
/** Returns the number of interleaved channels in the format. */
|
||||
int getNumInterleavedChannels() const noexcept { return (int) this->numInterleavedChannels; }
|
||||
|
||||
/** Returns the number of bytes between the start address of each sample. */
|
||||
int getNumBytesBetweenSamples() const noexcept { return InterleavingType::getNumBytesBetweenSamples (data); }
|
||||
|
||||
/** Returns the accuracy of this format when represented as a 32-bit integer.
|
||||
This is the smallest number above 0 that can be represented in the sample format, converted to
|
||||
a 32-bit range. E,g. if the format is 8-bit, its resolution is 0x01000000; if the format is 24-bit,
|
||||
its resolution is 0x100.
|
||||
*/
|
||||
static int get32BitResolution() noexcept { return (int) SampleFormat::resolution; }
|
||||
|
||||
/** Returns a pointer to the underlying data. */
|
||||
const void* getRawData() const noexcept { return data.data; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
SampleFormat data;
|
||||
|
||||
inline void advance() noexcept { this->advanceData (data); }
|
||||
|
||||
Pointer operator++ (int); // private to force you to use the more efficient pre-increment!
|
||||
Pointer operator-- (int);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A base class for objects that are used to convert between two different sample formats.
|
||||
|
||||
The AudioData::ConverterInstance implements this base class and can be templated, so
|
||||
you can create an instance that converts between two particular formats, and then
|
||||
store this in the abstract base class.
|
||||
|
||||
@see AudioData::ConverterInstance
|
||||
*/
|
||||
class Converter
|
||||
{
|
||||
public:
|
||||
virtual ~Converter() {}
|
||||
|
||||
/** Converts a sequence of samples from the converter's source format into the dest format. */
|
||||
virtual void convertSamples (void* destSamples, const void* sourceSamples, int numSamples) const = 0;
|
||||
|
||||
/** Converts a sequence of samples from the converter's source format into the dest format.
|
||||
This method takes sub-channel indexes, which can be used with interleaved formats in order to choose a
|
||||
particular sub-channel of the data to be used.
|
||||
*/
|
||||
virtual void convertSamples (void* destSamples, int destSubChannel,
|
||||
const void* sourceSamples, int sourceSubChannel, int numSamples) const = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class that converts between two templated AudioData::Pointer types, and which
|
||||
implements the AudioData::Converter interface.
|
||||
|
||||
This can be used as a concrete instance of the AudioData::Converter abstract class.
|
||||
|
||||
@see AudioData::Converter
|
||||
*/
|
||||
template <class SourceSampleType, class DestSampleType>
|
||||
class ConverterInstance : public Converter
|
||||
{
|
||||
public:
|
||||
ConverterInstance (int numSourceChannels = 1, int numDestChannels = 1)
|
||||
: sourceChannels (numSourceChannels), destChannels (numDestChannels)
|
||||
{}
|
||||
|
||||
void convertSamples (void* dest, const void* source, int numSamples) const override
|
||||
{
|
||||
SourceSampleType s (source, sourceChannels);
|
||||
DestSampleType d (dest, destChannels);
|
||||
d.convertSamples (s, numSamples);
|
||||
}
|
||||
|
||||
void convertSamples (void* dest, int destSubChannel,
|
||||
const void* source, int sourceSubChannel, int numSamples) const override
|
||||
{
|
||||
jassert (destSubChannel < destChannels && sourceSubChannel < sourceChannels);
|
||||
|
||||
SourceSampleType s (addBytesToPointer (source, sourceSubChannel * SourceSampleType::getBytesPerSample()), sourceChannels);
|
||||
DestSampleType d (addBytesToPointer (dest, destSubChannel * DestSampleType::getBytesPerSample()), destChannels);
|
||||
d.convertSamples (s, numSamples);
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ConverterInstance)
|
||||
|
||||
const int sourceChannels, destChannels;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A set of routines to convert buffers of 32-bit floating point data to and from
|
||||
various integer formats.
|
||||
|
||||
Note that these functions are deprecated - the AudioData class provides a much more
|
||||
flexible set of conversion classes now.
|
||||
*/
|
||||
class JUCE_API AudioDataConverters
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
static void convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2);
|
||||
static void convertFloatToInt16BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2);
|
||||
|
||||
static void convertFloatToInt24LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3);
|
||||
static void convertFloatToInt24BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3);
|
||||
|
||||
static void convertFloatToInt32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
|
||||
static void convertFloatToInt32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
|
||||
|
||||
static void convertFloatToFloat32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
|
||||
static void convertFloatToFloat32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
|
||||
|
||||
//==============================================================================
|
||||
static void convertInt16LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2);
|
||||
static void convertInt16BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2);
|
||||
|
||||
static void convertInt24LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3);
|
||||
static void convertInt24BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3);
|
||||
|
||||
static void convertInt32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
|
||||
static void convertInt32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
|
||||
|
||||
static void convertFloat32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
|
||||
static void convertFloat32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
|
||||
|
||||
//==============================================================================
|
||||
enum DataFormat
|
||||
{
|
||||
int16LE,
|
||||
int16BE,
|
||||
int24LE,
|
||||
int24BE,
|
||||
int32LE,
|
||||
int32BE,
|
||||
float32LE,
|
||||
float32BE,
|
||||
};
|
||||
|
||||
static void convertFloatToFormat (DataFormat destFormat,
|
||||
const float* source, void* dest, int numSamples);
|
||||
|
||||
static void convertFormatToFloat (DataFormat sourceFormat,
|
||||
const void* source, float* dest, int numSamples);
|
||||
|
||||
//==============================================================================
|
||||
static void interleaveSamples (const float** source, float* dest,
|
||||
int numSamples, int numChannels);
|
||||
|
||||
static void deinterleaveSamples (const float* source, float** dest,
|
||||
int numSamples, int numChannels);
|
||||
|
||||
private:
|
||||
AudioDataConverters();
|
||||
JUCE_DECLARE_NON_COPYABLE (AudioDataConverters)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_AUDIODATACONVERTERS_H_INCLUDED
|
||||
|
|
@ -0,0 +1,670 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
AudioSampleBuffer::AudioSampleBuffer() noexcept
|
||||
: numChannels (0), size (0), allocatedBytes (0),
|
||||
channels (static_cast<float**> (preallocatedChannelSpace)),
|
||||
isClear (false)
|
||||
{
|
||||
}
|
||||
|
||||
AudioSampleBuffer::AudioSampleBuffer (const int numChans,
|
||||
const int numSamples) noexcept
|
||||
: numChannels (numChans),
|
||||
size (numSamples)
|
||||
{
|
||||
jassert (numSamples >= 0);
|
||||
jassert (numChans >= 0);
|
||||
|
||||
allocateData();
|
||||
}
|
||||
|
||||
AudioSampleBuffer::AudioSampleBuffer (const AudioSampleBuffer& other) noexcept
|
||||
: numChannels (other.numChannels),
|
||||
size (other.size),
|
||||
allocatedBytes (other.allocatedBytes)
|
||||
{
|
||||
if (allocatedBytes == 0)
|
||||
{
|
||||
allocateChannels (other.channels, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
allocateData();
|
||||
|
||||
if (other.isClear)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
FloatVectorOperations::copy (channels[i], other.channels[i], size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::allocateData()
|
||||
{
|
||||
const size_t channelListSize = sizeof (float*) * (size_t) (numChannels + 1);
|
||||
allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (float) + channelListSize + 32;
|
||||
allocatedData.malloc (allocatedBytes);
|
||||
channels = reinterpret_cast<float**> (allocatedData.getData());
|
||||
|
||||
float* chan = (float*) (allocatedData + channelListSize);
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
{
|
||||
channels[i] = chan;
|
||||
chan += size;
|
||||
}
|
||||
|
||||
channels [numChannels] = nullptr;
|
||||
isClear = false;
|
||||
}
|
||||
|
||||
AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
|
||||
const int numChans,
|
||||
const int numSamples) noexcept
|
||||
: numChannels (numChans),
|
||||
size (numSamples),
|
||||
allocatedBytes (0)
|
||||
{
|
||||
jassert (dataToReferTo != nullptr);
|
||||
jassert (numChans >= 0 && numSamples >= 0);
|
||||
allocateChannels (dataToReferTo, 0);
|
||||
}
|
||||
|
||||
AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
|
||||
const int numChans,
|
||||
const int startSample,
|
||||
const int numSamples) noexcept
|
||||
: numChannels (numChans),
|
||||
size (numSamples),
|
||||
allocatedBytes (0),
|
||||
isClear (false)
|
||||
{
|
||||
jassert (dataToReferTo != nullptr);
|
||||
jassert (numChans >= 0 && startSample >= 0 && numSamples >= 0);
|
||||
allocateChannels (dataToReferTo, startSample);
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo,
|
||||
const int newNumChannels,
|
||||
const int newNumSamples) noexcept
|
||||
{
|
||||
jassert (dataToReferTo != nullptr);
|
||||
jassert (newNumChannels >= 0 && newNumSamples >= 0);
|
||||
|
||||
allocatedBytes = 0;
|
||||
allocatedData.free();
|
||||
|
||||
numChannels = newNumChannels;
|
||||
size = newNumSamples;
|
||||
|
||||
allocateChannels (dataToReferTo, 0);
|
||||
jassert (! isClear);
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int offset)
|
||||
{
|
||||
jassert (offset >= 0);
|
||||
|
||||
// (try to avoid doing a malloc here, as that'll blow up things like Pro-Tools)
|
||||
if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
|
||||
{
|
||||
channels = static_cast<float**> (preallocatedChannelSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
allocatedData.malloc ((size_t) numChannels + 1, sizeof (float*));
|
||||
channels = reinterpret_cast<float**> (allocatedData.getData());
|
||||
}
|
||||
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
{
|
||||
// you have to pass in the same number of valid pointers as numChannels
|
||||
jassert (dataToReferTo[i] != nullptr);
|
||||
|
||||
channels[i] = dataToReferTo[i] + offset;
|
||||
}
|
||||
|
||||
channels [numChannels] = nullptr;
|
||||
isClear = false;
|
||||
}
|
||||
|
||||
AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
setSize (other.getNumChannels(), other.getNumSamples(), false, false, false);
|
||||
|
||||
if (other.isClear)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
isClear = false;
|
||||
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
FloatVectorOperations::copy (channels[i], other.channels[i], size);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
AudioSampleBuffer::~AudioSampleBuffer() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::setSize (const int newNumChannels,
|
||||
const int newNumSamples,
|
||||
const bool keepExistingContent,
|
||||
const bool clearExtraSpace,
|
||||
const bool avoidReallocating) noexcept
|
||||
{
|
||||
jassert (newNumChannels >= 0);
|
||||
jassert (newNumSamples >= 0);
|
||||
|
||||
if (newNumSamples != size || newNumChannels != numChannels)
|
||||
{
|
||||
const size_t allocatedSamplesPerChannel = ((size_t) newNumSamples + 3) & ~3u;
|
||||
const size_t channelListSize = ((sizeof (float*) * (size_t) (newNumChannels + 1)) + 15) & ~15u;
|
||||
const size_t newTotalBytes = ((size_t) newNumChannels * (size_t) allocatedSamplesPerChannel * sizeof (float))
|
||||
+ channelListSize + 32;
|
||||
|
||||
if (keepExistingContent)
|
||||
{
|
||||
HeapBlock<char, true> newData;
|
||||
newData.allocate (newTotalBytes, clearExtraSpace || isClear);
|
||||
|
||||
const size_t numSamplesToCopy = (size_t) jmin (newNumSamples, size);
|
||||
|
||||
float** const newChannels = reinterpret_cast<float**> (newData.getData());
|
||||
float* newChan = reinterpret_cast<float*> (newData + channelListSize);
|
||||
|
||||
for (int j = 0; j < newNumChannels; ++j)
|
||||
{
|
||||
newChannels[j] = newChan;
|
||||
newChan += allocatedSamplesPerChannel;
|
||||
}
|
||||
|
||||
if (! isClear)
|
||||
{
|
||||
const int numChansToCopy = jmin (numChannels, newNumChannels);
|
||||
for (int i = 0; i < numChansToCopy; ++i)
|
||||
FloatVectorOperations::copy (newChannels[i], channels[i], (int) numSamplesToCopy);
|
||||
}
|
||||
|
||||
allocatedData.swapWith (newData);
|
||||
allocatedBytes = newTotalBytes;
|
||||
channels = newChannels;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (avoidReallocating && allocatedBytes >= newTotalBytes)
|
||||
{
|
||||
if (clearExtraSpace || isClear)
|
||||
allocatedData.clear (newTotalBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
allocatedBytes = newTotalBytes;
|
||||
allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear);
|
||||
channels = reinterpret_cast<float**> (allocatedData.getData());
|
||||
}
|
||||
|
||||
float* chan = reinterpret_cast<float*> (allocatedData + channelListSize);
|
||||
for (int i = 0; i < newNumChannels; ++i)
|
||||
{
|
||||
channels[i] = chan;
|
||||
chan += allocatedSamplesPerChannel;
|
||||
}
|
||||
}
|
||||
|
||||
channels [newNumChannels] = 0;
|
||||
size = newNumSamples;
|
||||
numChannels = newNumChannels;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::clear() noexcept
|
||||
{
|
||||
if (! isClear)
|
||||
{
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
FloatVectorOperations::clear (channels[i], size);
|
||||
|
||||
isClear = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::clear (const int startSample,
|
||||
const int numSamples) noexcept
|
||||
{
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
||||
if (! isClear)
|
||||
{
|
||||
if (startSample == 0 && numSamples == size)
|
||||
isClear = true;
|
||||
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
FloatVectorOperations::clear (channels[i] + startSample, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::clear (const int channel,
|
||||
const int startSample,
|
||||
const int numSamples) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
||||
if (! isClear)
|
||||
FloatVectorOperations::clear (channels [channel] + startSample, numSamples);
|
||||
}
|
||||
|
||||
float AudioSampleBuffer::getSample (int channel, int index) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (isPositiveAndBelow (index, size));
|
||||
return *(channels [channel] + index);
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::setSample (int channel, int index, float newValue) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (isPositiveAndBelow (index, size));
|
||||
*(channels [channel] + index) = newValue;
|
||||
isClear = false;
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::addSample (int channel, int index, float valueToAdd) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (isPositiveAndBelow (index, size));
|
||||
*(channels [channel] + index) += valueToAdd;
|
||||
isClear = false;
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::applyGain (const int channel,
|
||||
const int startSample,
|
||||
int numSamples,
|
||||
const float gain) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
||||
if (gain != 1.0f && ! isClear)
|
||||
{
|
||||
float* const d = channels [channel] + startSample;
|
||||
|
||||
if (gain == 0.0f)
|
||||
FloatVectorOperations::clear (d, numSamples);
|
||||
else
|
||||
FloatVectorOperations::multiply (d, gain, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::applyGainRamp (const int channel,
|
||||
const int startSample,
|
||||
int numSamples,
|
||||
float startGain,
|
||||
float endGain) noexcept
|
||||
{
|
||||
if (! isClear)
|
||||
{
|
||||
if (startGain == endGain)
|
||||
{
|
||||
applyGain (channel, startSample, numSamples, startGain);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
||||
const float increment = (endGain - startGain) / numSamples;
|
||||
float* d = channels [channel] + startSample;
|
||||
|
||||
while (--numSamples >= 0)
|
||||
{
|
||||
*d++ *= startGain;
|
||||
startGain += increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::applyGain (int startSample, int numSamples, float gain) noexcept
|
||||
{
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
applyGain (i, startSample, numSamples, gain);
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::applyGain (const float gain) noexcept
|
||||
{
|
||||
applyGain (0, size, gain);
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::applyGainRamp (int startSample, int numSamples,
|
||||
float startGain, float endGain) noexcept
|
||||
{
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
applyGainRamp (i, startSample, numSamples, startGain, endGain);
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::addFrom (const int destChannel,
|
||||
const int destStartSample,
|
||||
const AudioSampleBuffer& source,
|
||||
const int sourceChannel,
|
||||
const int sourceStartSample,
|
||||
int numSamples,
|
||||
const float gain) noexcept
|
||||
{
|
||||
jassert (&source != this || sourceChannel != destChannel);
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
|
||||
jassert (isPositiveAndBelow (sourceChannel, source.numChannels));
|
||||
jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size);
|
||||
|
||||
if (gain != 0.0f && numSamples > 0 && ! source.isClear)
|
||||
{
|
||||
float* const d = channels [destChannel] + destStartSample;
|
||||
const float* const s = source.channels [sourceChannel] + sourceStartSample;
|
||||
|
||||
if (isClear)
|
||||
{
|
||||
isClear = false;
|
||||
|
||||
if (gain != 1.0f)
|
||||
FloatVectorOperations::copyWithMultiply (d, s, gain, numSamples);
|
||||
else
|
||||
FloatVectorOperations::copy (d, s, numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gain != 1.0f)
|
||||
FloatVectorOperations::addWithMultiply (d, s, gain, numSamples);
|
||||
else
|
||||
FloatVectorOperations::add (d, s, numSamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::addFrom (const int destChannel,
|
||||
const int destStartSample,
|
||||
const float* source,
|
||||
int numSamples,
|
||||
const float gain) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
|
||||
jassert (source != nullptr);
|
||||
|
||||
if (gain != 0.0f && numSamples > 0)
|
||||
{
|
||||
float* const d = channels [destChannel] + destStartSample;
|
||||
|
||||
if (isClear)
|
||||
{
|
||||
isClear = false;
|
||||
|
||||
if (gain != 1.0f)
|
||||
FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples);
|
||||
else
|
||||
FloatVectorOperations::copy (d, source, numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gain != 1.0f)
|
||||
FloatVectorOperations::addWithMultiply (d, source, gain, numSamples);
|
||||
else
|
||||
FloatVectorOperations::add (d, source, numSamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::addFromWithRamp (const int destChannel,
|
||||
const int destStartSample,
|
||||
const float* source,
|
||||
int numSamples,
|
||||
float startGain,
|
||||
const float endGain) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
|
||||
jassert (source != nullptr);
|
||||
|
||||
if (startGain == endGain)
|
||||
{
|
||||
addFrom (destChannel, destStartSample, source, numSamples, startGain);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f))
|
||||
{
|
||||
isClear = false;
|
||||
const float increment = (endGain - startGain) / numSamples;
|
||||
float* d = channels [destChannel] + destStartSample;
|
||||
|
||||
while (--numSamples >= 0)
|
||||
{
|
||||
*d++ += startGain * *source++;
|
||||
startGain += increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::copyFrom (const int destChannel,
|
||||
const int destStartSample,
|
||||
const AudioSampleBuffer& source,
|
||||
const int sourceChannel,
|
||||
const int sourceStartSample,
|
||||
int numSamples) noexcept
|
||||
{
|
||||
jassert (&source != this || sourceChannel != destChannel);
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
|
||||
jassert (isPositiveAndBelow (sourceChannel, source.numChannels));
|
||||
jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size);
|
||||
|
||||
if (numSamples > 0)
|
||||
{
|
||||
if (source.isClear)
|
||||
{
|
||||
if (! isClear)
|
||||
FloatVectorOperations::clear (channels [destChannel] + destStartSample, numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
isClear = false;
|
||||
FloatVectorOperations::copy (channels [destChannel] + destStartSample,
|
||||
source.channels [sourceChannel] + sourceStartSample,
|
||||
numSamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::copyFrom (const int destChannel,
|
||||
const int destStartSample,
|
||||
const float* source,
|
||||
int numSamples) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
|
||||
jassert (source != nullptr);
|
||||
|
||||
if (numSamples > 0)
|
||||
{
|
||||
isClear = false;
|
||||
FloatVectorOperations::copy (channels [destChannel] + destStartSample, source, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::copyFrom (const int destChannel,
|
||||
const int destStartSample,
|
||||
const float* source,
|
||||
int numSamples,
|
||||
const float gain) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
|
||||
jassert (source != nullptr);
|
||||
|
||||
if (numSamples > 0)
|
||||
{
|
||||
float* const d = channels [destChannel] + destStartSample;
|
||||
|
||||
if (gain != 1.0f)
|
||||
{
|
||||
if (gain == 0)
|
||||
{
|
||||
if (! isClear)
|
||||
FloatVectorOperations::clear (d, numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
isClear = false;
|
||||
FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isClear = false;
|
||||
FloatVectorOperations::copy (d, source, numSamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::copyFromWithRamp (const int destChannel,
|
||||
const int destStartSample,
|
||||
const float* source,
|
||||
int numSamples,
|
||||
float startGain,
|
||||
float endGain) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
|
||||
jassert (source != nullptr);
|
||||
|
||||
if (startGain == endGain)
|
||||
{
|
||||
copyFrom (destChannel, destStartSample, source, numSamples, startGain);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f))
|
||||
{
|
||||
isClear = false;
|
||||
const float increment = (endGain - startGain) / numSamples;
|
||||
float* d = channels [destChannel] + destStartSample;
|
||||
|
||||
while (--numSamples >= 0)
|
||||
{
|
||||
*d++ = startGain * *source++;
|
||||
startGain += increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::reverse (int channel, int startSample, int numSamples) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
||||
if (! isClear)
|
||||
std::reverse (channels[channel] + startSample,
|
||||
channels[channel] + startSample + numSamples);
|
||||
}
|
||||
|
||||
void AudioSampleBuffer::reverse (int startSample, int numSamples) const noexcept
|
||||
{
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
reverse (i, startSample, numSamples);
|
||||
}
|
||||
|
||||
Range<float> AudioSampleBuffer::findMinMax (const int channel,
|
||||
const int startSample,
|
||||
int numSamples) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
||||
if (isClear)
|
||||
return Range<float>();
|
||||
|
||||
return FloatVectorOperations::findMinAndMax (channels [channel] + startSample, numSamples);
|
||||
}
|
||||
|
||||
float AudioSampleBuffer::getMagnitude (const int channel,
|
||||
const int startSample,
|
||||
const int numSamples) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
||||
if (isClear)
|
||||
return 0.0f;
|
||||
|
||||
const Range<float> r (findMinMax (channel, startSample, numSamples));
|
||||
|
||||
return jmax (r.getStart(), -r.getStart(), r.getEnd(), -r.getEnd());
|
||||
}
|
||||
|
||||
float AudioSampleBuffer::getMagnitude (int startSample, int numSamples) const noexcept
|
||||
{
|
||||
float mag = 0.0f;
|
||||
|
||||
if (! isClear)
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
mag = jmax (mag, getMagnitude (i, startSample, numSamples));
|
||||
|
||||
return mag;
|
||||
}
|
||||
|
||||
float AudioSampleBuffer::getRMSLevel (const int channel,
|
||||
const int startSample,
|
||||
const int numSamples) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
||||
if (numSamples <= 0 || channel < 0 || channel >= numChannels || isClear)
|
||||
return 0.0f;
|
||||
|
||||
const float* const data = channels [channel] + startSample;
|
||||
double sum = 0.0;
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
const float sample = data [i];
|
||||
sum += sample * sample;
|
||||
}
|
||||
|
||||
return (float) std::sqrt (sum / numSamples);
|
||||
}
|
||||
|
|
@ -0,0 +1,526 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED
|
||||
#define JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A multi-channel buffer of 32-bit floating point audio samples.
|
||||
|
||||
*/
|
||||
class JUCE_API AudioSampleBuffer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty buffer with 0 channels and 0 length. */
|
||||
AudioSampleBuffer() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a buffer with a specified number of channels and samples.
|
||||
|
||||
The contents of the buffer will initially be undefined, so use clear() to
|
||||
set all the samples to zero.
|
||||
|
||||
The buffer will allocate its memory internally, and this will be released
|
||||
when the buffer is deleted. If the memory can't be allocated, this will
|
||||
throw a std::bad_alloc exception.
|
||||
*/
|
||||
AudioSampleBuffer (int numChannels,
|
||||
int numSamples) noexcept;
|
||||
|
||||
/** Creates a buffer using a pre-allocated block of memory.
|
||||
|
||||
Note that if the buffer is resized or its number of channels is changed, it
|
||||
will re-allocate memory internally and copy the existing data to this new area,
|
||||
so it will then stop directly addressing this memory.
|
||||
|
||||
@param dataToReferTo a pre-allocated array containing pointers to the data
|
||||
for each channel that should be used by this buffer. The
|
||||
buffer will only refer to this memory, it won't try to delete
|
||||
it when the buffer is deleted or resized.
|
||||
@param numChannels the number of channels to use - this must correspond to the
|
||||
number of elements in the array passed in
|
||||
@param numSamples the number of samples to use - this must correspond to the
|
||||
size of the arrays passed in
|
||||
*/
|
||||
AudioSampleBuffer (float* const* dataToReferTo,
|
||||
int numChannels,
|
||||
int numSamples) noexcept;
|
||||
|
||||
/** Creates a buffer using a pre-allocated block of memory.
|
||||
|
||||
Note that if the buffer is resized or its number of channels is changed, it
|
||||
will re-allocate memory internally and copy the existing data to this new area,
|
||||
so it will then stop directly addressing this memory.
|
||||
|
||||
@param dataToReferTo a pre-allocated array containing pointers to the data
|
||||
for each channel that should be used by this buffer. The
|
||||
buffer will only refer to this memory, it won't try to delete
|
||||
it when the buffer is deleted or resized.
|
||||
@param numChannels the number of channels to use - this must correspond to the
|
||||
number of elements in the array passed in
|
||||
@param startSample the offset within the arrays at which the data begins
|
||||
@param numSamples the number of samples to use - this must correspond to the
|
||||
size of the arrays passed in
|
||||
*/
|
||||
AudioSampleBuffer (float* const* dataToReferTo,
|
||||
int numChannels,
|
||||
int startSample,
|
||||
int numSamples) noexcept;
|
||||
|
||||
/** Copies another buffer.
|
||||
|
||||
This buffer will make its own copy of the other's data, unless the buffer was created
|
||||
using an external data buffer, in which case boths buffers will just point to the same
|
||||
shared block of data.
|
||||
*/
|
||||
AudioSampleBuffer (const AudioSampleBuffer&) noexcept;
|
||||
|
||||
/** Copies another buffer onto this one.
|
||||
This buffer's size will be changed to that of the other buffer.
|
||||
*/
|
||||
AudioSampleBuffer& operator= (const AudioSampleBuffer&) noexcept;
|
||||
|
||||
/** Destructor.
|
||||
This will free any memory allocated by the buffer.
|
||||
*/
|
||||
~AudioSampleBuffer() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of channels of audio data that this buffer contains.
|
||||
@see getSampleData
|
||||
*/
|
||||
int getNumChannels() const noexcept { return numChannels; }
|
||||
|
||||
/** Returns the number of samples allocated in each of the buffer's channels.
|
||||
@see getSampleData
|
||||
*/
|
||||
int getNumSamples() const noexcept { return size; }
|
||||
|
||||
/** Returns a pointer to an array of read-only samples in one of the buffer's channels.
|
||||
For speed, this doesn't check whether the channel number is out of range,
|
||||
so be careful when using it!
|
||||
If you need to write to the data, do NOT call this method and const_cast the
|
||||
result! Instead, you must call getWritePointer so that the buffer knows you're
|
||||
planning on modifying the data.
|
||||
*/
|
||||
const float* getReadPointer (int channelNumber) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channelNumber, numChannels));
|
||||
return channels [channelNumber];
|
||||
}
|
||||
|
||||
/** Returns a pointer to an array of read-only samples in one of the buffer's channels.
|
||||
For speed, this doesn't check whether the channel number or index are out of range,
|
||||
so be careful when using it!
|
||||
If you need to write to the data, do NOT call this method and const_cast the
|
||||
result! Instead, you must call getWritePointer so that the buffer knows you're
|
||||
planning on modifying the data.
|
||||
*/
|
||||
const float* getReadPointer (int channelNumber, int sampleIndex) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channelNumber, numChannels));
|
||||
jassert (isPositiveAndBelow (sampleIndex, size));
|
||||
return channels [channelNumber] + sampleIndex;
|
||||
}
|
||||
|
||||
/** Returns a writeable pointer to one of the buffer's channels.
|
||||
For speed, this doesn't check whether the channel number is out of range,
|
||||
so be careful when using it!
|
||||
Note that if you're not planning on writing to the data, you should always
|
||||
use getReadPointer instead.
|
||||
*/
|
||||
float* getWritePointer (int channelNumber) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channelNumber, numChannels));
|
||||
isClear = false;
|
||||
return channels [channelNumber];
|
||||
}
|
||||
|
||||
/** Returns a writeable pointer to one of the buffer's channels.
|
||||
For speed, this doesn't check whether the channel number or index are out of range,
|
||||
so be careful when using it!
|
||||
Note that if you're not planning on writing to the data, you should
|
||||
use getReadPointer instead.
|
||||
*/
|
||||
float* getWritePointer (int channelNumber, int sampleIndex) noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channelNumber, numChannels));
|
||||
jassert (isPositiveAndBelow (sampleIndex, size));
|
||||
isClear = false;
|
||||
return channels [channelNumber] + sampleIndex;
|
||||
}
|
||||
|
||||
/** Returns an array of pointers to the channels in the buffer.
|
||||
|
||||
Don't modify any of the pointers that are returned, and bear in mind that
|
||||
these will become invalid if the buffer is resized.
|
||||
*/
|
||||
const float** getArrayOfReadPointers() const noexcept { return const_cast<const float**> (channels); }
|
||||
|
||||
/** Returns an array of pointers to the channels in the buffer.
|
||||
|
||||
Don't modify any of the pointers that are returned, and bear in mind that
|
||||
these will become invalid if the buffer is resized.
|
||||
*/
|
||||
float** getArrayOfWritePointers() noexcept { isClear = false; return channels; }
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the buffer's size or number of channels.
|
||||
|
||||
This can expand or contract the buffer's length, and add or remove channels.
|
||||
|
||||
If keepExistingContent is true, it will try to preserve as much of the
|
||||
old data as it can in the new buffer.
|
||||
|
||||
If clearExtraSpace is true, then any extra channels or space that is
|
||||
allocated will be also be cleared. If false, then this space is left
|
||||
uninitialised.
|
||||
|
||||
If avoidReallocating is true, then changing the buffer's size won't reduce the
|
||||
amount of memory that is currently allocated (but it will still increase it if
|
||||
the new size is bigger than the amount it currently has). If this is false, then
|
||||
a new allocation will be done so that the buffer uses takes up the minimum amount
|
||||
of memory that it needs.
|
||||
|
||||
If the required memory can't be allocated, this will throw a std::bad_alloc exception.
|
||||
*/
|
||||
void setSize (int newNumChannels,
|
||||
int newNumSamples,
|
||||
bool keepExistingContent = false,
|
||||
bool clearExtraSpace = false,
|
||||
bool avoidReallocating = false) noexcept;
|
||||
|
||||
|
||||
/** Makes this buffer point to a pre-allocated set of channel data arrays.
|
||||
|
||||
There's also a constructor that lets you specify arrays like this, but this
|
||||
lets you change the channels dynamically.
|
||||
|
||||
Note that if the buffer is resized or its number of channels is changed, it
|
||||
will re-allocate memory internally and copy the existing data to this new area,
|
||||
so it will then stop directly addressing this memory.
|
||||
|
||||
@param dataToReferTo a pre-allocated array containing pointers to the data
|
||||
for each channel that should be used by this buffer. The
|
||||
buffer will only refer to this memory, it won't try to delete
|
||||
it when the buffer is deleted or resized.
|
||||
@param numChannels the number of channels to use - this must correspond to the
|
||||
number of elements in the array passed in
|
||||
@param numSamples the number of samples to use - this must correspond to the
|
||||
size of the arrays passed in
|
||||
*/
|
||||
void setDataToReferTo (float** dataToReferTo,
|
||||
int numChannels,
|
||||
int numSamples) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Clears all the samples in all channels. */
|
||||
void clear() noexcept;
|
||||
|
||||
/** Clears a specified region of all the channels.
|
||||
|
||||
For speed, this doesn't check whether the channel and sample number
|
||||
are in-range, so be careful!
|
||||
*/
|
||||
void clear (int startSample,
|
||||
int numSamples) noexcept;
|
||||
|
||||
/** Clears a specified region of just one channel.
|
||||
|
||||
For speed, this doesn't check whether the channel and sample number
|
||||
are in-range, so be careful!
|
||||
*/
|
||||
void clear (int channel,
|
||||
int startSample,
|
||||
int numSamples) noexcept;
|
||||
|
||||
/** Returns true if the buffer has been entirely cleared.
|
||||
Note that this does not actually measure the contents of the buffer - it simply
|
||||
returns a flag that is set when the buffer is cleared, and which is reset whenever
|
||||
functions like getWritePointer() are invoked. That means the method does not take
|
||||
any time, but it may return false negatives when in fact the buffer is still empty.
|
||||
*/
|
||||
bool hasBeenCleared() const noexcept { return isClear; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a sample from the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
float getSample (int channel, int sampleIndex) const noexcept;
|
||||
|
||||
/** Sets a sample in the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
void setSample (int destChannel, int destSample, float newValue) noexcept;
|
||||
|
||||
/** Adds a value to a sample in the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
void addSample (int destChannel, int destSample, float valueToAdd) noexcept;
|
||||
|
||||
/** Applies a gain multiple to a region of one channel.
|
||||
|
||||
For speed, this doesn't check whether the channel and sample number
|
||||
are in-range, so be careful!
|
||||
*/
|
||||
void applyGain (int channel,
|
||||
int startSample,
|
||||
int numSamples,
|
||||
float gain) noexcept;
|
||||
|
||||
/** Applies a gain multiple to a region of all the channels.
|
||||
|
||||
For speed, this doesn't check whether the sample numbers
|
||||
are in-range, so be careful!
|
||||
*/
|
||||
void applyGain (int startSample,
|
||||
int numSamples,
|
||||
float gain) noexcept;
|
||||
|
||||
/** Applies a gain multiple to all the audio data. */
|
||||
void applyGain (float gain) noexcept;
|
||||
|
||||
/** Applies a range of gains to a region of a channel.
|
||||
|
||||
The gain that is applied to each sample will vary from
|
||||
startGain on the first sample to endGain on the last Sample,
|
||||
so it can be used to do basic fades.
|
||||
|
||||
For speed, this doesn't check whether the sample numbers
|
||||
are in-range, so be careful!
|
||||
*/
|
||||
void applyGainRamp (int channel,
|
||||
int startSample,
|
||||
int numSamples,
|
||||
float startGain,
|
||||
float endGain) noexcept;
|
||||
|
||||
/** Applies a range of gains to a region of all channels.
|
||||
|
||||
The gain that is applied to each sample will vary from
|
||||
startGain on the first sample to endGain on the last Sample,
|
||||
so it can be used to do basic fades.
|
||||
|
||||
For speed, this doesn't check whether the sample numbers
|
||||
are in-range, so be careful!
|
||||
*/
|
||||
void applyGainRamp (int startSample,
|
||||
int numSamples,
|
||||
float startGain,
|
||||
float endGain) noexcept;
|
||||
|
||||
/** Adds samples from another buffer to this one.
|
||||
|
||||
@param destChannel the channel within this buffer to add the samples to
|
||||
@param destStartSample the start sample within this buffer's channel
|
||||
@param source the source buffer to add from
|
||||
@param sourceChannel the channel within the source buffer to read from
|
||||
@param sourceStartSample the offset within the source buffer's channel to start reading samples from
|
||||
@param numSamples the number of samples to process
|
||||
@param gainToApplyToSource an optional gain to apply to the source samples before they are
|
||||
added to this buffer's samples
|
||||
|
||||
@see copyFrom
|
||||
*/
|
||||
void addFrom (int destChannel,
|
||||
int destStartSample,
|
||||
const AudioSampleBuffer& source,
|
||||
int sourceChannel,
|
||||
int sourceStartSample,
|
||||
int numSamples,
|
||||
float gainToApplyToSource = 1.0f) noexcept;
|
||||
|
||||
/** Adds samples from an array of floats to one of the channels.
|
||||
|
||||
@param destChannel the channel within this buffer to add the samples to
|
||||
@param destStartSample the start sample within this buffer's channel
|
||||
@param source the source data to use
|
||||
@param numSamples the number of samples to process
|
||||
@param gainToApplyToSource an optional gain to apply to the source samples before they are
|
||||
added to this buffer's samples
|
||||
|
||||
@see copyFrom
|
||||
*/
|
||||
void addFrom (int destChannel,
|
||||
int destStartSample,
|
||||
const float* source,
|
||||
int numSamples,
|
||||
float gainToApplyToSource = 1.0f) noexcept;
|
||||
|
||||
/** Adds samples from an array of floats, applying a gain ramp to them.
|
||||
|
||||
@param destChannel the channel within this buffer to add the samples to
|
||||
@param destStartSample the start sample within this buffer's channel
|
||||
@param source the source data to use
|
||||
@param numSamples the number of samples to process
|
||||
@param startGain the gain to apply to the first sample (this is multiplied with
|
||||
the source samples before they are added to this buffer)
|
||||
@param endGain the gain to apply to the final sample. The gain is linearly
|
||||
interpolated between the first and last samples.
|
||||
*/
|
||||
void addFromWithRamp (int destChannel,
|
||||
int destStartSample,
|
||||
const float* source,
|
||||
int numSamples,
|
||||
float startGain,
|
||||
float endGain) noexcept;
|
||||
|
||||
/** Copies samples from another buffer to this one.
|
||||
|
||||
@param destChannel the channel within this buffer to copy the samples to
|
||||
@param destStartSample the start sample within this buffer's channel
|
||||
@param source the source buffer to read from
|
||||
@param sourceChannel the channel within the source buffer to read from
|
||||
@param sourceStartSample the offset within the source buffer's channel to start reading samples from
|
||||
@param numSamples the number of samples to process
|
||||
|
||||
@see addFrom
|
||||
*/
|
||||
void copyFrom (int destChannel,
|
||||
int destStartSample,
|
||||
const AudioSampleBuffer& source,
|
||||
int sourceChannel,
|
||||
int sourceStartSample,
|
||||
int numSamples) noexcept;
|
||||
|
||||
/** Copies samples from an array of floats into one of the channels.
|
||||
|
||||
@param destChannel the channel within this buffer to copy the samples to
|
||||
@param destStartSample the start sample within this buffer's channel
|
||||
@param source the source buffer to read from
|
||||
@param numSamples the number of samples to process
|
||||
|
||||
@see addFrom
|
||||
*/
|
||||
void copyFrom (int destChannel,
|
||||
int destStartSample,
|
||||
const float* source,
|
||||
int numSamples) noexcept;
|
||||
|
||||
/** Copies samples from an array of floats into one of the channels, applying a gain to it.
|
||||
|
||||
@param destChannel the channel within this buffer to copy the samples to
|
||||
@param destStartSample the start sample within this buffer's channel
|
||||
@param source the source buffer to read from
|
||||
@param numSamples the number of samples to process
|
||||
@param gain the gain to apply
|
||||
|
||||
@see addFrom
|
||||
*/
|
||||
void copyFrom (int destChannel,
|
||||
int destStartSample,
|
||||
const float* source,
|
||||
int numSamples,
|
||||
float gain) noexcept;
|
||||
|
||||
/** Copies samples from an array of floats into one of the channels, applying a gain ramp.
|
||||
|
||||
@param destChannel the channel within this buffer to copy the samples to
|
||||
@param destStartSample the start sample within this buffer's channel
|
||||
@param source the source buffer to read from
|
||||
@param numSamples the number of samples to process
|
||||
@param startGain the gain to apply to the first sample (this is multiplied with
|
||||
the source samples before they are copied to this buffer)
|
||||
@param endGain the gain to apply to the final sample. The gain is linearly
|
||||
interpolated between the first and last samples.
|
||||
|
||||
@see addFrom
|
||||
*/
|
||||
void copyFromWithRamp (int destChannel,
|
||||
int destStartSample,
|
||||
const float* source,
|
||||
int numSamples,
|
||||
float startGain,
|
||||
float endGain) noexcept;
|
||||
|
||||
|
||||
/** Returns a Range indicating the lowest and highest sample values in a given section.
|
||||
|
||||
@param channel the channel to read from
|
||||
@param startSample the start sample within the channel
|
||||
@param numSamples the number of samples to check
|
||||
*/
|
||||
Range<float> findMinMax (int channel,
|
||||
int startSample,
|
||||
int numSamples) const noexcept;
|
||||
|
||||
/** Finds the highest absolute sample value within a region of a channel. */
|
||||
float getMagnitude (int channel,
|
||||
int startSample,
|
||||
int numSamples) const noexcept;
|
||||
|
||||
/** Finds the highest absolute sample value within a region on all channels. */
|
||||
float getMagnitude (int startSample,
|
||||
int numSamples) const noexcept;
|
||||
|
||||
/** Returns the root mean squared level for a region of a channel. */
|
||||
float getRMSLevel (int channel,
|
||||
int startSample,
|
||||
int numSamples) const noexcept;
|
||||
|
||||
/** Reverses a part of a channel. */
|
||||
void reverse (int channel, int startSample, int numSamples) const noexcept;
|
||||
|
||||
/** Reverses a part of the buffer. */
|
||||
void reverse (int startSample, int numSamples) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// Note that these methods have now been replaced by getReadPointer() and getWritePointer()
|
||||
JUCE_DEPRECATED_WITH_BODY (const float* getSampleData (int channel) const, { return getReadPointer (channel); })
|
||||
JUCE_DEPRECATED_WITH_BODY (const float* getSampleData (int channel, int index) const, { return getReadPointer (channel, index); })
|
||||
JUCE_DEPRECATED_WITH_BODY (float* getSampleData (int channel), { return getWritePointer (channel); })
|
||||
JUCE_DEPRECATED_WITH_BODY (float* getSampleData (int channel, int index), { return getWritePointer (channel, index); })
|
||||
|
||||
// These have been replaced by getArrayOfReadPointers() and getArrayOfWritePointers()
|
||||
JUCE_DEPRECATED_WITH_BODY (const float** getArrayOfChannels() const, { return getArrayOfReadPointers(); })
|
||||
JUCE_DEPRECATED_WITH_BODY (float** getArrayOfChannels(), { return getArrayOfWritePointers(); })
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int numChannels, size;
|
||||
size_t allocatedBytes;
|
||||
float** channels;
|
||||
HeapBlock<char, true> allocatedData;
|
||||
float* preallocatedChannelSpace [32];
|
||||
bool isClear;
|
||||
|
||||
void allocateData();
|
||||
void allocateChannels (float* const*, int offset);
|
||||
|
||||
JUCE_LEAK_DETECTOR (AudioSampleBuffer)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED
|
||||
|
|
@ -0,0 +1,893 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace FloatVectorHelpers
|
||||
{
|
||||
#define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest));
|
||||
#define JUCE_INCREMENT_SRC1_SRC2_DEST dest += (16 / sizeof (*dest)); src1 += (16 / sizeof (*dest)); src2 += (16 / sizeof (*dest));
|
||||
#define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest));
|
||||
|
||||
#if JUCE_USE_SSE_INTRINSICS
|
||||
static bool sse2Present = false;
|
||||
|
||||
static bool isSSE2Available() noexcept
|
||||
{
|
||||
if (sse2Present)
|
||||
return true;
|
||||
|
||||
sse2Present = SystemStats::hasSSE2();
|
||||
return sse2Present;
|
||||
}
|
||||
|
||||
inline static bool isAligned (const void* p) noexcept
|
||||
{
|
||||
return (((pointer_sized_int) p) & 15) == 0;
|
||||
}
|
||||
|
||||
struct BasicOps32
|
||||
{
|
||||
typedef float Type;
|
||||
typedef __m128 ParallelType;
|
||||
enum { numParallel = 4 };
|
||||
|
||||
static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_ps (&v); }
|
||||
static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_ps (v); }
|
||||
static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_ps (v); }
|
||||
static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_ps (dest, a); }
|
||||
static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_ps (dest, a); }
|
||||
|
||||
static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_ps (a, b); }
|
||||
static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_ps (a, b); }
|
||||
static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_ps (a, b); }
|
||||
static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_ps (a, b); }
|
||||
static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_ps (a, b); }
|
||||
|
||||
static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); }
|
||||
static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); }
|
||||
};
|
||||
|
||||
struct BasicOps64
|
||||
{
|
||||
typedef double Type;
|
||||
typedef __m128d ParallelType;
|
||||
enum { numParallel = 2 };
|
||||
|
||||
static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_pd (&v); }
|
||||
static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_pd (v); }
|
||||
static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_pd (v); }
|
||||
static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_pd (dest, a); }
|
||||
static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_pd (dest, a); }
|
||||
|
||||
static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_pd (a, b); }
|
||||
static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_pd (a, b); }
|
||||
static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_pd (a, b); }
|
||||
static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_pd (a, b); }
|
||||
static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_pd (a, b); }
|
||||
|
||||
static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1]); }
|
||||
static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); }
|
||||
};
|
||||
|
||||
#define JUCE_BEGIN_VEC_OP \
|
||||
typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \
|
||||
if (FloatVectorHelpers::isSSE2Available()) \
|
||||
{ \
|
||||
const int numLongOps = num / Mode::numParallel;
|
||||
|
||||
#define JUCE_FINISH_VEC_OP(normalOp) \
|
||||
num &= (Mode::numParallel - 1); \
|
||||
if (num == 0) return; \
|
||||
} \
|
||||
for (int i = 0; i < num; ++i) normalOp;
|
||||
|
||||
#define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \
|
||||
JUCE_BEGIN_VEC_OP \
|
||||
setupOp \
|
||||
if (FloatVectorHelpers::isAligned (dest)) JUCE_VEC_LOOP (vecOp, dummy, Mode::loadA, Mode::storeA, locals, JUCE_INCREMENT_DEST) \
|
||||
else JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \
|
||||
JUCE_FINISH_VEC_OP (normalOp)
|
||||
|
||||
#define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
|
||||
JUCE_BEGIN_VEC_OP \
|
||||
setupOp \
|
||||
if (FloatVectorHelpers::isAligned (dest)) \
|
||||
{ \
|
||||
if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \
|
||||
else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \
|
||||
}\
|
||||
else \
|
||||
{ \
|
||||
if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \
|
||||
else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
|
||||
} \
|
||||
JUCE_FINISH_VEC_OP (normalOp)
|
||||
|
||||
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \
|
||||
JUCE_BEGIN_VEC_OP \
|
||||
setupOp \
|
||||
{ \
|
||||
Mode::ParallelType (&loadSrc1) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src1) ? Mode::loadA : Mode::loadU; \
|
||||
Mode::ParallelType (&loadSrc2) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src2) ? Mode::loadA : Mode::loadU; \
|
||||
void (&storeDst) (Mode::Type* dest, Mode::ParallelType a) = FloatVectorHelpers::isAligned (dest) ? Mode::storeA : Mode::storeU; \
|
||||
JUCE_VEC_LOOP_TWO_SOURCES (vecOp, loadSrc1, loadSrc2, storeDst, locals, increment); \
|
||||
} \
|
||||
JUCE_FINISH_VEC_OP (normalOp)
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_USE_ARM_NEON
|
||||
|
||||
struct BasicOps32
|
||||
{
|
||||
typedef float Type;
|
||||
typedef float32x4_t ParallelType;
|
||||
enum { numParallel = 4 };
|
||||
|
||||
static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); }
|
||||
static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); }
|
||||
static forcedinline ParallelType loadU (const Type* v) noexcept { return vld1q_f32 (v); }
|
||||
static forcedinline void storeA (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); }
|
||||
static forcedinline void storeU (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); }
|
||||
|
||||
static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return vaddq_f32 (a, b); }
|
||||
static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return vsubq_f32 (a, b); }
|
||||
static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return vmulq_f32 (a, b); }
|
||||
static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return vmaxq_f32 (a, b); }
|
||||
static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return vminq_f32 (a, b); }
|
||||
|
||||
static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); }
|
||||
static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); }
|
||||
};
|
||||
|
||||
struct BasicOps64
|
||||
{
|
||||
typedef double Type;
|
||||
typedef double ParallelType;
|
||||
enum { numParallel = 1 };
|
||||
|
||||
static forcedinline ParallelType load1 (Type v) noexcept { return v; }
|
||||
static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; }
|
||||
static forcedinline ParallelType loadU (const Type* v) noexcept { return *v; }
|
||||
static forcedinline void storeA (Type* dest, ParallelType a) noexcept { *dest = a; }
|
||||
static forcedinline void storeU (Type* dest, ParallelType a) noexcept { *dest = a; }
|
||||
|
||||
static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return a + b; }
|
||||
static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return a - b; }
|
||||
static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return a * b; }
|
||||
static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return jmax (a, b); }
|
||||
static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return jmin (a, b); }
|
||||
|
||||
static forcedinline Type max (ParallelType a) noexcept { return a; }
|
||||
static forcedinline Type min (ParallelType a) noexcept { return a; }
|
||||
};
|
||||
|
||||
#define JUCE_BEGIN_VEC_OP \
|
||||
typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \
|
||||
if (Mode::numParallel > 1) \
|
||||
{ \
|
||||
const int numLongOps = num / Mode::numParallel;
|
||||
|
||||
#define JUCE_FINISH_VEC_OP(normalOp) \
|
||||
num &= (Mode::numParallel - 1); \
|
||||
if (num == 0) return; \
|
||||
} \
|
||||
for (int i = 0; i < num; ++i) normalOp;
|
||||
|
||||
#define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \
|
||||
JUCE_BEGIN_VEC_OP \
|
||||
setupOp \
|
||||
JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \
|
||||
JUCE_FINISH_VEC_OP (normalOp)
|
||||
|
||||
#define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
|
||||
JUCE_BEGIN_VEC_OP \
|
||||
setupOp \
|
||||
JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
|
||||
JUCE_FINISH_VEC_OP (normalOp)
|
||||
|
||||
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \
|
||||
JUCE_BEGIN_VEC_OP \
|
||||
setupOp \
|
||||
JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
|
||||
JUCE_FINISH_VEC_OP (normalOp)
|
||||
|
||||
//==============================================================================
|
||||
#else
|
||||
#define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \
|
||||
for (int i = 0; i < num; ++i) normalOp;
|
||||
|
||||
#define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
|
||||
for (int i = 0; i < num; ++i) normalOp;
|
||||
|
||||
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \
|
||||
for (int i = 0; i < num; ++i) normalOp;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#define JUCE_VEC_LOOP(vecOp, srcLoad, dstLoad, dstStore, locals, increment) \
|
||||
for (int i = 0; i < numLongOps; ++i) \
|
||||
{ \
|
||||
locals (srcLoad, dstLoad); \
|
||||
dstStore (dest, vecOp); \
|
||||
increment; \
|
||||
}
|
||||
|
||||
#define JUCE_VEC_LOOP_TWO_SOURCES(vecOp, src1Load, src2Load, dstStore, locals, increment) \
|
||||
for (int i = 0; i < numLongOps; ++i) \
|
||||
{ \
|
||||
locals (src1Load, src2Load); \
|
||||
dstStore (dest, vecOp); \
|
||||
increment; \
|
||||
}
|
||||
|
||||
#define JUCE_LOAD_NONE(srcLoad, dstLoad)
|
||||
#define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest);
|
||||
#define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src);
|
||||
#define JUCE_LOAD_SRC1_SRC2(src1Load, src2Load) const Mode::ParallelType s1 = src1Load (src1), s2 = src2Load (src2);
|
||||
#define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src);
|
||||
|
||||
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
|
||||
template<int typeSize> struct ModeType { typedef BasicOps32 Mode; };
|
||||
template<> struct ModeType<8> { typedef BasicOps64 Mode; };
|
||||
|
||||
template <typename Mode>
|
||||
struct MinMax
|
||||
{
|
||||
typedef typename Mode::Type Type;
|
||||
typedef typename Mode::ParallelType ParallelType;
|
||||
|
||||
static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept
|
||||
{
|
||||
int numLongOps = num / Mode::numParallel;
|
||||
|
||||
#if JUCE_USE_SSE_INTRINSICS
|
||||
if (numLongOps > 1 && isSSE2Available())
|
||||
#else
|
||||
if (numLongOps > 1)
|
||||
#endif
|
||||
{
|
||||
ParallelType val;
|
||||
|
||||
#if ! JUCE_USE_ARM_NEON
|
||||
if (isAligned (src))
|
||||
{
|
||||
val = Mode::loadA (src);
|
||||
|
||||
if (isMinimum)
|
||||
{
|
||||
while (--numLongOps > 0)
|
||||
{
|
||||
src += Mode::numParallel;
|
||||
val = Mode::min (val, Mode::loadA (src));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (--numLongOps > 0)
|
||||
{
|
||||
src += Mode::numParallel;
|
||||
val = Mode::max (val, Mode::loadA (src));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
val = Mode::loadU (src);
|
||||
|
||||
if (isMinimum)
|
||||
{
|
||||
while (--numLongOps > 0)
|
||||
{
|
||||
src += Mode::numParallel;
|
||||
val = Mode::min (val, Mode::loadU (src));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (--numLongOps > 0)
|
||||
{
|
||||
src += Mode::numParallel;
|
||||
val = Mode::max (val, Mode::loadU (src));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type result = isMinimum ? Mode::min (val)
|
||||
: Mode::max (val);
|
||||
|
||||
num &= (Mode::numParallel - 1);
|
||||
src += Mode::numParallel;
|
||||
|
||||
for (int i = 0; i < num; ++i)
|
||||
result = isMinimum ? jmin (result, src[i])
|
||||
: jmax (result, src[i]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return isMinimum ? juce::findMinimum (src, num)
|
||||
: juce::findMaximum (src, num);
|
||||
}
|
||||
|
||||
static Range<Type> findMinAndMax (const Type* src, int num) noexcept
|
||||
{
|
||||
int numLongOps = num / Mode::numParallel;
|
||||
|
||||
#if JUCE_USE_SSE_INTRINSICS
|
||||
if (numLongOps > 1 && isSSE2Available())
|
||||
#else
|
||||
if (numLongOps > 1)
|
||||
#endif
|
||||
{
|
||||
ParallelType mn, mx;
|
||||
|
||||
#if ! JUCE_USE_ARM_NEON
|
||||
if (isAligned (src))
|
||||
{
|
||||
mn = Mode::loadA (src);
|
||||
mx = mn;
|
||||
|
||||
while (--numLongOps > 0)
|
||||
{
|
||||
src += Mode::numParallel;
|
||||
const ParallelType v = Mode::loadA (src);
|
||||
mn = Mode::min (mn, v);
|
||||
mx = Mode::max (mx, v);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
mn = Mode::loadU (src);
|
||||
mx = mn;
|
||||
|
||||
while (--numLongOps > 0)
|
||||
{
|
||||
src += Mode::numParallel;
|
||||
const ParallelType v = Mode::loadU (src);
|
||||
mn = Mode::min (mn, v);
|
||||
mx = Mode::max (mx, v);
|
||||
}
|
||||
}
|
||||
|
||||
Range<Type> result (Mode::min (mn),
|
||||
Mode::max (mx));
|
||||
|
||||
num &= (Mode::numParallel - 1);
|
||||
src += Mode::numParallel;
|
||||
|
||||
for (int i = 0; i < num; ++i)
|
||||
result = result.getUnionWith (src[i]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return Range<Type>::findMinAndMax (src, num);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vclr (dest, 1, (size_t) num);
|
||||
#else
|
||||
zeromem (dest, num * sizeof (float));
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vclrD (dest, 1, (size_t) num);
|
||||
#else
|
||||
zeromem (dest, num * sizeof (double));
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vfill (&valueToFill, dest, 1, (size_t) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE,
|
||||
const Mode::ParallelType val = Mode::load1 (valueToFill);)
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::fill (double* dest, double valueToFill, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vfillD (&valueToFill, dest, 1, (size_t) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE,
|
||||
const Mode::ParallelType val = Mode::load1 (valueToFill);)
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::copy (float* dest, const float* src, int num) noexcept
|
||||
{
|
||||
memcpy (dest, src, (size_t) num * sizeof (float));
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept
|
||||
{
|
||||
memcpy (dest, src, (size_t) num * sizeof (double));
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
|
||||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
|
||||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept
|
||||
{
|
||||
JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST,
|
||||
const Mode::ParallelType amountToAdd = Mode::load1 (amount);)
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept
|
||||
{
|
||||
JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST,
|
||||
const Mode::ParallelType amountToAdd = Mode::load1 (amount);)
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float* src, float amount, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsadd (src, 1, &amount, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s),
|
||||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType am = Mode::load1 (amount);)
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double* src, double amount, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsaddD (src, 1, &amount, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s),
|
||||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType am = Mode::load1 (amount);)
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src1, const float* src2, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src1, const double* src2, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src1, const float* src2, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src1, const double* src2, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept
|
||||
{
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)),
|
||||
JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept
|
||||
{
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)),
|
||||
JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src1, const float* src2, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src1, const double* src2, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, float multiplier, int num) noexcept
|
||||
{
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
|
||||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, double multiplier, int num) noexcept
|
||||
{
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
|
||||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
}
|
||||
|
||||
void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
copyWithMultiply (dest, src, -1.0f, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FloatVectorOperations::negate (double* dest, const double* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_VDSP_FRAMEWORK
|
||||
vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num);
|
||||
#else
|
||||
copyWithMultiply (dest, src, -1.0f, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_ARM_NEON
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier,
|
||||
vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier),
|
||||
JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, )
|
||||
#else
|
||||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier,
|
||||
Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))),
|
||||
JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST,
|
||||
const Mode::ParallelType mult = Mode::load1 (multiplier);)
|
||||
#endif
|
||||
}
|
||||
|
||||
Range<float> JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
|
||||
return FloatVectorHelpers::MinMax<FloatVectorHelpers::BasicOps32>::findMinAndMax (src, num);
|
||||
#else
|
||||
return Range<float>::findMinAndMax (src, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
Range<double> JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const double* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
|
||||
return FloatVectorHelpers::MinMax<FloatVectorHelpers::BasicOps64>::findMinAndMax (src, num);
|
||||
#else
|
||||
return Range<double>::findMinAndMax (src, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
|
||||
return FloatVectorHelpers::MinMax<FloatVectorHelpers::BasicOps32>::findMinOrMax (src, num, true);
|
||||
#else
|
||||
return juce::findMinimum (src, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
double JUCE_CALLTYPE FloatVectorOperations::findMinimum (const double* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
|
||||
return FloatVectorHelpers::MinMax<FloatVectorHelpers::BasicOps64>::findMinOrMax (src, num, true);
|
||||
#else
|
||||
return juce::findMinimum (src, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
|
||||
return FloatVectorHelpers::MinMax<FloatVectorHelpers::BasicOps32>::findMinOrMax (src, num, false);
|
||||
#else
|
||||
return juce::findMaximum (src, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int num) noexcept
|
||||
{
|
||||
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
|
||||
return FloatVectorHelpers::MinMax<FloatVectorHelpers::BasicOps64>::findMinOrMax (src, num, false);
|
||||
#else
|
||||
return juce::findMaximum (src, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept
|
||||
{
|
||||
#if JUCE_USE_SSE_INTRINSICS
|
||||
if (FloatVectorHelpers::isSSE2Available())
|
||||
_MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF);
|
||||
#endif
|
||||
(void) shouldEnable;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class FloatVectorOperationsTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations") {}
|
||||
|
||||
template <typename ValueType>
|
||||
struct TestRunner
|
||||
{
|
||||
static void runTest (UnitTest& u, Random random)
|
||||
{
|
||||
const int range = random.nextBool() ? 500 : 10;
|
||||
const int num = random.nextInt (range) + 1;
|
||||
|
||||
HeapBlock<ValueType> buffer1 ((size_t) num + 16), buffer2 ((size_t) num + 16);
|
||||
HeapBlock<int> buffer3 ((size_t) num + 16);
|
||||
|
||||
#if JUCE_ARM
|
||||
ValueType* const data1 = buffer1;
|
||||
ValueType* const data2 = buffer2;
|
||||
int* const int1 = buffer3;
|
||||
#else
|
||||
ValueType* const data1 = addBytesToPointer (buffer1.getData(), random.nextInt (16));
|
||||
ValueType* const data2 = addBytesToPointer (buffer2.getData(), random.nextInt (16));
|
||||
int* const int1 = addBytesToPointer (buffer3.getData(), random.nextInt (16));
|
||||
#endif
|
||||
|
||||
fillRandomly (random, data1, num);
|
||||
fillRandomly (random, data2, num);
|
||||
|
||||
Range<ValueType> minMax1 (FloatVectorOperations::findMinAndMax (data1, num));
|
||||
Range<ValueType> minMax2 (Range<ValueType>::findMinAndMax (data1, num));
|
||||
u.expect (minMax1 == minMax2);
|
||||
|
||||
u.expect (valuesMatch (FloatVectorOperations::findMinimum (data1, num), juce::findMinimum (data1, num)));
|
||||
u.expect (valuesMatch (FloatVectorOperations::findMaximum (data1, num), juce::findMaximum (data1, num)));
|
||||
|
||||
u.expect (valuesMatch (FloatVectorOperations::findMinimum (data2, num), juce::findMinimum (data2, num)));
|
||||
u.expect (valuesMatch (FloatVectorOperations::findMaximum (data2, num), juce::findMaximum (data2, num)));
|
||||
|
||||
FloatVectorOperations::clear (data1, num);
|
||||
u.expect (areAllValuesEqual (data1, num, 0));
|
||||
|
||||
FloatVectorOperations::fill (data1, (ValueType) 2, num);
|
||||
u.expect (areAllValuesEqual (data1, num, (ValueType) 2));
|
||||
|
||||
FloatVectorOperations::add (data1, (ValueType) 2, num);
|
||||
u.expect (areAllValuesEqual (data1, num, (ValueType) 4));
|
||||
|
||||
FloatVectorOperations::copy (data2, data1, num);
|
||||
u.expect (areAllValuesEqual (data2, num, (ValueType) 4));
|
||||
|
||||
FloatVectorOperations::add (data2, data1, num);
|
||||
u.expect (areAllValuesEqual (data2, num, (ValueType) 8));
|
||||
|
||||
FloatVectorOperations::copyWithMultiply (data2, data1, (ValueType) 4, num);
|
||||
u.expect (areAllValuesEqual (data2, num, (ValueType) 16));
|
||||
|
||||
FloatVectorOperations::addWithMultiply (data2, data1, (ValueType) 4, num);
|
||||
u.expect (areAllValuesEqual (data2, num, (ValueType) 32));
|
||||
|
||||
FloatVectorOperations::multiply (data1, (ValueType) 2, num);
|
||||
u.expect (areAllValuesEqual (data1, num, (ValueType) 8));
|
||||
|
||||
FloatVectorOperations::multiply (data1, data2, num);
|
||||
u.expect (areAllValuesEqual (data1, num, (ValueType) 256));
|
||||
|
||||
FloatVectorOperations::negate (data2, data1, num);
|
||||
u.expect (areAllValuesEqual (data2, num, (ValueType) -256));
|
||||
|
||||
FloatVectorOperations::subtract (data1, data2, num);
|
||||
u.expect (areAllValuesEqual (data1, num, (ValueType) 512));
|
||||
|
||||
fillRandomly (random, int1, num);
|
||||
doConversionTest (u, data1, data2, int1, num);
|
||||
}
|
||||
|
||||
static void doConversionTest (UnitTest& u, float* data1, float* data2, int* const int1, int num)
|
||||
{
|
||||
FloatVectorOperations::convertFixedToFloat (data1, int1, 2.0f, num);
|
||||
convertFixed (data2, int1, 2.0f, num);
|
||||
u.expect (buffersMatch (data1, data2, num));
|
||||
}
|
||||
|
||||
static void doConversionTest (UnitTest&, double*, double*, int*, int) {}
|
||||
|
||||
static void fillRandomly (Random& random, ValueType* d, int num)
|
||||
{
|
||||
while (--num >= 0)
|
||||
*d++ = (ValueType) (random.nextDouble() * 1000.0);
|
||||
}
|
||||
|
||||
static void fillRandomly (Random& random, int* d, int num)
|
||||
{
|
||||
while (--num >= 0)
|
||||
*d++ = random.nextInt();
|
||||
}
|
||||
|
||||
static void convertFixed (float* d, const int* s, ValueType multiplier, int num)
|
||||
{
|
||||
while (--num >= 0)
|
||||
*d++ = *s++ * multiplier;
|
||||
}
|
||||
|
||||
static bool areAllValuesEqual (const ValueType* d, int num, ValueType target)
|
||||
{
|
||||
while (--num >= 0)
|
||||
if (*d++ != target)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool buffersMatch (const ValueType* d1, const ValueType* d2, int num)
|
||||
{
|
||||
while (--num >= 0)
|
||||
if (! valuesMatch (*d1++, *d2++))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool valuesMatch (ValueType v1, ValueType v2)
|
||||
{
|
||||
return std::abs (v1 - v2) < std::numeric_limits<ValueType>::epsilon();
|
||||
}
|
||||
};
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("FloatVectorOperations");
|
||||
|
||||
for (int i = 1000; --i >= 0;)
|
||||
{
|
||||
TestRunner<float>::runTest (*this, getRandom());
|
||||
TestRunner<double>::runTest (*this, getRandom());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static FloatVectorOperationsTests vectorOpTests;
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_FLOATVECTOROPERATIONS_H_INCLUDED
|
||||
#define JUCE_FLOATVECTOROPERATIONS_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A collection of simple vector operations on arrays of floats, accelerated with
|
||||
SIMD instructions where possible.
|
||||
*/
|
||||
class JUCE_API FloatVectorOperations
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Clears a vector of floats. */
|
||||
static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept;
|
||||
|
||||
/** Clears a vector of doubles. */
|
||||
static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept;
|
||||
|
||||
/** Copies a repeated value into a vector of floats. */
|
||||
static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept;
|
||||
|
||||
/** Copies a repeated value into a vector of doubles. */
|
||||
static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept;
|
||||
|
||||
/** Copies a vector of floats. */
|
||||
static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept;
|
||||
|
||||
/** Copies a vector of doubles. */
|
||||
static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept;
|
||||
|
||||
/** Copies a vector of floats, multiplying each value by a given multiplier */
|
||||
static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
|
||||
|
||||
/** Copies a vector of doubles, multiplying each value by a given multiplier */
|
||||
static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept;
|
||||
|
||||
/** Adds a fixed value to the destination values. */
|
||||
static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept;
|
||||
|
||||
/** Adds a fixed value to the destination values. */
|
||||
static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept;
|
||||
|
||||
/** Adds a fixed value to each source value and stores it in the destination array. */
|
||||
static void JUCE_CALLTYPE add (float* dest, float* src, float amount, int numValues) noexcept;
|
||||
|
||||
/** Adds a fixed value to each source value and stores it in the destination array. */
|
||||
static void JUCE_CALLTYPE add (double* dest, double* src, double amount, int numValues) noexcept;
|
||||
|
||||
/** Adds the source values to the destination values. */
|
||||
static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept;
|
||||
|
||||
/** Adds the source values to the destination values. */
|
||||
static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept;
|
||||
|
||||
/** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */
|
||||
static void JUCE_CALLTYPE add (float* dest, const float* src1, const float* src2, int num) noexcept;
|
||||
|
||||
/** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */
|
||||
static void JUCE_CALLTYPE add (double* dest, const double* src1, const double* src2, int num) noexcept;
|
||||
|
||||
/** Subtracts the source values from the destination values. */
|
||||
static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept;
|
||||
|
||||
/** Subtracts the source values from the destination values. */
|
||||
static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept;
|
||||
|
||||
/** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */
|
||||
static void JUCE_CALLTYPE subtract (float* dest, const float* src1, const float* src2, int num) noexcept;
|
||||
|
||||
/** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */
|
||||
static void JUCE_CALLTYPE subtract (double* dest, const double* src1, const double* src2, int num) noexcept;
|
||||
|
||||
/** Multiplies each source value by the given multiplier, then adds it to the destination value. */
|
||||
static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
|
||||
|
||||
/** Multiplies each source value by the given multiplier, then adds it to the destination value. */
|
||||
static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept;
|
||||
|
||||
/** Multiplies the destination values by the source values. */
|
||||
static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept;
|
||||
|
||||
/** Multiplies the destination values by the source values. */
|
||||
static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept;
|
||||
|
||||
/** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */
|
||||
static void JUCE_CALLTYPE multiply (float* dest, const float* src1, const float* src2, int numValues) noexcept;
|
||||
|
||||
/** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */
|
||||
static void JUCE_CALLTYPE multiply (double* dest, const double* src1, const double* src2, int numValues) noexcept;
|
||||
|
||||
/** Multiplies each of the destination values by a fixed multiplier. */
|
||||
static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept;
|
||||
|
||||
/** Multiplies each of the destination values by a fixed multiplier. */
|
||||
static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept;
|
||||
|
||||
/** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */
|
||||
static void JUCE_CALLTYPE multiply (float* dest, const float* src, float multiplier, int num) noexcept;
|
||||
|
||||
/** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */
|
||||
static void JUCE_CALLTYPE multiply (double* dest, const double* src, double multiplier, int num) noexcept;
|
||||
|
||||
/** Copies a source vector to a destination, negating each value. */
|
||||
static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept;
|
||||
|
||||
/** Copies a source vector to a destination, negating each value. */
|
||||
static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept;
|
||||
|
||||
/** Converts a stream of integers to floats, multiplying each one by the given multiplier. */
|
||||
static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept;
|
||||
|
||||
/** Finds the miniumum and maximum values in the given array. */
|
||||
static Range<float> JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept;
|
||||
|
||||
/** Finds the miniumum and maximum values in the given array. */
|
||||
static Range<double> JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept;
|
||||
|
||||
/** Finds the miniumum value in the given array. */
|
||||
static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept;
|
||||
|
||||
/** Finds the miniumum value in the given array. */
|
||||
static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept;
|
||||
|
||||
/** Finds the maximum value in the given array. */
|
||||
static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept;
|
||||
|
||||
/** Finds the maximum value in the given array. */
|
||||
static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept;
|
||||
|
||||
/** On Intel CPUs, this method enables or disables the SSE flush-to-zero mode.
|
||||
Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE
|
||||
*/
|
||||
static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept;
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_FLOATVECTOROPERATIONS_H_INCLUDED
|
||||
Loading…
Add table
Add a link
Reference in a new issue