Force AudioBuffer to work only with floats

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.

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

…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…

AudioBuffer already works with doubles.

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

1 Like

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