【问题标题】:Mono to Stereo conversion单声道到立体声的转换
【发布时间】:2011-09-11 16:23:43
【问题描述】:

我在这里遇到以下问题:我得到一个表示音频数据的字节块 (uint16_t*),而生成它们的设备正在捕获单声道声音,所以显然我在 1 个通道上有单声道音频数据。我需要将此数据传递给另一个设备,该设备需要交错立体声数据(因此,2 个通道)。我想要做的基本上是复制数据中的 1 通道,以便立体声数据的两个通道都包含相同的字节。你能指出一个有效的算法吗?

谢谢, f.

【问题讨论】:

  • 您使用什么音频格式/框架?
  • Anders 还要求提供框架。
  • Framework 没什么特别的,1 个设备将数据发送到一个指针 (void*) 并告诉我样本数(通过回调),而另一个设备采用这个数量的样本并且相同void* 并播放音频(从这个回调中调用第二个设备)。 2 号设备希望此数据是交错立体声。
  • 感谢大家的回复,问题解决了:)

标签: c algorithm audio


【解决方案1】:

您可能希望就地进行转换以节省一些内存。取决于相关设备的内存量有多小。所以你可能想用这样的东西代替 Paul R 的方法:

void interleave(uint16_t buf[], const int len)
{
    for (int i = len / 2 - 1, j = len - 1; i >= 0; --i) {
        buf[j--] = buf[i];
        buf[j--] = buf[i];
    }
}

从单声道设备获取声音数据时,您会分配一个两倍于所需大小的缓冲区并将其传递给单声道设备。这将用单声道音频填充一半的缓冲区。然后将该缓冲区传递给上述函数,该函数将其转换为立体声。最后,您将缓冲区传递给立体声设备。您节省了额外的分配,因此转换使用的内存减少了 33%。

【讨论】:

    【解决方案2】:

    您需要交错数据,但如果帧长度大于 1,则上述解决方案均无效。下面的代码可以解释可变帧长度。

    void Interleave(BYTE* left, BYTE* right, BYTE* stereo,int numSamples_in, int frameSize)
    {
        int writeIndex = 0;
        for (size_t j = 0; j < numSamples_in; j++)
        {
            for (int k = 0; k < frameSize; k++)
            {
                int index = j * frameSize + k;
    
                stereo[k + writeIndex] = left[index];
                stereo[k + writeIndex + frameSize] = right[index];
            }
            writeIndex += 2 * frameSize;
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果您只想要交错立体声样本,那么您可以使用如下函数:

      void interleave(const uint16_t * in_L,     // mono input buffer (left channel)
                      const uint16_t * in_R,     // mono input buffer (right channel)
                      uint16_t * out,            // stereo output buffer
                      const size_t num_samples)  // number of samples
      {
          for (size_t i = 0; i < num_samples; ++i)
          {
              out[i * 2] = in_L[i];
              out[i * 2 + 1] = in_R[i];
          }
      }
      

      要从单个单声道缓冲区生成立体声,您只需为 in_L 和 in_R 传递相同的指针,例如

      interleave(mono_buffer, mono_buffer, stereo_buffer, num_samples);
      

      【讨论】:

      • 下次您删除编辑时,您能否至少通知贡献者? :-/
      • 当然,虽然我不知道该怎么做?
      • 添加就地版本后忘记添加评论。我的错。
      • 没问题。仅供参考,我觉得您的编辑虽然包含有用的信息,但不属于此答案的一部分,也许应该作为单独的答案添加。我看到你现在已经这样做了:+1!
      • 这很接近,但它只适用于帧深度只有一个字节的特殊情况。我将此答案扩展为适用于任何帧深度。
      【解决方案4】:

      您将不得不复制缓冲区并复制它。由于您没有告诉我们格式,它是如何终止的,我无法提供代码,但它看起来像一个简单的 for 循环。

      int_16* allocateInterleaved(int_16* data, int length)
        int i;
        int *copy = malloc(sizeof(int_16)*length*2);
        if(copy == NULL) {
          /* handle error */
        }
        for(i =0; i<length; i++) {
          copy[2*i] = data[i];
          copy[2*i+1] = data[i]; 
        }
        return copy;
      }
      

      请原谅任何明显的拼写错误,我的 C 有点生锈。将签名 16 位转换为 int_16 所需的任何类型的 typdef。不要忘记释放复制缓冲区,或者更好地重用它。

      【讨论】:

        【解决方案5】:

        向两个通道传递相同的指针?如果这违反了restrict 规则,请使用memcpy()

        抱歉,您的问题太宽泛了。应用程序接口?操作系统? CPU架构?

        【讨论】:

        • 这不起作用,我没有为每个通道发送数据,我发送了一大堆字节和设备编号。 2 根据需要将它们分开。
        猜你喜欢
        • 1970-01-01
        • 2019-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-04
        • 2017-01-22
        相关资源
        最近更新 更多