一、操作系统介绍
Linux系统: ubuntu18.04 64位
Android系统: Android 8.1/9.0
windows系统: win10
QT版本: 5.12
声卡: win10 电脑自带声卡、罗技USB摄像头声卡、Android手机自带声卡都可以获取声音数据
二、需求介绍
使用QT本身代码在linux平台、Android平台、windows平台实时获取声卡的音频数据(PCM)。
后面可以配合ffmpeg进行编码压缩处理,完成远程语言对讲功能、视频监控的语音功能。
三、实现方式
QT里处理音频数据需要用到以下几个头文件:
#include <QAudio>
#include <QAudioFormat>
#include <QAudioInput>
#include <QAudioOutput>
#include <QIODevice>
在pro工程文件里需要加上:
QT += multimedia
四、实例代码
下面代码功能:采集声卡的音频数据存放到文件,再播放出来。
代码在win10、ubuntu18.04、Android8.1、android9.0都测试通过。
mainwin.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.raw"
#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");
}
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(16000); //设置采样率以对赫兹采样。
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::UnSignedInt); //样本是无符号整数
//选择默认设备作为输入源
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
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(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());
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(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()
{
foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
Log_Display(tr("Device name:%1\n").arg(deviceInfo.deviceName()));
}
mainwin.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);
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
ui界面:
注意:声音录制是否正常,需要看下面这些参数是否正确。
//设置录音的格式
auido_input_format.setSampleRate(16000); //设置采样率以对赫兹采样。
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); //在ubuntu下测试,这里设置为有符号整数录制的声音才正常
下面公众号里有QT、C++、C的基础全套学习教程: