【问题标题】:Audio mixing algorithm changing volume音频混合算法改变音量
【发布时间】:2015-11-30 06:09:58
【问题描述】:

我正在尝试使用以下算法混合一些音频样本:

short* FilterGenerator::mixSources(std::vector<RawData>rawsources, int numframes)
{
short* output = new short[numframes * 2]; // multiply 2 for channels

for (int sample = 0; sample < numframes * 2; ++sample)
{
    for (int sourceCount = 0; sourceCount < rawsources.size(); ++sourceCount)
    {
        if (sample <= rawsources.at(sourceCount).frames * 2)
        {
            short outputSample = rawsources.at(sourceCount).data[sample];
            output[sample] += outputSample;
        }
    }
}

// post mixing volume compression
for (int sample = 0; sample < numframes; ++sample)
{
    output[sample] /= (float)rawsources.size();
}

return output;
}

我得到了我想要的输出,除了当其中一个源完成后,其他源开始播放更响亮。我知道这是为什么,但我不知道如何正确解决。

另外,这是我输出的音频中 Audacity 的屏幕截图:

如您所见,肯定有问题。您可以看到音频在中心不再为零,并且一旦其中一个源播放完毕,您可以看到音频变得更响亮。

最重要的是我想解决音量问题,但非常感谢我能做的任何其他调整!

一些额外信息:我知道这段代码不允许单声道源,但没关系。我只会使用立体声交错音频样本。

【问题讨论】:

  • 这不是一个完整的答案,但您应该分配一次缓冲区并重新使用它。您希望避免在性能关键代码中分配内存。 (我假设这是指向声卡而不是文件。)
  • 这是指向一个文件。我正在非实时编辑音频。我知道我可以做一些更高效的事情,但这不是现在的问题。它现在可以非常快地完成我想要的操作
  • @JohnK:别出汗。现代分配器效率惊人,与 CPU 相比,音频速度非常慢。分配一个 1 kB 的缓冲区比播放它快一千倍。

标签: c++ algorithm audio


【解决方案1】:

通常混合不会除以来源的数量。这意味着将正常轨道与静音轨道混合可以将其幅度减半。如果你愿意,你最终可以标准化轨道,使其在他的范围内。

代码未经测试,可能有错误:

#include <algorithm> // for std::max 
#include <cmath>     // for std::fabs

short* FilterGenerator::mixSources(std::vector<RawData>rawsources, int numframes)
{
  // We can not use shorts immediately because can overflow
  // I use floats because in the renormalization not have distortions
  float *outputFloating = new float [numframes * 2];

  // The maximum of the absolute value of the signal 
  float maximumOutput = 0;

  for (int sample = 0; sample < numframes * 2; ++sample)
  {
      // makes sure that at the beginning is zero
      outputFloating[sample] = 0;

      for (int sourceCount = 0; sourceCount < rawsources.size(); ++sourceCount)
      {
          // I think that should be a '<'
          if (sample < rawsources.at(sourceCount).frames * 2)
              outputFloating[sample] += rawsources.at(sourceCount).data[sample];  
      }

      // Calculates the maximum
      maximumOutput = std::max (maximumOutput, std::fabs(outputFloating[sample]));
  }  

  // A short buffer
  short* output = new short [numframes * 2]; // multiply 2 for channels

  float multiplier = maximumOutput > 32767 ? 32767 / maximumOutput : 1;

  // Renormalize the track
  for (int sample = 0; sample < numframes * 2; ++sample)
      output[sample] = (short) (outputFloating[sample] * multiplier); 

  delete[] outputFloating;
  return output;
}

【讨论】:

  • 所以你会把除法排除在外,然后使用某种压缩?
  • 混音过程中可以计算输出信号绝对值的最大值,然后除以每个样本的最大值(当然只有超过最大范围)
  • 所以我必须进行某种线性动态范围压缩? (对下面评论的反应)
  • 我错误地认为您的浮点数在 [-1,+1] 范围内。简而言之,必须用最大可存储的最大信号重新归一化最大信号
  • 这取决于你想要什么,你可以使用动态压缩器或固定基于信号假设的最大值
【解决方案2】:

由于您在划分之前将所有内容加到short 中,因此您可能会溢出。你需要增加一个更大的中介。此外,最终缩放不应依赖于样本数量,它应该是一个常数 - 在调用函数之前 确定它。

short* FilterGenerator::mixSources(std::vector<RawData>rawsources, int numframes, double gain = 0.5)
{
    short* output = new short[numframes * 2]; // multiply 2 for channels

    for (int sample = 0; sample < numframes * 2; ++sample)
    {
        long newSample = 0;
        for (int sourceCount = 0; sourceCount < rawsources.size(); ++sourceCount)
        {
            if (sample <= rawsources.at(sourceCount).frames * 2)
            {
                short outputSample = rawsources.at(sourceCount).data[sample];
                newSample += outputSample;
            }
        }
        output[sample] = (short)(newSample * gain);
    }

return output;
}

【讨论】:

    【解决方案3】:

    您实际上不必执行“混音后体积压缩”。只需将所有来源相加,不要让总和溢出。这应该有效:

    short* FilterGenerator::mixSources(std::vector<RawData>rawsources, int numframes)
    {
    short* output = new short[numframes * 2]; // multiply 2 for channels
    
    for (int sample = 0; sample < numframes * 2; ++sample)
    {
        long sum = 0;
        for (int sourceCount = 0; sourceCount < rawsources.size(); ++sourceCount)
        {
            if (sample < rawsources.at(sourceCount).frames * 2)
            {
                short outputSample = rawsources.at(sourceCount).data[sample];
                sum += outputSample;
                output[sample] += outputSample;
            }
            if (sum > 32767) sum = 32767;
            if (sum < -32768) sum = -32768;
            output[sample] = (short)sum; 
        }
    }
    
    return output;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-06
      • 1970-01-01
      • 2014-02-01
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-10
      相关资源
      最近更新 更多