QtCreator+C++正弦波生成器83.3Hz信号周期性clipping问题求助
问题分析与解决方案
问题根源
你遇到的83.3Hz信号周期性削波,本质是相位不连续导致的信号突变:
- 83.3Hz实际是
250/3 Hz,在44100采样率下,单个周期的样本数为44100/(250/3)=529.2,并非整数 - 你每次生成100ms的buffer(固定4410个样本),都是从相位0开始计算正弦波,这意味着每个buffer的结尾相位和下一个buffer的开头相位完全断开(4410个样本对应8.333个周期,结尾相位不为0)
- 前后buffer的信号值突变,在示波器上就表现为每100ms一次的类似削波的尖峰;而其他频率(如60Hz)刚好满足整数周期,相位连续,所以无异常
解决方案
核心是维护一个累积相位变量,让每次生成的buffer都从上一次结束的相位继续计算,保证信号连续性。
步骤1:添加相位成员变量
在MainWindow类的头文件中添加全局相位变量:
private: double m_currentPhase = 0.0; int m_targetFrequency = 0; // 保存当前要生成的频率
步骤2:修改频率生成函数
更新gerador_frequencia,保存目标频率并重置相位:
void MainWindow::gerador_frequencia(int frequency) { stopSignal(); qDebug() << "Gerando sinal de" << frequency << "Hz"; m_targetFrequency = frequency; m_currentPhase = 0.0; // 切换频率时重置相位 int sampleRate = 44100; QAudioFormat format; format.setSampleRate(sampleRate); format.setChannelCount(1); format.setSampleFormat(QAudioFormat::Int16); QAudioDevice audioDevice = QMediaDevices::defaultAudioOutput(); audioSink = new QAudioSink(audioDevice, format, this); audioIODevice = audioSink->start(); audioTimer = new QTimer(this); connect(audioTimer, &QTimer::timeout, this, &MainWindow::onAudioNotify); audioTimer->start(100); stopTimer = new QTimer(this); connect(stopTimer, &QTimer::timeout, this, &MainWindow::stopSignal); stopTimer->setSingleShot(true); stopTimer->start(3600000); qDebug() << "Frequencia gerada com sucesso!!!"; }
步骤3:修改正弦波生成函数
让generateSineWave使用累积相位计算,保证连续:
QByteArray MainWindow::generateSineWave(int frequency, int sampleRate, int durationMs) { int sampleCount = static_cast<int>((static_cast<double>(sampleRate) * durationMs) / 1000.0); QByteArray buffer(sampleCount * sizeof(int16_t), 0); int16_t *data = reinterpret_cast<int16_t*>(buffer.data()); double amplitude = 16000; double angleIncrement = (2.0 * M_PI * frequency) / sampleRate; for (int i = 0; i < sampleCount; ++i) { data[i] = static_cast<int16_t>(amplitude * sin(m_currentPhase)); m_currentPhase += angleIncrement; // 防止相位无限增大,取模2π优化数值稳定性 while (m_currentPhase >= 2.0 * M_PI) { m_currentPhase -= 2.0 * M_PI; } } return buffer; }
步骤4:更新音频回调函数
每次定时器触发时生成新的连续相位buffer,而不是复用旧buffer:
void MainWindow::onAudioNotify() { if (!audioIODevice || !audioSink) return; // 每次生成新的连续相位buffer QByteArray newBuffer = generateSineWave(m_targetFrequency, audioSink->format().sampleRate(), 100); audioIODevice->write(newBuffer); }
额外注意事项
- 确保
stopSignal()函数正确清理audioTimer、stopTimer、audioSink等资源,避免内存泄漏 - 若仍有削波,可小幅降低
amplitude值(比如调整到15000),留足突变余量,但核心问题解决后大概率不需要
内容的提问来源于stack exchange,提问作者matheusbrl
相关产品推荐
相关产品推荐

