C++数学函数生成
C++ mathematical function generation
在做一个项目时,我发现需要准确地生成各种波浪。我认为简单的正弦波是最容易开始的,但看来我错了。我制作了一个简单的程序,生成 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;
}
我不确定这是什么问题。我知道由 short
s 构成的矢量可能存在一些问题,但这正是我的音频框架想要的,我对这种库没有经验,所以不知道会发生什么。
症状如下:
- 频率不正确
- 即:给定freq=440,A4不是播放的音符
- 奇怪的失真
- 大多数频率不会产生干净的波。 220、440、880都是干净的,其他大部分都是失真的
- 大部分频率都大幅上移
任何人都可以就我可能做错的地方提出建议吗?
这是我目前尝试过的方法:
- 制作我自己的正弦函数,以获得更高的准确性。
- 我对 sin(x) 使用了 15 次泰勒级数展开
- 更改了采样率,从 256 到 44100 之间的任何值,鉴于上述错误,听不到任何变化,波形只是更加失真。
谢谢。如果有什么信息可以帮到你,我一定会提供的。
从根本上说,您遗漏了一条信息。您没有指定要采集样本的时间长度。这也可以被认为是系统播放样本的速率。不过,大致朝这个方向做的事情会让你们更接近,但就目前而言。
samples.push_back(amp * std::sin(M_PI / freq *i));
我怀疑您向 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 次的正弦波一样准确。这没有考虑噪音,但对于大多数用途来说效果很好。
我建议研究数字信号处理的基础知识,因为它是一个非常有趣的领域并且非常有助于理解。
编辑:假设所有这些参数都被评估为浮点数。
在做一个项目时,我发现需要准确地生成各种波浪。我认为简单的正弦波是最容易开始的,但看来我错了。我制作了一个简单的程序,生成 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;
}
我不确定这是什么问题。我知道由 short
s 构成的矢量可能存在一些问题,但这正是我的音频框架想要的,我对这种库没有经验,所以不知道会发生什么。
症状如下:
- 频率不正确
- 即:给定freq=440,A4不是播放的音符
- 奇怪的失真
- 大多数频率不会产生干净的波。 220、440、880都是干净的,其他大部分都是失真的
- 大部分频率都大幅上移
任何人都可以就我可能做错的地方提出建议吗?
这是我目前尝试过的方法:
- 制作我自己的正弦函数,以获得更高的准确性。
- 我对 sin(x) 使用了 15 次泰勒级数展开
- 更改了采样率,从 256 到 44100 之间的任何值,鉴于上述错误,听不到任何变化,波形只是更加失真。
谢谢。如果有什么信息可以帮到你,我一定会提供的。
从根本上说,您遗漏了一条信息。您没有指定要采集样本的时间长度。这也可以被认为是系统播放样本的速率。不过,大致朝这个方向做的事情会让你们更接近,但就目前而言。
samples.push_back(amp * std::sin(M_PI / freq *i));
我怀疑您向 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 次的正弦波一样准确。这没有考虑噪音,但对于大多数用途来说效果很好。
我建议研究数字信号处理的基础知识,因为它是一个非常有趣的领域并且非常有助于理解。
编辑:假设所有这些参数都被评估为浮点数。