Using "new" vs HeapBlocks


#1

Before I start my next plugin project I’m trying to collect as much info on DSP program efficiency & good coding technique.

Looking through the JUCE coding standards (awesome resource btw, thanks Jules!) I came across: “Do not ever use ‘new’ or malloc to allocate a C++ array. Always use a HeapBlock instead.”

I’ve sifted through many sites regarding HeapBlocks but I’m still quite not content with my understanding: What exactly is a HeapBlock & what is its advantage over using “new”?

In context, I’m trying to find the best way to create circular delay buffers, which in the past I’ve used “new” to do so.

Cheers!

Muir


#2

Concerning malloc / new and DSP IMHO this page is a good introduction : ( http://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing ).


#3

That’s not really got anything to do with the preference for HeapBlock over new.

What you should look into (in answer to your specific question) is RAII.

The reason might be more clear if you instead think about it as preferring HeapBlock over delete/free. If you use a HeapBlock, then the deletion of the allocated memory is guaranteed when the object goes out of scope. All it really does is provide a robust and safe interface to manage and manipulate heap allocations - it’s not like it does anything particularly special. It means that you can never ‘forget’ to call free, because you never have to; any time that it should be called, it is.

It’s the same as with ScopedPointer - it’s not magic, it’s just a wrapper to a pointer that will make sure that the target object gets deleted if it is ever cleared or goes out of scope.

So yeah, look up RAII. You don’t need to use such constructs, but there’s not really much reason to avoid them, since all they actually do is make your life easier!


#4

Many many thanks. Just the kind of info I was hoping for :smiley:


#5

Below is a rough sketch of a circular delay buffer object in the works, with the intention of implementing RAII.

What would be the best way to declare the float array & set the pointer to it within the object’s constructor? I’m still squirrelly on the smart pointer syntax & the below code doesn’t work, although hopefully it’ll show what I’m trying to do.

[code]//Delay buffer using RAII paradigm
#include
using namespace std;

class CDelayBuff
{
unique_ptr m_pBuff; //Points to array of floats
int m_iSize; //Stores size of float array

public:
CDelayBuff() //Default constructor
{
m_pBuff = nullptr;
m_iSize = 0;
}

CDelayBuff(int &iSize)
{
	m_iSize = iSize;
	m_pBuff = (new float[m_iSize]); //Assign array size in 'normal' constructor
}

~CDelayBuff()
{
	//Smart pointers take care of deallocation
}

void Write(int &iWrite, float &fSample)
{
	m_pBuff[iWrite] = fSample;
}

void Read(int &iRead, float &fSample)
{
	fSample = m_pBuff[iRead];
}

};[/code]


#6

A thought just occurred to me after my last post: Since the array of floats is supposed to last as long as the CDelayBuff object itself, perhaps I could just use raw pointers within CDelayBuff (with the object’s destructor deleting the array) & then declare CDelayBuff objects in my parent plugin class using unique_ptr. The unique_ptr would then automatically call CDelayBuff’s destructor when it goes out of scope or if an exception is thrown. Would this be memory leak safe?

[code]class CDelayBuff1 //Using raw member pointers. unique_ptr is used to declare object in parent class
{
float* m_pBuff; //Points to array of floats
int m_iSize; //Stores size of float array

public:
CDelayBuff1() //Default constructor
{
m_pBuff = nullptr;
m_iSize = 0;
}

CDelayBuff1(int &iSize)
{
	m_iSize = iSize;
	m_pBuff = new float[m_iSize]; //Assign array size in 'normal' constructor
}

~CDelayBuff1()
{
	if (m_pBuff != nullptr)
		delete[] m_pBuff;
}

void Write(int &iWrite, float &fSample)
{
	m_pBuff[iWrite] = fSample;
}

void Read(int &iRead, float &fSample)
{
	fSample = m_pBuff[iRead];
}

};[/code]


#7

Q: How do you implement a circular delay?

A: Do it tomorrow.


#8

You should follow haydxn’s advice. Don’t use raw pointers, and HeapBlock would be the best thing to use for this.


#9

Thanks for all the help!! I think I’m really getting somewhere now & I found out how to add JUCE modules to non IntroJucer projects :smiley:

I’m not sure if this is fundamentally possible in C++ structures, but in my delay class I’d like the user to have the ability to declare the number of channels via the object’s constructor (see code below). Thus, if the user declared: CPreDly PreDelay(2, 44100); , the object “PreDelay” would contain two Heapblocks, each with a size of 44100.

[code]#include "juce_core\JuceHeader.h"
using namespace std;

//PreDelay Module using RAII convention

//CONTAINS:
//Sample processing function (by reference)
//Cooking function (by reference)
//Smoothing function (by reference)

class CPreDly
{
int m_iSize; //Sample size for buffers
int m_iW; //Write index for delay buffers
OwnedArray<HeapBlock> m_DlyBuffs; //Array contaning delay buffers of each channel

public:
CPreDly();
~CPreDly();

CPreDly(int &iNumChannels,int &iSize);

/*void flushBuffs();
void writeToBuffs(float &fSmp_L,float &fSmp_R);
void incrementWriteIndex();*/

};

CPreDly::CPreDly()
{
m_iSize = 0;
m_iW = 0;

}

CPreDly::CPreDly()
{
//OwnedArray & HeapBlock take care of memory deallocation
}

CPreDly::CPreDly(int &iNumChannels,int &iSize)
{
for (int i=0; i<iNumChannels; i++)
{
m_DlyBuffs.add(&(HeapBlock(iSize,true))); //Creates a HeapBlock of iSize floats and initializes them all to zero
}
m_iSize = iSize;
}[/code]

Thanks again,

muir