【问题标题】:C++ mathematical function generationC++ 数学函数生成
【发布时间】:2016-05-01 03:12:28
【问题描述】:

在开展一个项目时,我遇到了准确地生成各种波浪的需求。我认为简单的正弦波最容易开始,但似乎我错了。我做了一个简单的程序,它生成一个vector 的样本,然后播放这些样本,以便用户听到波形,作为测试。以下是相关代码:

vector<short> genSineWaveSample(int nsamples, float freq, float amp) {
  vector<short> samples;
  for(float i = 0; i <= nsamples; i++) {
    samples.push_back(amp * sinx15(freq*i));
  }
  return samples;
}

我不确定这是什么问题。我知道由shorts 组成的向量可能存在一些问题,但这正是我的音频框架想要的,我对这种库没有经验,所以不知道会发生什么。

症状如下:

  • 频率不正确
    • 即:给定 freq=440,A4 不是播放的音符
  • 奇怪的失真
    • 大多数频率不会产生干净的波。 220、440、880 都是干净的,其他大多数都是失真的
  • 大部分频率大幅上移

任何人都可以就我可能做错的事情提供建议吗?

这是我迄今为止尝试过的:

  • 制作我自己的正弦函数,以获得更高的精度。
    • 我对 sin(x) 使用了 15 次泰勒级数展开
  • 更改了采样率,从 256 更改为 44100,由于上述错误,听不到任何变化,只是波形更加失真。

谢谢。如果有任何信息可以帮助您,我有义务提供。

【问题讨论】:

  • 最好把i改成int,很可能它不会解决任何问题,但会更快
  • sinx15 是做什么的?
  • @EdHeal,我应该提到,sinx15 是我的 15 次泰勒多项式正弦逼近。
  • 什么是频率参数?每个周期至少需要两个样本才能获得无锯齿波。您可能忘记了 2pi 术语。 “频率”是作为欧米茄(2 * pi *频率)传递还是只是普通频率?此外,你实现它的方式你会想要 i / frequency 或类似的东西。
  • @Slava,会的。我认为最终可能会出现类型转换问题

标签: c++ math trigonometry


【解决方案1】:

我怀疑您向 sin15x 函数传递了不正确的值。如果您熟悉信号处理的基础知识,Nyquist frequency 是您可以忠实重建(或在您的情况下构建)采样信号的最低频率。被定义为信号中最高频率分量的 2 倍。

这对您的程序意味着您在每个周期中需要最后 2 个值来重现您想要重现的最高频率。在 20Khz 时,您每秒需要 40,000 个样本。看起来您只是在用值打包一个向量并让播放程序整理时间。

我们假设您使用 44.1Khz 作为播放采样频率。这意味着产生一秒钟 1kHz 波的代码片段看起来像

DataStructure wave = new DataStructure(44100) // creates some data structure of 44100 in length

for(int i = 0; i < 44100; i++)
{
  wave[i] = sin(2*pi * i * (frequency / 44100) + pi / 2) // sin is in radians, frequency in Hz
}

您需要除以频率,而不是相乘。要看到这一点,请以通过了 22,050 Hz 频率值的情况为例。对于 i = 0,你得到 sin(0) = 1。对于 i = 1,sin(3pi/2) = -1 等等。这为您提供了 1、-1、1、-1... 的重复序列,这是以 44.1Khz 采样的 22,050Hz 波的正确表示。当您降低频率时,这会起作用,但是每个周期您会获得越来越多的样本。有趣的是,这并没有什么不同。每个周期采样 2 个样本的正弦波与每秒采样 1000 次的正弦波一样精确。这没有考虑噪音,但对于大多数目的来说效果很好。

我建议研究数字信号处理的基础知识,因为它是一个非常有趣且易于理解的领域。

编辑:假设所有这些参数都被评估为浮点数。

【讨论】:

  • 嗯,我明白了。这根本不是我最初的想法,但我认为这里的关键是除以采样率。我将不得不对此进行研究,并且根据您提到的奈奎斯特频率问题,我会看到倍频可能成为问题的地方。我不确定这在数学上是如何工作的,但我肯定会研究它。谢谢。
  • 是的,采样率位很重要。我怀疑你得到一些 220、440 等结果的原因是它们被别名为地狱,你很幸运这些别名落在可听范围内,尽管严重失真
【解决方案2】:

从根本上说,您缺少一条信息。您没有指定要采集样本的时间量。这也可以被认为是系统播放样本的速率。不过,目前大致朝这个方向的东西会让你更接近。

samples.push_back(amp * std::sin(M_PI / freq *i));

【讨论】:

  • 这会产生极长的波长,播放时听不见。
  • 也许我不明白向量中的数据应该是什么样子。我假设,如果你绘制它们,它们应该沿着给定频率的正弦波下降?我不是音频工程师,但如果我能理解您想要的结果数据,我可以帮助您编写代码。
  • 没错,它应该看起来像一个正弦波,我编写了一个简单的程序来为给定频率生成一个 CSV,并将其绘制在电子表格中,它看起来确实像一个正弦波我写的。 nsamples 参数定义要生成多少点。该函数根据样本而不是时间来生成波形,并且在回放时样本与时间相关,但生成器函数不知道将经过多少时间。
  • 我仍然需要知道播放速率。如果您不知道播放样本的频率,就不可能实现特定的频率输出。本质上,问题是沿时间轴缩放的问题之一。您在此处发布的代码肯定会生成正弦模式,但频率会与输入频率成反比。
  • 谢谢,我想我有一些研究要做......也许我没有像我想的那样理解这一点。
猜你喜欢
  • 2015-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-17
相关资源
最近更新 更多