Qt-使用QAudioInput+QAudioOutput实现录音机功能(支持多个声卡)
相关资料:
https://blog.csdn.net/qq_45662588/article/details/115949124 原文地址
https://download.csdn.net/download/zhujianqiangqq/85460524 CSDN代码资包下载地址
实例代码:
.pro
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 QT += core gui 2 QT += multimedia multimediawidgets 3 4 5 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 6 7 CONFIG += c++11 8 9 # The following define makes your compiler emit warnings if you use 10 # any Qt feature that has been marked deprecated (the exact warnings 11 # depend on your compiler). Please consult the documentation of the 12 # deprecated API in order to know how to port your code away from it. 13 DEFINES += QT_DEPRECATED_WARNINGS 14 15 # You can also make your code fail to compile if it uses deprecated APIs. 16 # In order to do so, uncomment the following line. 17 # You can also select to disable deprecated APIs only up to a certain version of Qt. 18 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 19 20 SOURCES += \ 21 main.cpp \ 22 mainwindow.cpp 23 24 HEADERS += \ 25 mainwindow.h 26 27 FORMS += \ 28 mainwindow.ui 29 30 # Default rules for deployment. 31 qnx: target.path = /tmp/$${TARGET}/bin 32 else: unix:!android: target.path = /opt/$${TARGET}/bin 33 !isEmpty(target.path): INSTALLS += target
main.cpp
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "mainwindow.h" 2 3 #include <QApplication> 4 5 int main(int argc, char *argv[]) 6 { 7 QApplication a(argc, argv); 8 MainWindow w; 9 w.show(); 10 return a.exec(); 11 }
mainwindow.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #ifndef MAINWINDOW_H 2 #define MAINWINDOW_H 3 4 #include <QMainWindow> 5 #include <QtNetwork/QUdpSocket> 6 #include <QAudio> //这五个是QT处理音频的库 7 #include <QAudioFormat> 8 #include <QAudioInput> 9 #include <QAudioOutput> 10 #include <QIODevice> 11 #include <QFile> 12 #include <QTimer> 13 #include <QDir> 14 15 QT_BEGIN_NAMESPACE 16 namespace Ui { class MainWindow; } 17 QT_END_NAMESPACE 18 19 class MainWindow : public QMainWindow 20 { 21 Q_OBJECT 22 public: 23 void SetStyle(const QString &qssFile); 24 QFile sourceFile; // class member. 25 QFile destinationFile; // Class member 26 QAudioFormat auido_input_format; 27 QTimer timer_progressBar; 28 int progressBar_val; 29 MainWindow(QWidget *parent = nullptr); 30 ~MainWindow(); 31 QAudioInput *audio_in; 32 QAudioOutput *audio_out; 33 void Log_Display(QString text); 34 qint64 CreateWavFile(QString catheFileName , QString wavFileName); 35 QList<QAudioDeviceInfo> input_list; 36 QList<QAudioDeviceInfo> output_list; 37 private slots: 38 void update_progressBar(); 39 void on_pushButton_clicked(); 40 void stopRecording(); 41 void handleStateChanged_input(QAudio::State newState); 42 void handleStateChanged_out(QAudio::State newState); 43 void on_pushButton_2_clicked(); 44 45 void on_pushButton_3_clicked(); 46 47 private: 48 Ui::MainWindow *ui; 49 }; 50 #endif // MAINWINDOW_H
manwindow.cpp
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "mainwindow.h" 2 #include "ui_mainwindow.h" 3 4 5 //设置录音的时间--ms 6 #define AUDIO_INPUT_TIME 10000 7 8 9 //#define ANDROID_DEVICE 10 11 12 #ifdef ANDROID_DEVICE 13 //设置保存文件的路径 14 #define SAVE_FILE_PATH "/sdcard/DS_XIAOLONG/test.raw" 15 #else 16 //设置保存文件的路径 17 #define SAVE_FILE_PATH "test.pcm" 18 #define SAVE_WAV_FILE_PATH "test.wav" 19 #endif 20 21 22 /* 23 * 设置QT界面的样式 24 */ 25 void MainWindow::SetStyle(const QString &qssFile) 26 { 27 QFile file(qssFile); 28 if (file.open(QFile::ReadOnly)) { 29 QString qss = QLatin1String(file.readAll()); 30 qApp->setStyleSheet(qss); 31 QString PaletteColor = qss.mid(20,7); 32 qApp->setPalette(QPalette(QColor(PaletteColor))); 33 file.close(); 34 } 35 else 36 { 37 qApp->setStyleSheet(""); 38 } 39 } 40 41 //日志信息显示 42 void MainWindow::Log_Display(QString text) 43 { 44 ui->plainTextEdit->insertPlainText(text); 45 } 46 47 MainWindow::MainWindow(QWidget *parent) 48 : QMainWindow(parent) 49 , ui(new Ui::MainWindow) 50 { 51 ui->setupUi(this); 52 53 this->SetStyle(":/images/blue.css" ); //设置样式表 54 this->setWindowIcon(QIcon(":/images/log.ico")); //设置图标 55 this->setWindowTitle(QStringLiteral("录音机")); 56 57 //创建工作目录 58 #ifdef ANDROID_DEVICE 59 QDir dir; 60 if(!dir.exists("/sdcard/DS_XIAOLONG")) 61 { 62 if(dir.mkdir("/sdcard/DS_XIAOLONG")) 63 { 64 Log_Display("/sdcard/DS_XIAOLONG目录创建成功.\n"); 65 } 66 else 67 { 68 Log_Display("/sdcard/DS_XIAOLONG目录创建失败.\n"); 69 } 70 } 71 #endif 72 //进度条更新 73 progressBar_val=0; 74 ui->progressBar->setRange(0,AUDIO_INPUT_TIME); 75 ui->progressBar->setValue(0); 76 connect(&timer_progressBar, SIGNAL(timeout()), this, SLOT(update_progressBar())); 77 } 78 79 80 void MainWindow::stopRecording() 81 { 82 Log_Display(QStringLiteral("停止录音.\n")); 83 audio_in->stop(); 84 destinationFile.close(); 85 } 86 87 MainWindow::~MainWindow() 88 { 89 delete ui; 90 } 91 92 //录音状态 93 void MainWindow::handleStateChanged_input(QAudio::State newState) 94 { 95 switch (newState) { 96 case QAudio::StoppedState: 97 if (audio_in->error() != QAudio::NoError) { 98 // Error handling 99 Log_Display(QStringLiteral("录音出现错误.\n")); 100 } else { 101 // Finished recording 102 Log_Display(QStringLiteral("完成录音\n")); 103 //将PCM文件转为WAV文件 104 CreateWavFile(SAVE_FILE_PATH,SAVE_WAV_FILE_PATH); 105 } 106 break; 107 case QAudio::ActiveState: 108 // Started recording - read from IO device 109 Log_Display(QStringLiteral("开始从IO设备读取PCM声音数据.\n")); 110 break; 111 default: 112 // ... other cases as appropriate 113 break; 114 } 115 } 116 117 //开始采集音频数据 118 void MainWindow::on_pushButton_clicked() 119 { 120 static bool flag1=1; 121 if(flag1) //只需要运行一次 122 { 123 flag1=0; 124 //设置录音的格式 125 auido_input_format.setSampleRate(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率. 126 auido_input_format.setChannelCount(1); //将通道数设置为通道。 127 auido_input_format.setSampleSize(16); /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/ 128 auido_input_format.setCodec("audio/pcm"); //设置编码格式 129 auido_input_format.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序 130 auido_input_format.setSampleType(QAudioFormat::SignedInt); //样本类型 131 132 //选择默认设备作为输入源 133 //QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); 134 //选择声卡输入设备 135 QAudioDeviceInfo info=input_list.at(ui->comboBox_input->currentIndex()); 136 137 Log_Display(QStringLiteral("当前的录音设备的名字:%1\n").arg(info.deviceName())); 138 139 //判断输入的格式是否支持,如果不支持就使用系统支持的默认格式 140 if(!info.isFormatSupported(auido_input_format)) 141 { 142 auido_input_format=info.nearestFormat(auido_input_format); 143 /* 144 * 返回与系统支持的提供的设置最接近的QAudioFormat。 145 这些设置由所使用的平台/音频插件提供。 146 它们还取决于所使用的QAudio :: Mode。 147 */ 148 } 149 //当前设备支持的编码 150 Log_Display(QStringLiteral("当前设备支持的编码格式:\n")); 151 QStringList list=info.supportedCodecs(); 152 for(int i=0;i<list.size();i++) 153 { 154 Log_Display(list.at(i)+"\n"); 155 } 156 157 Log_Display(QStringLiteral("当前录音的采样率=%1\n").arg(auido_input_format.sampleRate())); 158 Log_Display(QStringLiteral("当前录音的通道数=%1\n").arg(auido_input_format.channelCount())); 159 Log_Display(QStringLiteral("当前录音的样本大小=%1\n").arg(auido_input_format.sampleSize())); 160 Log_Display(QStringLiteral("当前录音的编码格式=%1\n").arg(auido_input_format.codec())); 161 audio_in = new QAudioInput(info,auido_input_format, this); 162 connect(audio_in,SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged_input(QAudio::State))); 163 } 164 165 if(audio_in->state()==QAudio::StoppedState) 166 { 167 // qDebug()<<"没有处理任何数据.\n"; 168 //设置采集的时间 169 QTimer::singleShot(AUDIO_INPUT_TIME,this,SLOT(stopRecording())); 170 destinationFile.setFileName(SAVE_FILE_PATH); 171 destinationFile.open( QIODevice::WriteOnly | QIODevice::Truncate); 172 audio_in->start(&destinationFile); 173 progressBar_val=0; 174 ui->progressBar->setFormat(QStringLiteral("录音进度%p")); 175 timer_progressBar.start(1000); //开始定时器--显示进度条 176 } 177 } 178 179 //更新进度条 180 void MainWindow::update_progressBar() 181 { 182 progressBar_val+=1000; //1000ms 183 if(progressBar_val>=AUDIO_INPUT_TIME)timer_progressBar.stop(); 184 ui->progressBar->setValue(progressBar_val); 185 } 186 187 //开始播放音频 188 void MainWindow::on_pushButton_2_clicked() 189 { 190 static bool flag=1; 191 if(flag) 192 { 193 flag=0; 194 //QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); 195 //选择声卡输出设备 196 QAudioDeviceInfo info=output_list.at(ui->comboBox_output->currentIndex()); 197 198 if(!info.isFormatSupported(auido_input_format)) 199 { 200 Log_Display(QStringLiteral("后端不支持原始音频格式,无法播放音频.\n")); 201 return; 202 } 203 //当前设备支持的编码 204 Log_Display(QStringLiteral("当前设备支持的编码格式:\n")); 205 QStringList list=info.supportedCodecs(); 206 for(int i=0;i<list.size();i++) 207 { 208 Log_Display(list.at(i)+"\n"); 209 } 210 audio_out = new QAudioOutput(info,auido_input_format,this); 211 connect(audio_out,SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged_out(QAudio::State))); 212 } 213 sourceFile.setFileName(SAVE_FILE_PATH); 214 sourceFile.open(QIODevice::ReadOnly); 215 audio_out->start(&sourceFile); 216 progressBar_val=0; 217 ui->progressBar->setFormat(QStringLiteral("播放进度%p")); 218 timer_progressBar.start(1000); //开始定时器--显示进度条 219 } 220 221 //播放音频的反馈信息 222 void MainWindow::handleStateChanged_out(QAudio::State newState) 223 { 224 switch (newState){ 225 case QAudio::IdleState: 226 // Finished playing (no more data) 227 audio_out->stop(); 228 sourceFile.close(); 229 Log_Display(QStringLiteral("音频播放完成.\n")); 230 break; 231 232 case QAudio::StoppedState: 233 // Stopped for other reasons 234 if (audio_out->error() != QAudio::NoError) { 235 Log_Display(QStringLiteral("播放音频出现错误.\n")); 236 } 237 break; 238 default: 239 // ... other cases as appropriate 240 break; 241 } 242 } 243 244 //查询可用的音频设备列表 245 void MainWindow::on_pushButton_3_clicked() 246 { 247 input_list.clear(); 248 ui->comboBox_output->clear(); 249 foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) 250 { 251 Log_Display(QStringLiteral("声音输出设备:%1\n").arg(deviceInfo.deviceName())); 252 input_list.append(deviceInfo); 253 ui->comboBox_output->addItem(deviceInfo.deviceName()); 254 } 255 256 output_list.clear(); 257 ui->comboBox_input->clear(); 258 foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) 259 { 260 Log_Display(QStringLiteral("声音输入设备:%1\n").arg(deviceInfo.deviceName())); 261 output_list.append(deviceInfo); 262 ui->comboBox_input->addItem(deviceInfo.deviceName()); 263 } 264 265 } 266 267 struct WAVFILEHEADER 268 { 269 // RIFF 头 270 char RiffName[4]; 271 unsigned long nRiffLength; 272 273 // 数据类型标识符 274 char WavName[4]; 275 276 // 格式块中的块头 277 char FmtName[4]; 278 unsigned long nFmtLength; 279 280 // 格式块中的块数据 281 unsigned short nAudioFormat; 282 unsigned short nChannleNumber; 283 unsigned long nSampleRate; 284 unsigned long nBytesPerSecond; 285 unsigned short nBytesPerSample; 286 unsigned short nBitsPerSample; 287 288 // 数据块中的块头 289 char DATANAME[4]; 290 unsigned long nDataLength; 291 }; 292 293 294 // 将生成的.raw文件转成.wav格式文件; 295 qint64 MainWindow::CreateWavFile(QString PcmFileName,QString wavFileName) 296 { 297 // 开始设置WAV的文件头 298 WAVFILEHEADER WavFileHeader; 299 qstrcpy(WavFileHeader.RiffName,"RIFF"); 300 qstrcpy(WavFileHeader.WavName, "WAVE"); 301 qstrcpy(WavFileHeader.FmtName, "fmt "); 302 qstrcpy(WavFileHeader.DATANAME,"data"); 303 304 // 表示 FMT块 的长度 305 WavFileHeader.nFmtLength = 16; 306 // 表示 按照PCM 编码; 307 WavFileHeader.nAudioFormat = 1; 308 // 声道数目; 309 WavFileHeader.nChannleNumber = 1; 310 // 采样频率; 311 WavFileHeader.nSampleRate = 16000; 312 313 // nBytesPerSample 和 nBytesPerSecond这两个值通过设置的参数计算得到; 314 // 数据块对齐单位(每个采样需要的字节数 = 通道数 × 每次采样得到的样本数据位数 / 8 ) 315 WavFileHeader.nBytesPerSample = 2; 316 // 波形数据传输速率 317 // (每秒平均字节数 = 采样频率 × 通道数 × 每次采样得到的样本数据位数 / 8 = 采样频率 × 每个采样需要的字节数 ) 318 WavFileHeader.nBytesPerSecond = 32000; 319 320 // 每次采样得到的样本数据位数; 321 WavFileHeader.nBitsPerSample = 16; 322 323 QFile cacheFile(PcmFileName); 324 QFile wavFile(wavFileName); 325 326 if (!cacheFile.open(QIODevice::ReadWrite)) 327 { 328 return -1; 329 } 330 if (!wavFile.open(QIODevice::WriteOnly)) 331 { 332 return -2; 333 } 334 335 int nSize = sizeof(WavFileHeader); 336 qint64 nFileLen = cacheFile.bytesAvailable(); 337 338 WavFileHeader.nRiffLength = static_cast<unsigned long>(nFileLen - 8 + nSize); 339 //static_cast<类型>(变量); //新式的强制转换 340 WavFileHeader.nDataLength = static_cast<unsigned long>(nFileLen); 341 342 // 先将wav文件头信息写入,再将音频数据写入; 343 wavFile.write((const char *)&WavFileHeader,nSize); 344 wavFile.write(cacheFile.readAll()); 345 346 cacheFile.close(); 347 wavFile.close(); 348 349 return nFileLen; 350 }
mainwindow.ui
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <ui version="4.0"> 3 <class>MainWindow</class> 4 <widget class="QMainWindow" name="MainWindow"> 5 <property name="geometry"> 6 <rect> 7 <x>0</x> 8 <y>0</y> 9 <width>800</width> 10 <height>600</height> 11 </rect> 12 </property> 13 <property name="windowTitle"> 14 <string>MainWindow</string> 15 </property> 16 <widget class="QWidget" name="centralwidget"> 17 <widget class="QPlainTextEdit" name="plainTextEdit"> 18 <property name="geometry"> 19 <rect> 20 <x>20</x> 21 <y>110</y> 22 <width>671</width> 23 <height>191</height> 24 </rect> 25 </property> 26 </widget> 27 <widget class="QProgressBar" name="progressBar"> 28 <property name="geometry"> 29 <rect> 30 <x>20</x> 31 <y>310</y> 32 <width>671</width> 33 <height>23</height> 34 </rect> 35 </property> 36 <property name="value"> 37 <number>24</number> 38 </property> 39 </widget> 40 <widget class="QComboBox" name="comboBox_input"> 41 <property name="geometry"> 42 <rect> 43 <x>30</x> 44 <y>20</y> 45 <width>661</width> 46 <height>22</height> 47 </rect> 48 </property> 49 </widget> 50 <widget class="QComboBox" name="comboBox_output"> 51 <property name="geometry"> 52 <rect> 53 <x>30</x> 54 <y>50</y> 55 <width>661</width> 56 <height>22</height> 57 </rect> 58 </property> 59 </widget> 60 <widget class="QPushButton" name="pushButton"> 61 <property name="geometry"> 62 <rect> 63 <x>140</x> 64 <y>80</y> 65 <width>121</width> 66 <height>20</height> 67 </rect> 68 </property> 69 <property name="text"> 70 <string>开始采集音频数据</string> 71 </property> 72 </widget> 73 <widget class="QPushButton" name="pushButton_2"> 74 <property name="geometry"> 75 <rect> 76 <x>410</x> 77 <y>80</y> 78 <width>111</width> 79 <height>20</height> 80 </rect> 81 </property> 82 <property name="text"> 83 <string>开始播放音频</string> 84 </property> 85 </widget> 86 <widget class="QPushButton" name="pushButton_3"> 87 <property name="geometry"> 88 <rect> 89 <x>270</x> 90 <y>0</y> 91 <width>171</width> 92 <height>20</height> 93 </rect> 94 </property> 95 <property name="text"> 96 <string>查询可用的音频设备列表</string> 97 </property> 98 </widget> 99 </widget> 100 <widget class="QMenuBar" name="menubar"> 101 <property name="geometry"> 102 <rect> 103 <x>0</x> 104 <y>0</y> 105 <width>800</width> 106 <height>22</height> 107 </rect> 108 </property> 109 </widget> 110 <widget class="QStatusBar" name="statusbar"/> 111 </widget> 112 <resources/> 113 <connections/> 114 </ui>
-----------------------------------以下是学习资料--------------------------------------
本章需要用到的类如下:
QAudioDeviceInfo类提供音频输出设备
QAudioFormat类提供音频参数设置
QAudioOutput类提供了用于将PCM原始音频数据发送到音频输出设备的接口。
第一步:
QT += multimedia
第二步:
foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
{
qDebug() << "Device name: " << deviceInfo.deviceName();
}
作者:疯狂Delphi
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
欢迎关注我,一起进步!扫描下方二维码即可加我
![](https://images.cnblogs.com/cnblogs_com/FKdelphi/1101510/o_200417075318%E5%8A%A0%E6%88%91QQ.jpg)
![](https://images.cnblogs.com/cnblogs_com/FKdelphi/1101510/o_200417080925%E5%8A%A0%E6%88%91%E5%BE%AE%E4%BF%A1.jpg)