qt6 多媒体开发代码分析(三、音频采集)

编程入门 行业动态 更新时间:2024-10-25 13:21:08

qt6 多媒体开发代码分析(三、<a href=https://www.elefans.com/category/jswz/34/1768490.html style=音频采集)"/>

qt6 多媒体开发代码分析(三、音频采集)

 QAudioSource *audioSource; //用于采集原始音频

QAudioSink   *audioSink;   //用于播放原始音频

几个概念:

音频的采样频率是指每秒钟对信号进行采样的次数,单位为赫兹(Hz)。采样频率决定了数字音频中能够表示的最高频率范围。

常见的音频采样频率有 8 kHz、16 kHz、44.1 kHz、48 kHz 等。其中,CD音质标准为44.1 kHz,广播和视频制作常用的采样频率为48 kHz。

较低的采样频率会导致高频信号无法还原,造成音频质量损失;而较高的采样频率可以更准确地还原原始音频信号,但会占用更多的存储空间。

选择适当的采样频率需要考虑信号的频率范围和所需的音频质量。一般来说,对于人耳可接受的音频,采样频率在 20 kHz 以上就能较好地还原信号。

音频采样点格式是指用于描述数字音频样本值的数据类型和位数。通常情况下,一次采样会生成一个或多个采样点,每个采样点的值用此格式进行编码。

常见的采样点格式有以下几种:

  1. 整数:采样点用有符号或无符号的整数表示,其中最常见的是16位或24位的整数。这种格式比较简单,但可能会出现量化误差。

  2. 浮点数:采样点以浮点数格式表示。浮点数表示方法可以更精确地还原原始音频信号,避免了量化误差问题,因此在音频编辑和处理中广泛应用。

  3. DSD:一种基于脉冲密度调制(Pulse Density Modulation,简称PDM)的数字声音编码方式,具有高采样率和分辨率,被广泛应用于SACD等高保真数字音频系统。

在选择采样点格式时需要根据所需的音质和存储空间要求进行平衡。一般来说,采用更高的采样点格式能够更好地还原音频信号,但会占用更多的存储空间。

音频每采样点字节数是指描述单个采样点所需的字节数。它是由采样点格式和通道数两个因素决定的,通常以字节为单位进行计算。

例如,16位整数格式的单声道(即只有一个通道)音频,每个采样点需要 2 个字节(16位=2个字节)来描述;而双声道音频则需要 4 个字节(即每个采样点包含左右两个通道,每个通道使用2个字节)。同样地,24位整数格式的单声道音频,每个采样点需要 3 个字节,而双声道则需要 6 个字节。

需要注意的是,除了采样点本身的字节数外,还需要考虑采样率和通道数等因素对于音频数据总大小的影响。同时,不同的采样格式可能存在字节对齐等问题,也需要进行相应的处理。

音频采样中的每帧字节数是指描述一帧音频数据所需的字节数,它由采样点格式、通道数和每个通道中的采样点数三个因素决定。

一帧音频数据包含多个采样点,其数量等于每个通道中的采样点数。例如,对于一个双声道、每秒采样率为 44100 次、16 位整数格式的音频文件,每帧的字节数就是 4 × 2 = 8 字节。其中 4 表示每个采样点需要占用 2 个字节(16 位),同时每帧中有两个通道,因此需要乘以 2。

帧(Frame)是一种音频数据的组织形式,通常每帧包含多个采样点,用于描述一段时间内的连续音频数据。在数字音频处理中,帧的大小与数据传输、存储和处理等方面都有重要的应用。在具体实现时,采样格式的字节对齐方式也会对帧大小产生影响,需注意相应的处理。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include    <QMainWindow>
#include    <QtCharts>
#include    <QtMultimedia>
#include    "tmydevice.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTprivate:const qint64  m_bufferSize=10000;   //缓冲区大小,字节数bool  m_isWorking=false;    //是否正在采集或播放QLineSeries  *lineSeries;   //曲线序列QAudioSource *audioSource; //用于采集原始音频TMyDevice    *myDevice;    //用于显示的IODeviceQAudioSink   *audioSink;   //用于播放原始音频QFile  sinkFileDevice;     //用于audioSink的文件设备void iniChart();    //初始化图表void closeEvent(QCloseEvent *event);    //close事件处理函数public:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:
//自定义槽函数void    do_IODevice_update(qint64 blockSize);void    do_sink_stateChanged(QAudio::State state);void on_actStart_triggered();void on_actStop_triggered();//    void on_actDeviceTest_triggered();void on_btnGetFile_clicked();void on_chkBoxSaveToFile_clicked(bool checked);void on_actTest_triggered();void on_comboSampFormat_currentIndexChanged(int index);void on_spinChanCount_valueChanged(int arg1);void on_actPlayFile_triggered();void on_actPreferFormat_triggered();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

这是一个名为MainWindow的类的头文件 mainwindow.h。它继承自 QMainWindow 类,并包含了一些私有成员变量和函数来管理主窗口的操作和状态。

以下是该类的一些重要成员:

  • m_bufferSize:表示缓冲区大小的常量,以字节为单位。
  • m_isWorking:表示当前是否正在进行采集或播放操作的布尔变量。
  • lineSeries:一个指向 QLineSeries 类的指针,用于绘制曲线图的序列。
  • audioSource:用于采集原始音频的 QAudioSource 对象。
  • myDevice:用于显示的 TMyDevice 对象。
  • audioSink:用于播放原始音频的 QAudioSink 对象。
  • sinkFileDevice:用于 audioSink 的文件设备的 QFile 对象。

此外,该类还有一些私有函数,用于初始化图表、处理窗口关闭事件等。

头文件中还定义了一些槽函数,用于响应与用户界面的交互操作,例如开始采集、停止采集、选取文件等。

总而言之,mainwindow.h 定义了 MainWindow 类,负责管理主窗口的功能和状态,并与用户界面进行交互。

#include "mainwindow.h"
#include "ui_mainwindow.h"#include    <QMessageBox>void MainWindow::iniChart()
{//创建图表QChart *chart = new QChart;chart->setTitle("音频输入原始信号");ui->chartView->setChart(chart);lineSeries= new QLineSeries();   //创建序列chart->addSeries(lineSeries);lineSeries->setUseOpenGL(true);  //使用OpenGL加速QValueAxis *axisX = new QValueAxis;  //X坐标轴axisX->setRange(0, m_bufferSize);    //X数据范围axisX->setLabelFormat("%g");axisX->setTitleText("Samples");QValueAxis *axisY = new QValueAxis;  //Y坐标轴axisY->setRange(0, 256);             //UInt8采样,数据范围0~255axisY->setTitleText("Audio Level");chart->addAxis(axisX,Qt::AlignBottom);chart->addAxis(axisY,Qt::AlignLeft);lineSeries->attachAxis(axisX);lineSeries->attachAxis(axisY);chart->legend()->hide();        //隐藏图例
}MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);iniChart();     //创建图表QAudioDevice device=QMediaDevices::defaultAudioInput();  //默认音频输入设备if (device.isNull()){ui->actStart->setEnabled(false);ui->groupBoxDevice->setTitle("音频输入设置(无设备)");QMessageBox::information(this,"提示","无音频输入设备");return;}ui->comboDevices->addItem(device.description());        //只添加默认音频输入设备//首选音频格式QAudioFormat audioFormat =device.preferredFormat();     //音频输入设备的首选音频格式ui->comboSampFormat->setCurrentIndex(audioFormat.sampleFormat());   //采样点格式ui->spinSampRate->setValue(audioFormat.sampleRate());   //采样频率int minRate=device.minimumSampleRate();int maxRate=device.maximumSampleRate();ui->labSampRateRange->setText(QString::asprintf("范围: %d~%d",minRate,maxRate));ui->spinSampRate->setRange(minRate, maxRate);ui->spinChanCount->setValue(audioFormat.channelCount()); //通道个数int minChan=device.minimumChannelCount();int maxChan=device.maximumChannelCount();ui->labChanCountRange->setText(QString::asprintf("范围:%d~%d",minChan,maxChan));ui->spinChanCount->setRange(minChan, maxChan);ui->spinBytesPerSamp->setValue(audioFormat.bytesPerSample());  //每个采样点的字节数ui->spinBytesPerFrame->setValue(audioFormat.bytesPerFrame());  //每帧字节数
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::do_IODevice_update(qint64 blockSize)
{float time=audioSource->processedUSecs()/1000; //msQString str=QString::asprintf("已录制时间 =%.1f 秒", time/1000);ui->labBufferSize->setText(str);ui->labBlockSize->setText(QString("实际数据块字节数=%1").arg(blockSize));
}void MainWindow::do_sink_stateChanged(QAudio::State state)
{if (state == QAudio::IdleState)  //播放结束后的空闲状态{sinkFileDevice.close();     //关闭文件audioSink->stop();          //停止播放audioSink->deleteLater();   //在主循环里删除对象ui->actPlayFile->setEnabled(true);m_isWorking=false;          //表示没有设备在工作了}
}void MainWindow::on_actStart_triggered()
{//“开始采集”按钮if(ui->comboSampFormat->currentIndex()==0){QMessageBox::critical(this,"错误","请设置采样点格式");return;}bool saveToFile=ui->chkBoxSaveToFile->isChecked();  //是否保存到文件QString fileName= ui->editFileName->text().trimmed();if ((saveToFile) && (fileName.isEmpty())){QMessageBox::critical(this,"错误","请设置要保存的文件");return;}//设置音频格式QAudioFormat daqFormat;daqFormat.setSampleRate(ui->spinSampRate->value());      //采样频率daqFormat.setChannelCount(ui->spinChanCount->value());   //通道个数int index=ui->comboSampFormat->currentIndex();daqFormat.setSampleFormat(QAudioFormat::SampleFormat(index));  //采样点格式audioSource = new QAudioSource(daqFormat, this);audioSource->setBufferSize(m_bufferSize);    //设置缓冲区大小,如10000audioSource->setVolume(1);                   //设置录音音量, 0~1myDevice = new TMyDevice(this);connect(myDevice,&TMyDevice::updateBlockSize,this, &MainWindow::do_IODevice_update);bool showWave=ui->chkBoxShowWave->isChecked();      //是否实时显示曲线myDevice->openDAQ(m_bufferSize,showWave,lineSeries,saveToFile, fileName);audioSource->start(myDevice); //以流设备作为参数,开始录入音频输入数据m_isWorking=true;       //表示有设置在工作,不允许关闭窗口ui->actStart->setEnabled(false);ui->actStop->setEnabled(true);ui->actPlayFile->setEnabled(false);     //"播放文件"按钮
}void MainWindow::on_actStop_triggered()
{//"停止采集"按钮audioSource->stop();   //停止采集myDevice->closeDAQ();  //IO设备停止记录delete myDevice;       //删除对象delete audioSource;    //删除对象m_isWorking=false;      //表示没有设备在工作了ui->actStart->setEnabled(true);ui->actStop->setEnabled(false);ui->actPlayFile->setEnabled(ui->chkBoxSaveToFile->isChecked()); //"播放文件"按钮
}void MainWindow::on_btnGetFile_clicked()
{//"数据文件" 按钮QString curPath=QDir::currentPath();//获取系统当前目录QString dlgTitle="选择输出文件"; //对话框标题QString filter="原始音频数据文件(*.raw);;所有文件(*.*)"; //文件过滤器QString selectedFile=QFileDialog::getSaveFileName(this,dlgTitle,curPath,filter);if (!selectedFile.isEmpty()){ui->editFileName->setText(selectedFile);QFileInfo  fileInfo(selectedFile);QDir::setCurrent(fileInfo.absolutePath());}
}void MainWindow::on_chkBoxSaveToFile_clicked(bool checked)
{//"数据记录到文件" CheckBoxui->btnGetFile->setEnabled(checked);ui->editFileName->setEnabled(checked);
}void MainWindow::on_actTest_triggered()
{//"测试音频格式"按钮QAudioFormat daqFormat;daqFormat.setSampleRate(ui->spinSampRate->value());     //采样频率daqFormat.setChannelCount(ui->spinChanCount->value());  //通道数int index=ui->comboSampFormat->currentIndex();daqFormat.setSampleFormat(QAudioFormat::SampleFormat(index));   //采样点格式QAudioDevice device=QMediaDevices::defaultAudioInput();  //默认音频输入设备if (device.isFormatSupported(daqFormat))QMessageBox::information(this,"提示","默认设备支持所选格式 ");elseQMessageBox::critical(this,"提示","默认设备不支持所选格式 ");
}void MainWindow::on_comboSampFormat_currentIndexChanged(int index)
{//"采样点格式" 下拉列表框switch(index)    //采样点格式, 更新" 每采样点字节数" SpinBox的显示值{case 0:     //Unknowcase 1:     //UInt8ui->spinBytesPerSamp->setValue(1); break;case 2:     //Int16ui->spinBytesPerSamp->setValue(2); break;case 3:     //Int32case 4:     //Floatui->spinBytesPerSamp->setValue(4);}int bytes=ui->spinChanCount->value() * ui->spinBytesPerSamp->value();ui->spinBytesPerFrame->setValue(bytes);   //更新每帧字节数bool canShowWave= (index==1) && (ui->spinChanCount->value() ==1);  //是否可以显示曲线ui->chkBoxShowWave->setEnabled(canShowWave);   //只有UInt8, 通道数为1 时才能显示曲线if (!canShowWave)ui->chkBoxShowWave->setChecked(false);    //不能显示曲线
}void MainWindow::on_spinChanCount_valueChanged(int arg1)
{//"通道数"  SpinBoxui->spinBytesPerFrame->setValue( arg1 * (ui->spinBytesPerSamp->value()));  //计算每帧字节数bool canShowWave= (arg1==1) && (ui->comboSampFormat->currentIndex() ==1);ui->chkBoxShowWave->setEnabled(canShowWave);   //只有UInt8, 通道数为1 时才能显示曲线if (!canShowWave)ui->chkBoxShowWave->setChecked(false);     //不能显示曲线
}void MainWindow::closeEvent(QCloseEvent *event)
{if (m_isWorking){QMessageBox::information(this,"提示","正在采集或播放音频,不允许退出");event->ignore();}elseevent->accept();
}void MainWindow::on_actPlayFile_triggered()
{//"播放文件"按钮QString filename =ui->editFileName->text().trimmed();if (filename.isEmpty() ||   !QFile::exists(filename))  //检查文件{QMessageBox::critical(this,"错误","文件名为空,或文件不存在");return;}sinkFileDevice.setFileName(filename);   //文件IO设备设置文件if ( !sinkFileDevice.open(QIODeviceBase::ReadOnly))   //以只读方式打开{QMessageBox::critical(this,"错误","打开文件时出现错误,无法播放");return;}QAudioFormat format;    //使用界面上的音频格式参数format.setSampleRate(ui->spinSampRate->value());format.setChannelCount(ui->spinChanCount->value());int index=ui->comboSampFormat->currentIndex();format.setSampleFormat(QAudioFormat::SampleFormat(index));QAudioDevice audioDevice= QMediaDevices::defaultAudioOutput();  //默认的音频输出设备if (!audioDevice.isFormatSupported(format))   //是否支持此音频格式参数{QMessageBox::critical(this,"错误","播放设备不支持此音频格式设置,无法播放");return;}audioSink =new QAudioSink(format, this);   //创建audioSinkconnect(audioSink, &QAudioSink::stateChanged,this, &MainWindow::do_sink_stateChanged);audioSink->start(&sinkFileDevice);    //开始播放m_isWorking=true;       //表示有设备在工作,不能关闭窗口ui->actPlayFile->setEnabled(false);
}void MainWindow::on_actPreferFormat_triggered()
{//"首先音频格式"按钮, 显示默认音频输入设备的首选音频格式QAudioFormat audioFormat =QMediaDevices::defaultAudioInput().preferredFormat();ui->spinSampRate->setValue(audioFormat.sampleRate());   //采样频率ui->comboSampFormat->setCurrentIndex(audioFormat.sampleFormat());   //采样点格式ui->spinChanCount->setValue(audioFormat.channelCount());            //通道个数ui->spinBytesPerSamp->setValue(audioFormat.bytesPerSample());       //每个采样点的字节数ui->spinBytesPerFrame->setValue(audioFormat.bytesPerFrame());       //每帧字节数
}

这段代码是一个音频录制和播放的程序,采用了Qt框架。下面是对代码中各部分功能的说明:

  1. iniChart()函数:该函数用于创建图表并初始化相关设置,包括设置标题、坐标轴范围和样式等。

  2. MainWindow类的构造函数:初始化界面并调用iniChart()函数创建图表,然后获取默认音频输入设备的信息并显示在界面上。

  3. do_IODevice_update()函数:在录制过程中更新界面上的录制时间和数据块字节数。

  4. do_sink_stateChanged()函数:当播放设备状态改变时触发,用于处理播放结束后的操作,包括关闭文件、停止播放和删除对象等。

  5. on_actStart_triggered()函数:当点击“开始采集”按钮时触发,根据用户设置的音频格式和保存文件选项进行录制设置,并启动音频输入设备进行录制。

  6. on_actStop_triggered()函数:当点击“停止采集”按钮时触发,停止音频输入设备的录制和关闭相关对象。

  7. on_btnGetFile_clicked()函数:当点击“数据文件”按钮时触发,用于选择保存录制数据的文件路径。

  8. on_chkBoxSaveToFile_clicked()函数:当点击“数据记录到文件”复选框时触发,根据复选框状态设置相关控件的可用性。

  9. on_actTest_triggered()函数:当点击“测试音频格式”按钮时触发,根据用户设置的音频格式和默认音频输入设备的支持情况弹出相应的提示信息。

  10. on_comboSampFormat_currentIndexChanged()函数:当选择采样点格式的下拉列表框的选项发生变化时触发,根据选项来更新每个采样点的字节数,并根据当前选择的采样点格式和通道数设置曲线显示的可用性。

  11. on_spinChanCount_valueChanged()函数:当通道数的值改变时触发,根据新值更新每帧字节数,并根据当前选择的采样点格式和通道数设置曲线显示的可用性。

  12. closeEvent()函数:在窗口关闭事件发生时触发,如果有设备正在工作,则弹出提示信息并阻止窗口关闭。

  13. on_actPlayFile_triggered()函数:当点击“播放文件”按钮时触发,根据用户设置的文件名和音频格式进行播放设置,并开始播放指定的音频文件。

  14. on_actPreferFormat_triggered()函数:当点击“首选音频格式”按钮时触发,获取默认音频输入设备的首选音频格式并显示在界面上。

这段代码实现了录制和播放音频的基本功能,并提供了图表显示、文件保存和音频格式设置等附加功能。

更多推荐

qt6 多媒体开发代码分析(三、音频采集)

本文发布于:2023-12-08 03:44:07,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1672259.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:音频   多媒体   代码

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!