You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算无谐波正弦波的有效频率?(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;
}

代码说明

  1. 奈奎斯特限制:直接通过nyquist = nSampleRate/2计算上限,确保生成的频率不会发生混叠。
  2. k值范围:k从1开始到maxK,保证每个数据报包含整数个正弦波周期,拼接后波形连续无断点。
  3. 频率计算:严格按照f=(k*Fs)/N计算,确保每个块的起始和结束相位完全一致,避免拼接时产生谐波。
  4. 最小频率过滤:可选参数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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 16:27:53