Force AudioBuffer to work only with floats


#1

Since nothing in JUCE supports AudioBuffer with anything but floating point types, it would probably be a good idea to force AudioBuffer to only work with floats to prevent people from doing things like AudioBuffer<int16>. Or are there actual use cases for non-floating point types…?

The change for AudioBuffer is rather simple :

public:
	static_assert(std::is_floating_point<Type>::value, "Only floating point types supported");

type_traits may need to also be included.


#2

Well I’m going to give a bit of history to this… and look for another solution…

Goal:

I want to use AudioFormatReader::read() to read into either an AudioBuffer<float>
for 24 bit or to use an AudioBuffer<int16> to reduce the resolution from 24 to 16
bit as a low memory option in my synth – keeping my samples in memory in
smaller AudioBuffers (saving 50% RAM for loaded sounds).

Unfortunately AudioFormatReader::read() only reads into an AudioBuffer<float>
rather than limit AudioBuffer to only float types… I’d suggest that
AudioFormatReader::read() be templated.

I’ll try this:

AudioBuffer<float>* pFloatBuffer = new AudioBuffer<float> (pReader->numChannels, iTotalSamplestoWrite);

pReader->read (pFloatBuffer, 0, iTotalSamplestoWrite, 0, true, true);
                
juce::AudioData::ConverterInstance<float, int16> conv (pFloatBuffer->getNumChannels(), pFloatBuffer->getNumChannels());
                
AudioBuffer<int16>* pBuffer16 = new AudioBuffer<int16> (pReader->numChannels, iTotalSamplestoWrite);
                
conv.convertSamples (pBuffer16, pFloatBuffer, pFloatBuffer->getNumSamples());

Rail


#3

…and I would say that we will ultimately need AudioBuffer< double >, so templating could make sense,…even if using doubles is of questionable necessity. It is not hard to see future demands pushing us that way…


#4

AudioBuffer already works with doubles.


#5

The AudioFormatReader uses floats, I believe. That was my reference. I was not clear in specifying.


#6

Well a decision does need to be made one way or the other… since AudioBuffer has some float only code which causes a problem if you use non-float types (int16)

    AudioBuffer (const AudioBuffer& other)
       : 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);  // <<<<---  This will fail if the AudioBuffer is not a float type
            }
        }
    }

Other than this my code looks like it would work for AudioBuffer<int16> using:

void convertFloatBufferToShort (AudioBuffer<float>& srcBuffer, AudioBuffer<int16>& destBuffer)
{
    for (int i = 0; i < srcBuffer.getNumChannels(); ++i)
        {
        using DstSampleType = AudioData::Pointer<AudioData::Int16,   AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
        using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
    
        DstSampleType dstData (destBuffer.getWritePointer (i), srcBuffer.getNumChannels());
        SrcSampleType srcData (srcBuffer.getReadPointer (i));
        
        dstData.convertSamples (srcData, srcBuffer.getNumSamples());
        }
}

void convertShortBufferToFloat (AudioBuffer<int16>& srcBuffer, AudioBuffer<float>& destBuffer)
{
    for (int i = 0; i < srcBuffer.getNumChannels(); ++i)
        {
        using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
        using SrcSampleType = AudioData::Pointer<AudioData::Int16,   AudioData:: NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
        
        DstSampleType dstData (destBuffer.getWritePointer (i));
        SrcSampleType srcData (srcBuffer.getReadPointer (i), srcBuffer.getNumChannels());
        
        dstData.convertSamples (srcData, srcBuffer.getNumSamples());
        }
}

BTW… FloatVectorOperations::copy() just calls:

void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept
{
    memcpy (dest, src, (size_t) num * sizeof (double));
}

so there’s no reason not to have a different version using sizeof (DataType)

Replace:

 FloatVectorOperations::copy (channels[i], other.channels[i], size);

with:

 memcpy (channels[i], other.channels[i], (size_t) size * sizeof (Type));

Never mind… I only needed a very small subset of AudioBuffer, so I created my own ShortAudioBuffer class,

Rail