基于Qt框架的音频采集与播放工具

目录

核心功能

音频采集

实时波形显示

音频回放

参数配置与验证

关键技术实现

音频数据处理

动态图表

线程与事件处理

错误处理

潜在改进方向

总结


该程序是一个基于Qt框架的音频采集与播放工具,支持实时波形显示、数据保存及回放。以下是实现思路与方法:

核心功能

  1. 音频采集

    • 通过QAudioSource从默认音频输入设备(如麦克风)实时获取音频数据。

    • 支持自定义采样率、通道数、采样格式等参数。

    • 数据可保存为.raw文件,便于后续分析或回放。

  2. 实时波形显示

    • 使用QChart动态绘制音频波形。

    • 仅支持UInt8格式单通道数据的实时显示(通过QLineSeries实现)。

  3. 音频回放

    • 通过QAudioSink播放已保存的音频文件,支持与采集时相同的音频格式参数。

    • 实现代码:

      //播放文件
      void MainWindow::on_actPlayFile_triggered()
      {
          QString filename=ui->editFileName->text().trimmed();
          if(filename.isEmpty()){
              QMessageBox::critical(this,"警告","未设置播放文件");
              return;
          }
          if(!QFile::exists(filename)){
              QMessageBox::critical(this,"警告","文件不可用");
              return;
          }
      
      
          sinkfileDevice.setFileName(filename);
          if(!sinkfileDevice.open(QIODeviceBase::ReadOnly)){
              QMessageBox::critical(this,"警告","无法打开指定文件!");
              return;
          }
      
          QAudioFormat format;
          format.setChannelCount(ui->spinChanCount->value());
          format.setSampleRate(ui->spinSampRate->value());
          int index=ui->comboSampFormat->currentIndex();
          format.setSampleFormat(QAudioFormat::SampleFormat(index));
          audiosink=new QAudioSink(format,this);
          audiosink->start(&sinkfileDevice);
          m_isworking=true;
          ui->actPlayFile->setEnabled(false);
          ui->actStart->setEnabled(false);
          connect(audiosink,&QAudioSink::stateChanged,this,&MainWindow::do_stateChanged);
      }

  4. 参数配置与验证

    • 提供界面控件调整音频格式参数(如采样率、通道数)。

    • 支持测试当前参数是否被设备支持。

关键技术实现

  1. 音频数据处理

    • 自定义TMyDevice(继承自QIODevice):

      • writeData()中处理输入数据:

        • 文件保存:直接将二进制数据写入文件。

        • 波形更新:将音频数据转换为QPointF点集,动态更新图表。

      • 数据截断逻辑:当数据量超过图表容量(m_range)时:

        • 截取旧数据的后m_range - len个点。

        • 调整旧数据的X坐标(从0开始),避免新数据堆积在右侧。

        • 追加新数据并更新序列,确保图表始终显示最新内容。

        • 实现代码如下:

          if(save_to_file){
                  m_file.write(data,len);
              }
              if(draw_chart){
                  //更新序列用到qlist容器
                  QList<QPointF>points;
                  //预分配空间
                  points.reserve(m_range);
                  //原来的序列中点的数量
                  int oldcount =series->points().size();
                  //如果序列中数据满了,截断从len到m_range中的数据放到list中
                  if(oldcount>=m_range){
                      const int keepcount = m_range - len;  // 需要保留的旧数据数量
                      points = series->points().mid(oldcount - keepcount);  // 正确截取旧数据
                      for (int i = 0; i < points.size(); ++i) {
                          points[i].setX(i);
                      }
          
                  }
                  else {
                      points=series->points();
                  }
                  //如果当前的序列没满,就把新的数据补上,如果满了循环不会执行
                  int curcount=points.size();
                  for (int k = 0; k < len; k++) {
                      qreal x = (curcount + k) % m_range;  // X 坐标循环在 0~m_range-1 之间
                      quint8 value = static_cast<quint8>(data[k]);  // 确保 Y 值正确转换
                      points.append(QPointF(x, value));
                  }
          
                  //更新序列
                  series->replace(points);
                  emit updateBlockSize(len);
                  return len;

          这是我写的写入函数,其中更新图表数据的功能是首先如果有新的数据要进序列,我们就把原来序列前面去掉一部分,后面的数据往前走,给新数据留出相应的位置,这就到了数据截断这有一个坑,按照deepseek分析是调用point.mid()函数直接截取比一个一个点填入更为高效,但问题就是出现在这里,这里的QList容器装的数据类型是qpointf,他是数据点,是一个二维的,之前我光想着截取下来替换series中的数据点,但没想到mid()是截取了数据点的完整数据,即(x,y)全截取下来了,这就造成了一个问题,我们截取下来的数据点要往左移,右边append新的数据点,如果未更新x值,就会造成图表显示异常,他的曲线全部拥挤在图标右边。 解决方法就是把list容器中的值遍历一遍,给他们的x值重新赋值。这里长教训了,要注意截取的数据是什么类型。                        

  2. 动态图表

    • 初始化inichart()创建QLineSeries并绑定坐标轴。

    • 性能优化:使用series->replace(points)批量更新数据,而非逐点添加。

    • 坐标轴范围:X轴固定为0 ~ m_maxsize(2000),Y轴固定为0 ~ 256(适配UInt8数据)。

    • 实现代码:

      //mainwindow.cpp
      void MainWindow::inichart()
      {
          QChart *chart=new QChart;
          chart->setTitle("音频输入原始信号");
          ui->chartView->setChart(chart);
      
          series =new QLineSeries();
          chart->addSeries(series);
          //series->setUseOpenGL(true);//opengl加速
      
          //坐标轴设置
          QValueAxis *axisX=new QValueAxis;
          axisX->setRange(0,m_maxsize);
          axisX->setLabelFormat("%g");
          axisX->setTitleText("samples");
          QValueAxis *axisY=new QValueAxis;
          axisY->setRange(0,256);//chart只能使用UTF_8
          axisY->setTitleText("Audio Level");
      
          chart->addAxis(axisX,Qt::AlignBottom);
          chart->addAxis(axisY,Qt::AlignLeft);
      
          series->attachAxis(axisX);
          series->attachAxis(axisY);
      }
      

      m_maxsize是预设值,后面可以被覆盖,y轴只能是0~256,为了适配UTF—8。

  3. 线程与事件处理

    • 主线程处理UI交互与数据更新,未单独分离数据处理线程(可能导致界面卡顿,但代码未体现)。

    • 通过信号updateBlockSize更新录制时间与数据块大小。

  4. 错误处理

    • 设备检测:若无可用音频设备,禁用功能并提示。

    • 文件操作:检查文件路径有效性,避免无效写入。

潜在改进方向

  1. 多线程优化:将数据写入与图表更新移至子线程,避免主线程阻塞。

  2. 扩展格式支持:当前仅支持UInt8单通道波形显示,可适配更多格式(如Int16)。

  3. 错误恢复机制:增加文件写入失败的回滚逻辑,确保数据完整性。

  4. 性能监控:添加CPU/内存占用显示,帮助用户优化参数配置。

总结

该程序通过Qt多媒体模块实现了音频采集、实时波形显示与回放功能,核心难点在于动态数据的截断与图表更新。通过自定义TMyDevice类高效处理数据流,结合QChart的批量更新机制,确保了实时性的同时解决了旧版本的数据显示异常问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值