Qt-使用QAudioInput+QAudioOutput实现录音机功能(支持多个声卡)

 

相关资料:

https://blog.csdn.net/qq_45662588/article/details/115949124     原文地址

https://download.csdn.net/download/zhujianqiangqq/85460524         CSDN代码资包下载地址

实例代码:

.pro

 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
View Code

main.cpp

 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 }
View Code

mainwindow.h

 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
View Code

manwindow.cpp

  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 }
View Code

mainwindow.ui

  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>
View Code

 

 

-----------------------------------以下是学习资料--------------------------------------

本章需要用到的类如下:
QAudioDeviceInfo类提供音频输出设备
QAudioFormat类提供音频参数设置
QAudioOutput类提供了用于将PCM原始音频数据发送到音频输出设备的接口。

第一步:
QT += multimedia

第二步:
foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
{
qDebug() << "Device name: " << deviceInfo.deviceName();
}

posted on 2021-08-13 14:52  疯狂delphi  阅读(1131)  评论(0编辑  收藏  举报

导航