2

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

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

我想创建一个类似的“二维指针数组”,但没有太大的成功。我可以创建一个简单的指针并遍历它读取/写入值....

double* samples;
samples[0] = aValue;

....但是我正在举办一个崩溃节,试图实施一些能让我...

samples[0][0] = aValue;

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

4

3 回答 3

2
double* samples;
samples[0] = aValue;

这真的很糟糕。:( 请不要这样做!“sample”只是指向您内存中某个位置的指针。它指向的内存根本没有分配,但是您正在写入该内存...

您可以从堆或堆栈中分配一块内存。但是,堆栈有大小限制(在您的编译器设置中配置) - 因此对于较大的块(如音频数据),您通常会从堆中分配它。但是你必须小心,你不会从堆中泄漏内存 - 堆栈内存是由你的变量范围自动管理的,所以这更容易开始。在 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;

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

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

IE

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;
}
于 2013-11-06T10:52:02.790 回答
1

@Constantin 的回答非常准确,但我只是想在您的实现中添加一点,您不应该process()回调中分配缓冲区。这样做可能会导致您的插件花费太多时间,因此系统可能会丢弃音频缓冲区,从而导致播放故障。

因此,这些缓冲区应该是您的主要处理类(即AEffect)的字段,并且您应该在构造函数中分配它们的大小。千万不要new在方法里面或者delete里面使用,process()不然就是自找麻烦!

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

于 2013-11-06T16:46:08.933 回答
0

如果您想用 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::vectornew[]malloc因为在现代 C++ 中这样做是正确的!在这种情况下搞砸的可能性较低。由于向量负责管理内存,因此您不会有任何内存泄漏。delete[]这在 C++ 中尤其重要,因为您可能会有异常飞来飞去,因此函数可能会在函数末尾使用 a 之前提前退出。

于 2013-11-06T11:44:33.993 回答