如何创建指向与 VST 音频缓冲区相同的指针数组的指针

How to create pointer to pointer array same as VST audio buffer?

本文关键字:指针 缓冲区 音频 VST 数组 何创建 创建      更新时间:2023-10-16

在VST规范中,多声道音频数据的缓冲区被传递.....

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

我想创建一个类似的"2d 指针数组",但没有取得多大成功。 我可以创建一个简单的指针并遍历它读取/写入值......

double* samples;
samples[0] = aValue;

....但是我有一个崩溃节,试图实施一些让我......

samples[0][0] = aValue;

实现这一点的正确方法是什么?

double* samples;
samples[0] = aValue;

这真的很糟糕。:(请不要这样做!"样本"只是指向内存中某处的指针。它指向的内存根本没有分配,但您正在写入此内存...

您可以从堆或堆栈分配内存块。但是,堆栈有大小限制(在编译器设置中配置) - 因此对于较大的块(如音频数据),您通常会从堆中分配它。但是你必须小心,你不会从堆中泄漏内存 - 堆栈内存由变量的作用域自动管理,所以这更容易开始。在 C/C++ 中,你可以像这样从堆栈中分配内存:

double samples[512];

然后你可以做这样的事情:

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

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

等等...但如果你只是这样做,

double* pointerToSample;
pointerToSample[127] = aValue;

您实际上是在写入未分配的内存!您的指针指向某处,但后面没有分配的内存。小心这个!如果样本变量已经超出范围,也永远不要访问 pointerToSample:否则不再分配内存指针到样本指向。

要从C++堆中分配内存,有关键字 new(分配内存)和删除(之后释放内存)。

double *samples = new double[512];

将为示例数据分配内存块。但是使用它后,您必须手动删除它 - 否则您将泄漏内存。所以只需做:

delete[] samples;

在你完成它之后。

最后但并非最不重要的是回答您的问题,如何创建一个二维数组来调用方法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的回答几乎是钉住了它,但我只是想补充一点,在您的实现中,您不应该process()回调中分配缓冲区。这样做可能会导致您的插件花费太多时间,因此系统可能会丢弃音频缓冲区,从而导致播放故障。

因此,这些缓冲区应该是主处理类(即AEffect)的字段,并且您应该在构造函数中分配它们的大小。切勿process()方法中使用newdelete,否则您将自找麻烦!

这是关于实时音频编程的注意事项的精彩指南。

如果你想

用C++写一些东西来提供类似于你展示的界面,我会使用 std::vector 来管理内存,如下所示:

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

这仅存储数据。对于指针数组,您需要一个指针数组。:)

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

这是有效的,因为std::vector保证所有元素都相邻且线性存储在内存中。因此,您也可以这样做:

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

请务必注意,如果调整其中一些向量的大小,指针可能会无效,在这种情况下,您应该继续获取新指针。

请记住,data成员函数是 C++11 功能。如果你不想使用它,你可以写

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

(除非向量为空)

您可能

有兴趣直接通过引用传递缓冲区向量,而不是将 double** 传递给某个函数,但是,如果您希望接口与 C 兼容,这显然是行不通的。只是说。

编辑:关于为什么我选择std::vector而不是new[]malloc的说明:因为这在现代C++中是正确的做法!在这种情况下,搞砸的机会较低。您不会有任何内存泄漏,因为矢量负责管理内存。这在C++中尤其重要,因为您可能会有异常飞来飞去,因此在函数结束时使用delete[]之前可能会提前退出函数。