QT中使用fftw库

开发环境:

系统:Windows 10

QT版本:5.10

编译器:mingw32

 

1.下载fftw

http://www.fftw.org/install/windows.html,根据自已编译器版本下载32位或64位

2.生成lib库

使用visual studio里面的lib.exe工具(已管理员权限运行CMD),进入fftw-3.3.5-dll32目录,运行:

lib /def:libfftw3-3.def
lib /def:libfftw3f-3.def
lib /def:libfftw3l-3.def

会生成对应的lib和exp文件,如果需要.a库文件,把.lib直接重命名为.a即可

 

3.qt工程设置

在pro文件中添加lib和头文件

将libfftw3-3.dll,libfftw3f-3.dll和libfftw3l-3.dll拷贝到工程的build目录下

 

4.测试

读一个48K,16bit,单声道的pcm文件,然后通过fft变换后显示振幅谱和相位谱。

/**
 * @brief MainWindow::show_amplitude_waveform 显示频域的幅度
 * @param file_path 数据文件
 * @param polt_1    显示振幅
 * @param polt_2    显示相位
 * @return
 */
int MainWindow::show_amplitude_waveform(char * file_path, QCustomPlot *polt_1, QCustomPlot *polt_2)
{
    double val_max = 0;
    size_t result;
    char  *buf;
    short *in_buf;
    FILE *fp1=fopen(file_path, "rb");   //打开读权限
    fseek(fp1,0,SEEK_END);  //文件指针从0挪到尾部
    long filesize;
    filesize=ftell(fp1);    //ftell求文件指针相对于0的便宜字节数,就求出了文件字节数
    int N = filesize/2;     //计算数据个数

    if(fp1==NULL)   //判断文件是否打开
    {
        QMessageBox::critical(NULL, "错误", "\"" + QString(QLatin1String(file_path)) + "\"" + "文件打开失败!", QMessageBox::Yes, QMessageBox::Yes);
        return -1;
    }

    rewind(fp1);    //还原指针位置
    buf=(char *)malloc(filesize);   //开辟空间给缓存数组
    if(buf==NULL)
    {
        QMessageBox::critical(NULL, "错误", "\"" + QString(QLatin1String(file_path)) + "\"" + "内存分配失败!", QMessageBox::Yes, QMessageBox::Yes);
        return -1;
    }

    result =fread(buf, 1, filesize, fp1);//每次读一个字节到buf,同时求读的次数
    if(result != filesize)  //判断读的次数和文件大小是否一致
    {
        QMessageBox::critical(NULL, "错误", "\"" + QString(QLatin1String(file_path)) + "\"" + "文件读取失败!", QMessageBox::Yes, QMessageBox::Yes);
        return -1;
    }

    in_buf = (short *)buf;  //强转为short,因为采样深度是16bit

    //为fft输入计算分配空间
    double * in = (double*)fftw_malloc(sizeof(double) * N);
    for(int i=0; i<N; i++)
    {
        in[i] = in_buf[i];  //将pcm文件中的数据复制到fft的输入
    }

    //为fft输出算分配空间
    fftw_complex * out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);

    //进行fft变换,fftw_plan_dft_c2r_1d函数进行反变换
    fftw_plan p = FFTW3_H::fftw_plan_dft_r2c_1d(N, in, out, FFTW_ESTIMATE);
    fftw_execute(p);

    double dx3 = (double)SAMPLE_RATE / N;

    polt_1->xAxis->setRange(0, SAMPLE_RATE/2, Qt::AlignLeft);

    //根据FFT计算的复数计算振幅谱
    for( int i=0; i<N/2; i++ )
    {
        double val = sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]);
        val = val / (N / 2);
        polt_1->graph(0)->addData( dx3 * i, val );

        if( val > val_max )
        {
            val_max = val;
        }

        double db = log(val);
        //qDebug("frequency = %f, amplitude = %f, db = %f", dx3 * i, val / (N / 2), db);
    }

    polt_1->yAxis->setRange(val_max*0.6, val_max*1.2, Qt::AlignBottom);
    polt_1->replot();

    polt_2->xAxis->setRange(0, SAMPLE_RATE/2, Qt::AlignLeft);
    polt_2->yAxis->setRange(0, 10, Qt::AlignBaseline);
    //根据FFT计算的复数计算相位谱
    for( int i=0; i<N/2; i++ )
    {
        double val = atan2(out[i][1], out[i][0]);
        polt_2->graph(0)->addData( dx3 * i, val );
    }
    polt_2->replot();

    fclose(fp1);    //关闭文件指针
    fftw_destroy_plan(p);
    free(buf);      //释放buf
    fftw_free(in);
    fftw_free(out);

    return 0;
}

 

<think>好的,用户想了解如何在C++ Qt使用FFT。首先,我需要确认FFT的基本概念和常见。FFT是快速傅里叶变换,用于将信号从时域转换到频域。Qt本身不提供FFT实现,所以得考虑第三方。 接下来,用户可能希望知道具体的集成方法。常见的FFTW、KissFFT、Eigen FFT等。FFTW功能强大但需要处理许可证问题,KissFFT更轻量,适合嵌入式或跨平台。需要说明如何将这些集成到Qt项目中。 然后,用户可能需要示例代码。比如使用KissFFT,在.pro文件中添加包含路径和源文件,然后编写FFT处理的代码。需要注意复数数组的创建和释放,避免内存泄漏。 另外,Qt的信号与槽机制可能用于处理实时数据,比如音频输入。这里可以提到QAudioInput和QIODevice的结合,将捕获的数据送入FFT处理。 还要注意性能问题,比如选择合适的FFT,处理实数输入时使用rfft,以及多线程优化。用户可能关心实时应用的延迟问题,需要建议避免动态内存分配,使用预分配的缓冲区。 最后,检查是否有常见的坑,比如的编译配置、数据对齐问题,或者采样率与FFT长度的匹配。确保示例代码的正确性,并提醒用户测试和验证结果。 总结步骤:介绍FFT用途,推荐,集成方法,代码示例,性能优化,注意事项。确保回答结构清晰,逐步引导用户实现Qt中的FFT应用。</think>在C++ Qt中实现FFT(快速傅里叶变换)需要结合数学或第三方。以下是分步骤说明: 1. **FFT原理基础** $$ X_k = \sum_{n=0}^{N-1} x_n \cdot e^{-i2\pi kn/N} $$ 离散傅里叶变换(DFT)的快速算法实现,时间复杂度为$$O(N\log N)$$ 2. **Qt项目集成FFT** - **推荐选择**: * FFTW(需编译安装) * KissFFT(单文件易集成) * Eigen FFT(需要Eigen 3.4+) ```cpp // 示例:使用KissFFT在Qt中的实现 #include "kiss_fft.h" QVector<qreal> performFFT(const QVector<qreal>& input) { int N = input.size(); kiss_fft_cfg cfg = kiss_fft_alloc(N, 0, 0, 0); kiss_fft_cpx* in = new kiss_fft_cpx[N]; kiss_fft_cpx* out = new kiss_fft_cpx[N]; // 填充输入数据(实数转复数) for(int i=0; i<N; ++i) { in[i].r = input[i]; in[i].i = 0; } kiss_fft(cfg, in, out); // 计算幅值 QVector<qreal> magnitudes; for(int i=0; i<N/2; ++i) { magnitudes.append(sqrt(out[i].r*out[i].r + out[i].i*out[i].i)); } delete[] in; delete[] out; kiss_fft_free(cfg); return magnitudes; } ``` 3. **Qt项目配置(.pro文件)** ```makefile INCLUDEPATH += /path/to/kissfft SOURCES += /path/to/kiss_fft.c ``` 4. **实时音频处理示例** ```cpp // 结合QAudioInput进行实时分析 QAudioFormat format; format.setSampleRate(44100); format.setChannelCount(1); format.setSampleSize(16); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::SignedInt); QAudioInput audioInput(format); QIODevice* inputDevice = audioInput.start(); QObject::connect(inputDevice, &QIODevice::readyRead, [&](){ QByteArray data = inputDevice->readAll(); QVector<qreal> samples = convertToSamples(data); // 16bit转浮点 QVector<qreal> spectrum = performFFT(samples); emit spectrumUpdated(spectrum); // 发送频谱信号 }); ``` 5. **性能优化建议** - 使用`kiss_fftr`进行实数FFT优化 - 预分配内存避免动态分配 - 多线程处理(QThreadPool + QRunnable) - SIMD指令优化(需支持) 6. **可视化实现** ```cpp // 使用QCustomPlot显示频谱 QCustomPlot *plot = new QCustomPlot; QCPBars *bars = new QCPBars(plot->xAxis, plot->yAxis); bars->setWidth(0.5); // 更新频谱显示槽函数 connect(this, &AudioAnalyzer::spectrumUpdated, [=](QVector<qreal> spectrum){ QVector<double> x, y; for(int i=0; i<spectrum.size(); ++i){ x.append(i); y.append(spectrum[i]); } bars->setData(x, y); plot->replot(); }); ``` **注意事项**: 1. 采样率与频率分辨率的关系:$$ \Delta f = \frac{f_s}{N} $$ 2. 加窗处理(汉宁窗等)减少频谱泄漏 3. 复数结果的处理需注意相位信息 4. 逆变换实现需保持正向/反向配置一致 建议先使用预先生成的测试信号(如正弦波叠加)验证FFT结果的正确性,再接入实时音频数据。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值