以下是一个使用QT C++绘制频谱图的示例代码。该程序可以读取wav文件并显示其频谱图。
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QFileDialog>
#include <QtMath>
#include "wavefile.h"
class SpectrumWidget : public QWidget
{
public:
SpectrumWidget(QWidget* parent = nullptr)
: QWidget(parent), m_spectrum(nullptr), m_spectrumLength(0)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setMinimumSize(300, 200);
}
~SpectrumWidget()
{
delete[] m_spectrum;
}
void setWaveFile(const QString& filename)
{
WaveFile wave(filename);
int sampleRate = wave.sampleRate();
int numChannels = wave.numChannels();
int numSamples = wave.numSamples();
if (numChannels != 1) {
qDebug() << "Only mono files are supported";
return;
}
const double* samples = wave.samples();
// Compute the FFT of the signal
const int N = 1024; // FFT size
double window[N];
for (int i = 0; i < N; i++) {
window[i] = 0.5 * (1 - qCos(2*M_PI*i/(N-1))); // Hanning window
}
m_spectrumLength = N/2 + 1;
m_spectrum = new double[m_spectrumLength];
for (int i = 0; i <= numSamples-N; i += N/2) {
std::complex<double> x[N];
for (int j = 0; j < N; j++) {
x[j] = samples[i+j] * window[j]; // Apply window function
}
std::valarray<std::complex<double>> v(x, N);
fft(v); // Compute FFT
for (int j = 0; j < m_spectrumLength; j++) {
m_spectrum[j] += std::abs(v[j])/m_spectrumLength;
}
}
update();
}
protected:
void paintEvent(QPaintEvent* event) override
{
QPainter painter(this);
painter.fillRect(rect(), Qt::black);
if (!m_spectrum) return;
int numBins = m_spectrumLength;
double binWidth = width()/(double)numBins;
double maxValue = *std::max_element(m_spectrum, m_spectrum+m_spectrumLength);
for (int i = 0; i < numBins; i++) {
double x1 = i*binWidth;
double y1 = height();
double x2 = (i+1)*binWidth - 1;
double y2 = height()*(1 - m_spectrum[i]/maxValue);
QRectF rect(x1, y1, x2-x1, y2-y1);
QColor color(255*m_spectrum[i]/maxValue, 255*m_spectrum[i]/maxValue, 255);
painter.fillRect(rect, color);
}
}
private:
double* m_spectrum;
int m_spectrumLength;
void fft(std::valarray<std::complex<double>>& x)
{
int n = x.size();
if (n <= 1) return;
// Split even and odd elements
std::valarray<std::complex<double>> even = x[std::slice(0, n/2, 2)];
std::valarray<std::complex<double>> odd = x[std::slice(1, n/2, 2)];
// Recursively compute FFT of even and odd parts
fft(even);
fft(odd);
// Combine results by multiplying with twiddle factors
for (int k = 0; k < n/2; k++) {
std::complex<double> t = std::polar(1.0, -2*M_PI*k/n) * odd[k];
x[k] = even[k] + t;
x[k+n/2] = even[k] - t;
}
}
};
class MainWindow : public QWidget
{
public:
MainWindow(QWidget* parent = nullptr)
: QWidget(parent)
{
setWindowTitle("Spectrum");
setMinimumSize(400, 300);
m_spectrumWidget = new SpectrumWidget(this);
m_openButton = new QPushButton("Open", this);
connect(m_openButton, &QPushButton::clicked,
this, &MainWindow::onOpenClicked);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(m_spectrumWidget);
layout->addWidget(m_openButton);
}
private slots:
void onOpenClicked()
{
QString filename = QFileDialog::getOpenFileName(this, "Open WAV file", "", "*.wav");
if (!filename.isEmpty()) {
m_spectrumWidget->setWaveFile(filename);
updateGeometry();
adjustSize();
}
}
private:
SpectrumWidget* m_spectrumWidget;
QPushButton* m_openButton;
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
请注意,此示例代码片段仅用于演示绘制频谱图的基本原理,并不涵盖所有情况。例如,它只支持单声道WAV文件,并且使用固定的FFT大小和窗函数。在实际应用中,需要根据实际情况选择合适的参数,并进行必要的优化和错误处理。