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

Posted

技术标签:

【中文标题】如何创建指向与 VST 音频缓冲区相同的指针数组的指针?【英文标题】:How to create pointer to pointer array same as VST audio buffer? 【发布时间】:2013-11-06 09:59:07 【问题描述】:

在 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;

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

【问题讨论】:

如果您只希望语法示例[0][0] 工作,则不需要指针。您也可以使用向量的向量。 【参考方案1】:
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;

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

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

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;

【讨论】:

【参考方案2】:

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

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

Here's a great guide关于实时音频编程的注意事项。

【讨论】:

不知道为什么您认为任何人建议在调用进程函数时分配内存。【参考方案3】:

如果你想用 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[] 之前提前退出。

【讨论】:

谢谢,这看起来很完美,能够使用向量的管理和指针的直接访问速度。 这段代码非常危险,它在 process() 回调中做了很多额外的工作,这对时间非常关键。 @Constantine 的答案对于 C++ 音频工作要好得多。我知道您提倡一般的 C++ 最佳实践,但在这种情况下,需要进行一些额外的优化,否则插件会丢弃音频缓冲区并导致播放故障。 @NikReiman,我一点也不同意你的看法。也许你误解了什么。我当然不打算提倡在时间关键阶段创建大量临时向量。想想我在这里写的“设置”代码。 @NikReiman:另外,vector 与 new[] 的关系与您提出的关于 when 分配的问题非常正交。 @sellibitze 在那种情况下,也许我太从字面上理解了你的帖子。对不起!

以上是关于如何创建指向与 VST 音频缓冲区相同的指针数组的指针?的主要内容,如果未能解决你的问题,请参考以下文章

我将如何以编程方式与 VST(i) 插件交互以合成音频?

是否可以在 Windows 7 中获取指向默认音频端点设备缓冲区的指针?

将 char* 推送到向量时出现问题,但在每次迭代后,它会将指向相同值缓冲区的指针添加到向量中 [已解决]

创建2D数组和指向&array-1的指针

VST 前瞻和 setInitialDelay()

返回一个指向 char 数组的指针。如何打印出数组的全部内容?