717 lines
41 KiB
C++
717 lines
41 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2017 - ROLI Ltd.
|
|
|
|
JUCE is an open source library subject to commercial or open-source
|
|
licensing.
|
|
|
|
The code included in this file is provided under the terms of the ISC license
|
|
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
|
To use, copy, modify, and/or distribute this software for any purpose with or
|
|
without fee is hereby granted provided that the above copyright notice and
|
|
this permission notice appear in all copies.
|
|
|
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
|
DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce
|
|
{
|
|
|
|
//==============================================================================
|
|
/**
|
|
This class a container which holds all the classes pertaining to the AudioData::Pointer
|
|
audio sample format class.
|
|
|
|
@see AudioData::Pointer.
|
|
|
|
@tags{Audio}
|
|
*/
|
|
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) (*((uint8*) 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) (((uint8) (*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) (((unsigned int) ByteOrder::littleEndian24Bit (data)) << 8); }
|
|
inline int32 getAsInt32BE() const noexcept { return (int32) (((unsigned int) 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) (int32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
|
|
inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (int32) (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() = default;
|
|
inline NonInterleaved (const NonInterleaved&) = default;
|
|
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 {}
|
|
inline Interleaved (const Interleaved& other) = default;
|
|
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 = 1;
|
|
enum { isInterleavedType = 1 };
|
|
};
|
|
|
|
//==============================================================================
|
|
class NonConst
|
|
{
|
|
public:
|
|
using VoidType = void;
|
|
static inline void* toVoidPtr (VoidType* v) noexcept { return v; }
|
|
enum { isConst = 0 };
|
|
};
|
|
|
|
class Const
|
|
{
|
|
public:
|
|
using VoidType = const void;
|
|
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_assert (InterleavingType::isInterleavedType == 0, "Incorrect constructor for interleaved data");
|
|
}
|
|
|
|
/** 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
|
|
{
|
|
// trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
|
|
static_assert (Constness::isConst == 0, "Attempt to write to a const pointer");
|
|
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
|
|
{
|
|
// trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
|
|
static_assert (Constness::isConst == 0, "Attempt to write to a const pointer");
|
|
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
|
|
{
|
|
// trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
|
|
static_assert (Constness::isConst == 0, "Attempt to write to a const pointer");
|
|
|
|
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
|
|
{
|
|
// trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
|
|
static_assert (Constness::isConst == 0, "Attempt to write to a const pointer");
|
|
|
|
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 */
|
|
Range<float> findMinAndMax (size_t numSamples) const noexcept
|
|
{
|
|
if (numSamples == 0)
|
|
return Range<float>();
|
|
|
|
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;
|
|
}
|
|
|
|
return Range<float> (mn, mx);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return Range<float> (mn * (float) (1.0 / (1.0 + Int32::maxValue)),
|
|
mx * (float) (1.0 / (1.0 + Int32::maxValue)));
|
|
}
|
|
|
|
/** Scans a block of data, returning the lowest and highest levels as floats */
|
|
void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept
|
|
{
|
|
Range<float> r (findMinAndMax (numSamples));
|
|
minValue = r.getStart();
|
|
maxValue = r.getEnd();
|
|
}
|
|
|
|
/** 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() = default;
|
|
|
|
/** 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.
|
|
|
|
@tags{Audio}
|
|
*/
|
|
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)
|
|
};
|
|
|
|
} // namespace juce
|