实时音频,快速循环中的临时缓冲区,不同的方法

Real-time audio, temporary buffers in fast loop, different methods

本文关键字:缓冲区 方法 音频 循环 实时      更新时间:2023-10-16

我的应用程序必须处理实时音频,为此我必须创建一个临时缓冲区来保存处理过的音频,直到它到达驱动程序。我在多线程中这样做所以我基本上是这样做的:

float *ProcessChunk(int sf)
{
  float tmp = new float[sf]; // Remember to delete[]
  for (int s=0; s<sf; ++s)
    tmp[s] = myProcessor->Tick(); // an external class that does the audio processing and returns the result per sample
}
void AudioCallbackFunction(void *outputBuffer, int sampleFrames)
{
  float *out = (float*)outputBuffer;
  auto t1 = async(launch::async, &ProcessChunk, sampleFrames);
  float *b = t1.get();
  for (int s=0; s<sampleFrames; ++s)
  {
    *out++ = b[s];
  }
  delete[] b;
}

在上面的例子中,这是完美的工作,每次ProcessChunk()被调用,一个新的浮点缓冲区被创建,然后在AudioCallbackFunction()中被删除。

另一种方法是使用vector:

vector<float> ProcessChunk(int sf)
{
  vector<float> tmp;
  for (int s=0; s<sf; ++s)
    tmp[s] = myProcessor->Tick();
  return tmp;
}
void AudioCallbackFunction(void *outputBuffer, int sampleFrames)
{
  float *out = (float*)outputBuffer;
  auto t1 = async(launch::async, &ProcessChunk, sampleFrames);
  vector<float> b = t1.get(); // get another copy of the vector?
  for (int s=0; s<sampleFrames; ++s)
  {
    *out++ = b[s];
  }
}

另一种替代方法是在程序启动时只创建一次浮点缓冲区,并在程序退出时删除它:

float *tmp;
// Once i know the exact value of sampleFrames
tmp = new float[sampleFrames];
float *ProcessChunk(int sf)
{
  for (int s=0; s<sf; ++s)
    tmp[s] = myProcessor->Tick();
}
void AudioCallbackFunction(void *outputBuffer, int sampleFrames)
{
  float *out = (float*)outputBuffer;
  auto t1 = async(launch::async, &ProcessChunk, sampleFrames);
  float *b = t1.get();
  for (int s=0; s<sampleFrames; ++s)
  {
    *out++ = b[s];
  }
}
// in main() when the program quits
delete[] tmp;

请注意,上面的代码只是一个例子,我的实际代码使用多个线程,并使用立体声音频。只使用一个线程将使它成为"多线程"没有意义,所以请不要评论这个。

问题:

  1. 向量分配和释放有多快?
  2. 在一个非常快的循环中连续创建然后删除内存缓冲区是多么安全?
  3. 您认为哪种方法更安全?
  1. 不够快。vector为元素分配的内存是连续的。
  2. 在通用硬件上,这不是一个真正的安全问题,因为你不希望内存耗尽。如果您正在实现一个驱动程序或此代码需要在嵌入式系统中运行,则可能会有所不同。
  3. 最安全的方法是矢量之一,假设你做得正确(ProcessChunk不调整矢量大小)。

    方法1很好,但是您仍然需要手动管理内存。如果你没有记得删除[]呢?

    使用global缓冲区需要额外注意线程安全。多少取决于你的实际代码。

可以将vector和惟一缓冲区组合:

vector<float> permbuffer; 
void ProcessChunk(vector<float>& perm, int sf)
{
  //perm size will eventually converge.
  perm.resize(sf);
  for (int s=0; s<sf; ++s)
    perm[s] = myProcessor->Tick();
  return; //good practice would be to return effectively read
}
void AudioCallbackFunction(void *outputBuffer, int sampleFrames)
{
  float *out = (float*)outputBuffer;
  //ditch async for thread to use references
  std::thread t(ProcessChunk, std::ref(permbuffer), sampleFrames);
  t.wait();
  for (int s=0; s<sampleFrames; ++s)
  {
    *out++ = permbuffer[s];
  }
}