如何计算无谐波正弦波的有效频率?(44.1kHz采样率)
问题描述
我正在编写一款采样率为44.1kHz的正弦波音频生成程序。下方两幅图的X轴为720个音频样本,Y轴为取值±4000的正弦波(4000代表振幅/音量)。
将包含720个样本的正弦波(1440字节的16位有符号PCM音频)以连续数据报形式发送至音频应用,以还原纯净测试音。要获得纯净音调,需生成下方的连续波形(目前需手动调整频率实现)。
我的问题是:如何通过数学方法计算符合要求的有效频率集合?
正弦波生成代码片段
// check sampleRate, channelCount and sampleFormat if (rAudioFormat.isValid()) { const auto sampleFormat = rAudioFormat.sampleFormat(); const auto channelCount = rAudioFormat.channelCount(); const auto bytesPerFrame = rAudioFormat.bytesPerFrame(); const auto volume = aVolumePercent / 100.0f; // payload must have room for at least 1 frame if (aAudioSizeBytes >= bytesPerFrame) { static std::default_random_engine gRandomNumberGenerator; // each frame contains inputChannelCount audio samples const qint64 frameCount = aAudioSizeBytes / bytesPerFrame; // pad size in bytes at the end of the datagram. const qint64 paddingBytes = aAudioSizeBytes % bytesPerFrame; // allocate raw data for an unpadded audio sample const auto payload = std::make_unique<char[]>(aAudioSizeBytes - paddingBytes); switch (aSignalGenerator) { case SignalGenerator::Monotone: { if ((!rGeneratorParams.empty()) && (static_cast<size_t>(rAudioFormat.sampleRate()) > 0u)) { const auto frequency = rGeneratorParams[0]; for (auto t = 0; t < frameCount; ++t) { const auto value = qSin( 2 * M_PI * frequency * (t % frameCount) / rAudioFormat.sampleRate()); switch (sampleFormat) { . . . case QAudioFormat::SampleFormat::Int16: { constexpr auto gMaxVal = std::numeric_limits<int16_t>::max(); const auto dest = reinterpret_cast<int16_t*>(payload.get()); for (auto i = 0; i < channelCount; i++) { dest[t * channelCount + i] = static_cast<int16_t>(gMaxVal * volume * value); } } break;
波形对比图

当前未完善的辅助函数代码
//! helper function to calculate produce pure tones //! given nTotalSamples with given nSampleRate. std::vector<int> getDivisors(int nSampleRate, int nTotalSamples, int nearestMin) { std::vector<int> divisors; if ((nSampleRate> 0) && (nearestMin > 0) && (nearestMin <= nTotalSamples)) { for (int i = nearestMin; i < nTotalSamples; ++i) { if ((nTotalSamples % i) == 0) { // calculate the corresponding tone frequency divisors.emplace_back(i); } } } return divisors; }
解决方案
核心数学逻辑
要让连续发送的720样本数据报拼接后无断点(形成纯净无谐波的正弦波),必须保证每个720样本块恰好包含整数个正弦波周期。
设采样率为Fs=44100Hz,每个数据报的样本数为N=720,频率为f,则需满足:(f * N) / Fs = k,其中k是正整数(每个块包含k个周期)
变形可得有效频率公式:f = (k * Fs) / N
同时要遵守奈奎斯特采样定理:f < Fs/2,即f < 22050Hz,因此k的最大值为floor( (N * Fs/2) / Fs ) = floor(N/2) = 360
完善后的辅助函数
修改函数,直接计算符合要求的频率集合,同时过滤超过奈奎斯特极限的数值:
#include <vector> #include <cmath> #include <algorithm> //! 计算可生成纯净音调的有效频率集合 //! @param nSampleRate: 采样率(如44100) //! @param nTotalSamples: 每个数据报的样本数(如720) //! @param minFreq: 最小目标频率(可选,默认0) //! @return 符合要求的有效频率列表 std::vector<double> getPureToneFrequencies(int nSampleRate, int nTotalSamples, double minFreq = 0.0) { std::vector<double> frequencies; if (nSampleRate <= 0 || nTotalSamples <= 0) { return frequencies; } const double nyquist = nSampleRate / 2.0; // 计算最大允许的k值:k_max = floor( (nyquist * nTotalSamples) / nSampleRate ) const int maxK = static_cast<int>(std::floor( (nyquist * nTotalSamples) / nSampleRate )); for (int k = 1; k <= maxK; ++k) { double freq = static_cast<double>(k * nSampleRate) / nTotalSamples; if (freq >= minFreq) { frequencies.push_back(freq); } } // 可选:排序频率列表 std::sort(frequencies.begin(), frequencies.end()); return frequencies; }
代码说明
- 奈奎斯特限制:直接通过
nyquist = nSampleRate/2计算上限,确保生成的频率不会发生混叠。 - k值范围:k从1开始到
maxK,保证每个数据报包含整数个正弦波周期,拼接后波形连续无断点。 - 频率计算:严格按照
f=(k*Fs)/N计算,确保每个块的起始和结束相位完全一致,避免拼接时产生谐波。 - 最小频率过滤:可选参数
minFreq可以过滤掉过低的频率,符合实际测试需求。
验证示例
以你的参数(Fs=44100,N=720)为例:
- k=1时,f=(1*44100)/720=61.25Hz
- k=2时,f=122.5Hz
- ...
- k=360时,f=(360*44100)/720=22050Hz(刚好等于奈奎斯特频率,实际建议取到359,f=21988.75Hz)
这些频率生成的720样本块,拼接后都会形成连续的正弦波,不会产生谐波。
内容的提问来源于stack exchange,提问作者johnco3
相关产品推荐
相关产品推荐

