Skip to content Skip to footer

QT音频开发:使用QAudioInput+QAudioOutput实现录音机功能,支持选择指定声卡录音,指定扬声器放音

一、环境介绍

操作系统: windows10

QT版本: 5.12

二、录音机效果

三、核心代码

mainwindow.h代码:

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include

#include

#include //这五个是QT处理音频的库

#include

#include

#include

#include

#include

#include

#include

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 input_list;

QList 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

{

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

{

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(nFileLen - 8 + nSize);

//static_cast<类型>(变量); //新式的强制转换

WavFileHeader.nDataLength = static_cast(nFileLen);

// 先将wav文件头信息写入,再将音频数据写入;

wavFile.write((const char *)&WavFileHeader,nSize);

wavFile.write(cacheFile.readAll());

cacheFile.close();

wavFile.close();

return nFileLen;

}

ui界面:

Copyright © 2088 2006年德国世界杯_世界杯歌曲凯歌 - lt795.com All Rights Reserved.
友情链接