正弦音发生器产生不需要的谐波

问题描述 投票:0回答:1

我正在编写一个程序,以 44.1khz 采样率生成正弦波音频。下面的 2 个图在 X 轴上包含 720 个音频样本,在 Y 轴上包含 +/-4000 之间的正弦波(在本例中 4000 表示幅度/音量)。

720 个样本正弦波(1,440 字节的 PCM 16 签名音频)的内容作为背对背数据报发送到音频应用程序,在该应用程序中应再现纯测试音。为了获得纯音,我需要生成较低的图(我必须手动调整频率,直到获得连续的图)。

我的问题是如何从数学上计算有效频率集以适应上述情况。

这就是我制作上述图的方式:

// 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;

到目前为止,我已经想出了以下代码,它完成了部分工作(我认为)。我知道我需要得到 720 的偶数(因为它需要适合正弦)除数,但我不太知道如何完成此代码以返回与每个除数对应的频率值。考虑到采样率,我可能还应该考虑奈奎斯特来限制最大可再现音调频率。

    //! 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;
    }
math audio-streaming audiowaveform
1个回答
1
投票

您的帧持续时间是样本数除以采样率。振荡次数是频率乘以持续时间。您希望它是一个整数。因此,只需计算您将得到的数字,然后将其四舍五入到最接近的整数并转换回频率:

double frameDuration = samplesPerFrame / sampleRate;
double fractionalOscillations = frameDuration * desiredFrequency;
double wholeOscillations = round(fractionalOscillations);
double goodFrequency = wholeOscillations / frameDuration;

注意我正在使用

double
,所以我不必担心一路上的任何舍入效果。这可能会导致每个单独的振荡只采集一小部分样本。您计算个人值的方式 - 通过仅向正弦函数提供参数 - 我不认为这是一个问题。

另请注意,您必须使用兼容的设备。如果您使用采样率作为每秒样本数,频率作为 Hz,则效果很好,因为两者都有单位

1/s

© www.soinside.com 2019 - 2024. All rights reserved.