Question

In the VST spec, a buffer of multichannel audio data is passed around.....

MyClass::ProcessDoubleReplacing(double **inputs, double **outputs, int frames)
{
    //and accessed like this...
    outputs[channel][sample] = inputs[channel][sample]
}

I want to create a similar "2d pointer array", but am not having much success. I can create a simple pointer and iterate through it reading/writing values....

double* samples;
samples[0] = aValue;

.... but am having a crash festival trying to implement something that will allow me to...

samples[0][0] = aValue;

What would be the correct way to implement this?

Was it helpful?

Solution 3

If you want to write something in C++ to provide a similar interface like the one you showed, I would use std::vector for managing the memory like this:

vector<vector<double>> buffers (2,vector<double>(500));

This only stores the data. For an array of pointers you need an array of pointers. :)

vector<double*> pointers;
pointers.push_back(buffers[0].data());
pointers.push_back(buffers[1].data());

This works since std::vector makes the guarantee that all elements are stored adjacent and linearly in memory. So, you're also allowed to do this:

double** p = pointers.data();
p[0][123] = 17;
p[1][222] = 29;

It's important to note that if you resize some of these vectors, the pointers might get invalid in which case you should go ahead and get the new pointer(s).

Keep in mind that the data member function is a C++11 feature. If you don't want to use it, you can write

&some_vector[0] // instead of some_vector.data()

(unless the vector is empty)

Instead of passing a double** to some function, you might be interested in passing the buffers vector directly by reference, though, this obviously won't work if you want your interface to be C compatible. Just saying.

Edit: A note on why I chose std::vector over new[] and malloc: Because it's the right thing to do in modern C++! The chance of messing up in this case is lower. You won't have any memory leaks since the vector takes care of managing the memory. This is especially important in C++ since you might have exceptions flying around so that functions might be exited early before the use of a delete[] at the end of the function.

OTHER TIPS

double* samples;
samples[0] = aValue;

That's really bad. :( Please don't do this! "sample" is just a pointer to somewhere in your memory. The memory it points to is not allocated at all, but you're writing to this memory...

You can allocate a block of memory either from the heap or from the stack. However, the stack has a size limit (configured in your compiler settings) - so for larger blocks (like audio data) you would typically allocate it from the heap. But you have to take care, that you won't leak memory from the heap - the stack memory is automatic managed by the scope of your variable, so that's easier to start with. In C/C++ you can allocate memory from the stack like this:

double samples[512];

then you can do stuff like:

samples[0] = aValue; // change value of 1st sample in sample buffer with 512 elements

or

double* pointerToSample = samples[255]; // point to 256ths sample in the sample buffer
pointerToSample[127] = aValue;          // change value of 384ths sample (256+128) in our sample buffer with 512 elements

and so on... BUT if you just do,

double* pointerToSample;
pointerToSample[127] = aValue;

You're actualing writing to unallocated memory! Your pointer points somewhere, but there is no allocated memory behind it. Be carefull with this! Also never access pointerToSample if the samples variable is already out-of-scope: the memory pointerToSample points to is no longer allocated otherwise.

To allocate memory from the heap in C++ there is the keyword new (to allocate memory) and delete (to free memory afterwards) dynamically.

i.e.

double *samples = new double[512];

will allocate a block of memory for your sample data. But after using it, you have to manually delete it - otherwise you're leaking memory. So just do:

delete[] samples;

after you're finished with it.

Last but not least to answer your question how to create a two dimensional array to call the method ProcessDoubleReplacing()

int main(int argc, char ** argv){
  /* create 2 dimensional array */
  int** samplesIn = new int*[44100];
  int** samplesOut = new int*[44100];
  for(int i = 0; i < 44100; ++i){   // 1s @ 44.1Khz
    samplesIn[i] = new int[2];      // stereo
    samplesOut[i] = new int[2];      // stereo
  }
  /* TODO: fill your input buffer with audio samples from somewhere i.e. file */
  ProcessDoubleReplacing(samplesIn, samplesOut, 44100);

  /* cleanup */
  for(int i = 0; i < 44100; ++i) {
    delete [] samplesIn[i];
    delete [] samplesOut[i];
  }
  delete [] samplesIn;
  delete [] samplesOut;

  return 0;
}

@Constantin's answer pretty much nailed it, but I just wanted to add that in your implementation you should not allocate the buffers in your process() callback. Doing so may cause your plugin to take too much time, and as a consequence the system can drop audio buffers, causing playback glitches.

So instead, these buffers should be fields of your main processing class (ie, the AEffect), and you should allocate their size in the constructor. Never use new or delete inside of the process() method or else you are asking for trouble!

Here's a great guide about the do's and don'ts of realtime audio programming.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top