一、環境介紹
操作系統: windows10
QT版本: 5.12
二、錄音機效果
三、核心代碼
mainwindow.h代碼:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork/QUdpSocket>
#include <QAudio> //這五個是QT處理音頻的庫
#include <QAudioFormat>
#include <QAudioInput>
#include <QAudioOutput>
#include <QIODevice>
#include <QFile>
#include <QTimer>
#include <QDir>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
void SetStyle(const QString &qssFile);
QFile sourceFile; // class member.
QFile destinationFile; // Class member
QAudioFormat auido_input_format;
QTimer timer_progressBar;
int progressBar_val;
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QAudioInput *audio_in;
QAudioOutput *audio_out;
void Log_Display(QString text);
qint64 CreateWavFile(QString catheFileName , QString wavFileName);
QList<QAudioDeviceInfo> input_list;
QList<QAudioDeviceInfo> output_list;
private slots:
void update_progressBar();
void on_pushButton_clicked();
void stopRecording();
void handleStateChanged_input(QAudio::State newState);
void handleStateChanged_out(QAudio::State newState);
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp代碼
#include "mainwindow.h"
#include "ui_mainwindow.h"
//設置錄音的時間--ms
#define AUDIO_INPUT_TIME 10000
//#define ANDROID_DEVICE
#ifdef ANDROID_DEVICE
//設置保存文件的路徑
#define SAVE_FILE_PATH "/sdcard/DS_XIAOLONG/test.raw"
#else
//設置保存文件的路徑
#define SAVE_FILE_PATH "test.pcm"
#define SAVE_WAV_FILE_PATH "test.wav"
#endif
/*
* 設置QT界面的樣式
*/
void MainWindow::SetStyle(const QString &qssFile)
{
QFile file(qssFile);
if (file.open(QFile::ReadOnly)) {
QString qss = QLatin1String(file.readAll());
qApp->setStyleSheet(qss);
QString PaletteColor = qss.mid(20,7);
qApp->setPalette(QPalette(QColor(PaletteColor)));
file.close();
}
else
{
qApp->setStyleSheet("");
}
}
//日誌信息顯示
void MainWindow::Log_Display(QString text)
{
ui->plainTextEdit->insertPlainText(text);
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->SetStyle(":/images/blue.css" ); //設置樣式表
this->setWindowIcon(QIcon(":/images/log.ico")); //設置圖標
this->setWindowTitle("錄音機");
//創建工作目錄
#ifdef ANDROID_DEVICE
QDir dir;
if(!dir.exists("/sdcard/DS_XIAOLONG"))
{
if(dir.mkdir("/sdcard/DS_XIAOLONG"))
{
Log_Display("/sdcard/DS_XIAOLONG目錄創建成功.\n");
}
else
{
Log_Display("/sdcard/DS_XIAOLONG目錄創建失敗.\n");
}
}
#endif
//進度條更新
progressBar_val=0;
ui->progressBar->setRange(0,AUDIO_INPUT_TIME);
ui->progressBar->setValue(0);
connect(&timer_progressBar, SIGNAL(timeout()), this, SLOT(update_progressBar()));
}
void MainWindow::stopRecording()
{
Log_Display("停止錄音.\n");
audio_in->stop();
destinationFile.close();
}
MainWindow::~MainWindow()
{
delete ui;
}
//錄音狀態
void MainWindow::handleStateChanged_input(QAudio::State newState)
{
switch (newState) {
case QAudio::StoppedState:
if (audio_in->error() != QAudio::NoError) {
// Error handling
Log_Display("錄音出現錯誤.\n");
} else {
// Finished recording
Log_Display("完成錄音\n");
//將PCM文件轉爲WAV文件
CreateWavFile(SAVE_FILE_PATH,SAVE_WAV_FILE_PATH);
}
break;
case QAudio::ActiveState:
// Started recording - read from IO device
Log_Display("開始從IO設備讀取PCM聲音數據.\n");
break;
default:
// ... other cases as appropriate
break;
}
}
//開始採集音頻數據
void MainWindow::on_pushButton_clicked()
{
static bool flag1=1;
if(flag1) //只需要運行一次
{
flag1=0;
//設置錄音的格式
auido_input_format.setSampleRate(44100); //設置採樣率以對赫茲採樣。 以秒爲單位,每秒採集多少聲音數據的頻率.
auido_input_format.setChannelCount(1); //將通道數設置爲通道。
auido_input_format.setSampleSize(16); /*將樣本大小設置爲指定的sampleSize(以位爲單位)通常爲8或16,但是某些系統可能支持更大的樣本量。*/
auido_input_format.setCodec("audio/pcm"); //設置編碼格式
auido_input_format.setByteOrder(QAudioFormat::LittleEndian); //樣本是小端字節順序
auido_input_format.setSampleType(QAudioFormat::SignedInt); //樣本類型
//選擇默認設備作爲輸入源
//QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
//選擇聲卡輸入設備
QAudioDeviceInfo info=input_list.at(ui->comboBox_input->currentIndex());
Log_Display(tr("當前的錄音設備的名字:%1\n").arg(info.deviceName()));
//判斷輸入的格式是否支持,如果不支持就使用系統支持的默認格式
if(!info.isFormatSupported(auido_input_format))
{
auido_input_format=info.nearestFormat(auido_input_format);
/*
* 返回與系統支持的提供的設置最接近的QAudioFormat。
這些設置由所使用的平臺/音頻插件提供。
它們還取決於所使用的QAudio :: Mode。
*/
}
//當前設備支持的編碼
Log_Display("當前設備支持的編碼格式:\n");
QStringList list=info.supportedCodecs();
for(int i=0;i<list.size();i++)
{
Log_Display(list.at(i)+"\n");
}
Log_Display(tr("當前錄音的採樣率=%1\n").arg(auido_input_format.sampleRate()));
Log_Display(tr("當前錄音的通道數=%1\n").arg(auido_input_format.channelCount()));
Log_Display(tr("當前錄音的樣本大小=%1\n").arg(auido_input_format.sampleSize()));
Log_Display(tr("當前錄音的編碼格式=%1\n").arg(auido_input_format.codec()));
audio_in = new QAudioInput(info,auido_input_format, this);
connect(audio_in,SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged_input(QAudio::State)));
}
if(audio_in->state()==QAudio::StoppedState)
{
// qDebug()<<"沒有處理任何數據.\n";
//設置採集的時間
QTimer::singleShot(AUDIO_INPUT_TIME,this,SLOT(stopRecording()));
destinationFile.setFileName(SAVE_FILE_PATH);
destinationFile.open( QIODevice::WriteOnly | QIODevice::Truncate);
audio_in->start(&destinationFile);
progressBar_val=0;
ui->progressBar->setFormat("錄音進度%p");
timer_progressBar.start(1000); //開始定時器--顯示進度條
}
}
//更新進度條
void MainWindow::update_progressBar()
{
progressBar_val+=1000; //1000ms
if(progressBar_val>=AUDIO_INPUT_TIME)timer_progressBar.stop();
ui->progressBar->setValue(progressBar_val);
}
//開始播放音頻
void MainWindow::on_pushButton_2_clicked()
{
static bool flag=1;
if(flag)
{
flag=0;
//QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
//選擇聲卡輸出設備
QAudioDeviceInfo info=output_list.at(ui->comboBox_output->currentIndex());
if(!info.isFormatSupported(auido_input_format))
{
Log_Display("後端不支持原始音頻格式,無法播放音頻.\n");
return;
}
//當前設備支持的編碼
Log_Display("當前設備支持的編碼格式:\n");
QStringList list=info.supportedCodecs();
for(int i=0;i<list.size();i++)
{
Log_Display(list.at(i)+"\n");
}
audio_out = new QAudioOutput(info,auido_input_format,this);
connect(audio_out,SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged_out(QAudio::State)));
}
sourceFile.setFileName(SAVE_FILE_PATH);
sourceFile.open(QIODevice::ReadOnly);
audio_out->start(&sourceFile);
progressBar_val=0;
ui->progressBar->setFormat("播放進度%p");
timer_progressBar.start(1000); //開始定時器--顯示進度條
}
//播放音頻的反饋信息
void MainWindow::handleStateChanged_out(QAudio::State newState)
{
switch (newState){
case QAudio::IdleState:
// Finished playing (no more data)
audio_out->stop();
sourceFile.close();
Log_Display("音頻播放完成.\n");
break;
case QAudio::StoppedState:
// Stopped for other reasons
if (audio_out->error() != QAudio::NoError) {
Log_Display("播放音頻出現錯誤.\n");
}
break;
default:
// ... other cases as appropriate
break;
}
}
//查詢可用的音頻設備列表
void MainWindow::on_pushButton_3_clicked()
{
input_list.clear();
ui->comboBox_output->clear();
foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
{
Log_Display(tr("聲音輸出設備:%1\n").arg(deviceInfo.deviceName()));
input_list.append(deviceInfo);
ui->comboBox_output->addItem(deviceInfo.deviceName());
}
output_list.clear();
ui->comboBox_input->clear();
foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioInput))
{
Log_Display(tr("聲音輸入設備:%1\n").arg(deviceInfo.deviceName()));
output_list.append(deviceInfo);
ui->comboBox_input->addItem(deviceInfo.deviceName());
}
}
struct WAVFILEHEADER
{
// RIFF 頭
char RiffName[4];
unsigned long nRiffLength;
// 數據類型標識符
char WavName[4];
// 格式塊中的塊頭
char FmtName[4];
unsigned long nFmtLength;
// 格式塊中的塊數據
unsigned short nAudioFormat;
unsigned short nChannleNumber;
unsigned long nSampleRate;
unsigned long nBytesPerSecond;
unsigned short nBytesPerSample;
unsigned short nBitsPerSample;
// 數據塊中的塊頭
char DATANAME[4];
unsigned long nDataLength;
};
// 將生成的.raw文件轉成.wav格式文件;
qint64 MainWindow::CreateWavFile(QString PcmFileName,QString wavFileName)
{
// 開始設置WAV的文件頭
WAVFILEHEADER WavFileHeader;
qstrcpy(WavFileHeader.RiffName,"RIFF");
qstrcpy(WavFileHeader.WavName, "WAVE");
qstrcpy(WavFileHeader.FmtName, "fmt ");
qstrcpy(WavFileHeader.DATANAME,"data");
// 表示 FMT塊 的長度
WavFileHeader.nFmtLength = 16;
// 表示 按照PCM 編碼;
WavFileHeader.nAudioFormat = 1;
// 聲道數目;
WavFileHeader.nChannleNumber = 1;
// 採樣頻率;
WavFileHeader.nSampleRate = 16000;
// nBytesPerSample 和 nBytesPerSecond這兩個值通過設置的參數計算得到;
// 數據塊對齊單位(每個採樣需要的字節數 = 通道數 × 每次採樣得到的樣本數據位數 / 8 )
WavFileHeader.nBytesPerSample = 2;
// 波形數據傳輸速率
// (每秒平均字節數 = 採樣頻率 × 通道數 × 每次採樣得到的樣本數據位數 / 8 = 採樣頻率 × 每個採樣需要的字節數 )
WavFileHeader.nBytesPerSecond = 32000;
// 每次採樣得到的樣本數據位數;
WavFileHeader.nBitsPerSample = 16;
QFile cacheFile(PcmFileName);
QFile wavFile(wavFileName);
if (!cacheFile.open(QIODevice::ReadWrite))
{
return -1;
}
if (!wavFile.open(QIODevice::WriteOnly))
{
return -2;
}
int nSize = sizeof(WavFileHeader);
qint64 nFileLen = cacheFile.bytesAvailable();
WavFileHeader.nRiffLength = static_cast<unsigned long>(nFileLen - 8 + nSize);
//static_cast<類型>(變量); //新式的強制轉換
WavFileHeader.nDataLength = static_cast<unsigned long>(nFileLen);
// 先將wav文件頭信息寫入,再將音頻數據寫入;
wavFile.write((const char *)&WavFileHeader,nSize);
wavFile.write(cacheFile.readAll());
cacheFile.close();
wavFile.close();
return nFileLen;
}
ui界面: