Qt - 音频采集程序
Qt 麦克风音频数据采集(编码数据)
我们将使用Qt的多媒体模块来捕获音频,并编码保存到文件。同时,我们创建一个简单的界面,包含开始和停止按钮,以及状态显示。
步骤:
-
包含必要的头文件。
-
创建一个Qt应用程序,并设置界面。
-
使用QAudioRecorder来录制音频,并设置编码参数。
-
将录制的音频保存到文件,并在界面上显示状态。
注意:由于Qt6中一些多媒体类已被更改,我们这里使用Qt5的QAudioRecorder(因为Qt6中移除了QAudioRecorder,但Qt5中还有)。如果使用Qt6,可能需要使用QMediaRecorder和QAudioInput的方式。但根据问题要求,我们使用QAudioEncoderSettings,所以这里假设使用Qt5。
由于问题没有指定Qt版本,我们按照Qt5来实现。
我们将创建一个简单的窗口,包含两个按钮(开始和停止)和一个标签显示状态。
代码结构:
-
包含头文件
-
定义MainWindow类,私有成员:QAudioRecorder指针,按钮和标签。
-
实现开始和停止槽函数。
注意:在.pro文件中需要添加 multimedia 模块。
下面开始编写代码:
我来为您实现一个使用Qt和C++的音频采集应用程序,可以编码并保存麦克风音频数据。
首先,在 .pro 文件中添加必要的模块:
QT += core gui multimedia multimediawidgets
CONFIG += c++17
SOURCES = \
main.cpp \
mainwindow.cpp
HEADERS = \
mainwindow.h
FORMS = \
mainwindow.ui
2. mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QAudioRecorder>
#include <QAudioEncoderSettings>
#include <QAudioDeviceInfo>
#include <QMessageBox>
#include <QDateTime>
#include <QFileDialog>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_startButton_clicked();
void on_stopButton_clicked();
void updateProgress(qint64 duration);
void displayError();
void updateStatus(QMediaRecorder::Status status);
void updateAudioDevices();
private:
Ui::MainWindow *ui;
QAudioRecorder *audioRecorder;
QAudioEncoderSettings audioSettings;
QString outputFile;
void setupAudioSettings();
void setupUI();
QString generateOutputFilename();
};
#endif // MAINWINDOW_H
3. mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, audioRecorder(new QAudioRecorder(this))
{
ui->setupUi(this);
setupUI();
setupAudioSettings();
updateAudioDevices();
// 连接信号和槽
connect(audioRecorder, &QAudioRecorder::durationChanged, this, &MainWindow::updateProgress);
connect(audioRecorder, QOverload<QMediaRecorder::Error>::of(&QAudioRecorder::error), this, &MainWindow::displayError);
connect(audioRecorder, &QAudioRecorder::statusChanged, this, &MainWindow::updateStatus);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setupUI()
{
// 设置按钮状态
ui->stopButton->setEnabled(false);
// 设置状态标签
ui->statusLabel->setText("准备就绪");
ui->progressLabel->setText("时长: 00:00");
// 设置窗口标题和大小
setWindowTitle("音频采集器");
setMinimumSize(400, 300);
}
void MainWindow::setupAudioSettings()
{
// 配置音频编码设置
audioSettings.setCodec("audio/aac");
audioSettings.setQuality(QMultimedia::HighQuality);
audioSettings.setChannelCount(2);
audioSettings.setSampleRate(44100);
audioSettings.setBitRate(128000);
audioSettings.setEncodingMode(QMultimedia::ConstantQualityEncoding);
// 应用设置到录音器
audioRecorder->setEncodingSettings(audioSettings);
// 设置音频输入为默认麦克风
QStringList audioInputs = audioRecorder->audioInputs();
if (!audioInputs.isEmpty()) {
audioRecorder->setAudioInput(audioInputs.first());
}
// 在UI上显示编码信息
ui->codecLabel->setText(QString("编码器: %1").arg(audioSettings.codec()));
ui->sampleRateLabel->setText(QString("采样率: %1 Hz").arg(audioSettings.sampleRate()));
ui->bitrateLabel->setText(QString("比特率: %1 kbps").arg(audioSettings.bitRate() / 1000));
ui->channelsLabel->setText(QString("声道: %1").arg(audioSettings.channelCount()));
}
void MainWindow::updateAudioDevices()
{
ui->audioDeviceComboBox->clear();
// 获取所有可用的音频输入设备
QStringList audioInputs = audioRecorder->audioInputs();
QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
for (const QAudioDeviceInfo &device : devices) {
ui->audioDeviceComboBox->addItem(device.deviceName());
}
if (audioInputs.isEmpty()) {
ui->statusLabel->setText("错误: 未找到音频输入设备");
ui->startButton->setEnabled(false);
}
}
QString MainWindow::generateOutputFilename()
{
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
return QString("audio_record_%1.m4a").arg(timestamp);
}
void MainWindow::on_startButton_clicked()
{
// 选择保存文件的位置
QString defaultFileName = generateOutputFilename();
outputFile = QFileDialog::getSaveFileName(this,
"保存音频文件",
QDir::homePath() + "/" + defaultFileName,
"音频文件 (*.m4a *.aac *.mp3)");
if (outputFile.isEmpty()) {
return; // 用户取消了保存
}
// 设置输出文件
audioRecorder->setOutputLocation(QUrl::fromLocalFile(outputFile));
// 开始录音
audioRecorder->record();
// 更新UI状态
ui->startButton->setEnabled(false);
ui->stopButton->setEnabled(true);
ui->statusLabel->setText("录音中...");
ui->filenameLabel->setText(QString("文件: %1").arg(QFileInfo(outputFile).fileName()));
}
void MainWindow::on_stopButton_clicked()
{
// 停止录音
audioRecorder->stop();
// 更新UI状态
ui->startButton->setEnabled(true);
ui->stopButton->setEnabled(false);
ui->statusLabel->setText("录音已停止");
QMessageBox::information(this, "完成",
QString("音频已保存到:\n%1").arg(outputFile));
}
void MainWindow::updateProgress(qint64 duration)
{
// 更新录音时长显示
qint64 seconds = duration / 1000;
qint64 minutes = seconds / 60;
seconds = seconds % 60;
ui->progressLabel->setText(QString("时长: %1:%2")
.arg(minutes, 2, 10, QLatin1Char('0'))
.arg(seconds, 2, 10, QLatin1Char('0')));
}
void MainWindow::displayError()
{
QMessageBox::warning(this, "错误",
QString("录音错误: %1").arg(audioRecorder->errorString()));
ui->statusLabel->setText("错误发生");
ui->startButton->setEnabled(true);
ui->stopButton->setEnabled(false);
}
void MainWindow::updateStatus(QMediaRecorder::Status status)
{
QString statusText;
switch (status) {
case QMediaRecorder::RecordingStatus:
statusText = "录音中";
break;
case QMediaRecorder::PausedStatus:
statusText = "已暂停";
break;
case QMediaRecorder::StoppedStatus:
statusText = "已停止";
break;
default:
statusText = "未知状态";
break;
}
ui->statusLabel->setText(statusText);
}
4. mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>音频采集器</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>音频设备</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QComboBox" name="audioDeviceComboBox"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>编码信息</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="codecLabel">
<property name="text">
<string>编码器: </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="sampleRateLabel">
<property name="text">
<string>采样率: </string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="bitrateLabel">
<property name="text">
<string>比特率: </string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="channelsLabel">
<property name="text">
<string>声道: </string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>控制</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="startButton">
<property name="text">
<string>开始录音</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopButton">
<property name="text">
<string>停止录音</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>状态信息</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>准备就绪</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="progressLabel">
<property name="text">
<string>时长: 00:00</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filenameLabel">
<property name="text">
<string>文件: 未选择</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置应用程序信息
QApplication::setApplicationName("音频采集器");
QApplication::setApplicationVersion("1.0");
QApplication::setOrganizationName("MyCompany");
MainWindow w;
w.show();
return a.exec();
}
功能特点
这个音频采集应用程序具有以下特点:
-
音频编码设置:使用
QAudioEncoderSettings配置音频参数-
编码器:AAC
-
采样率:44.1kHz
-
比特率:128kbps
-
声道:立体声
-
-
用户界面:
-
开始/停止控制按钮
-
音频设备选择
-
实时状态显示
-
录音时长显示
-
编码信息显示
-
-
文件保存:
-
自动生成带时间戳的文件名
-
支持选择保存位置
-
保存为常见的音频格式
-
-
错误处理:
-
设备检测
-
录音错误处理
-
状态监控
-
编译和运行

Qt 麦克风音频数据采集(原始数据)
我们将创建一个简单的Qt应用程序,用于采集麦克风的音频数据。我们将使用QAudioInput来捕获音频,并将捕获的原始PCM数据保存到文件中。同时,我们也会在界面上显示一些基本的控制按钮(开始、停止)和状态信息。
步骤:
-
创建Qt项目(例如使用Qt Widgets Application)。
-
在.pro文件中添加multimedia模块:QT += multimedia
-
设计一个简单的界面,包含两个按钮(开始和停止)和一个标签显示状态。
-
在后台代码中,设置音频输入设备,并当点击开始按钮时,开始捕获音频数据;点击停止按钮时,停止捕获。
注意:我们这里将捕获的原始PCM数据保存为文件,但请注意原始PCM数据没有文件头(如WAV头),所以不能直接播放。如果需要可播放的文件,可以添加WAV头或者使用其他格式。
但是,为了简单起见,我们只保存原始PCM数据。如果你需要播放,可以使用Audacity等工具导入原始数据(设置正确的采样率、样本大小和通道数)。
我们将使用以下音频格式:
-
采样率:44100 Hz
-
样本大小:16位
-
通道数:2(立体声)
如果默认设备不支持这些参数,我们将使用最接近的设置。
代码结构:
-
使用QAudioInput进行音频输入。
-
使用QFile来存储捕获的音频数据。
界面设计:
-
一个QPushButton用于开始捕获。
-
一个QPushButton用于停止捕获(初始不可用)。
-
一个QLabel用于显示状态。
逻辑:
-
点击开始按钮后,初始化音频输入设备并开始捕获,同时将开始按钮禁用,停止按钮启用。
-
点击停止按钮后,停止捕获,关闭文件,并将按钮状态重置。
我们将创建一个类,比如叫AudioCapture,来管理音频捕获。
由于Qt6中对多媒体模块有一些变化,请注意:如果你使用的是Qt6,需要确保包括正确的模块。在Qt6中,QAudioInput位于QtMultimedia模块中。
下面是一个完整的 Qt 应用程序,用于采集麦克风音频数据并实时显示波形。
1. AudioCaptureApp.pro
QT += core gui multimedia widgets
CONFIG += c++17
TARGET = AudioCaptureApp
TEMPLATE = app
SOURCES += \
main.cpp \
mainwindow.cpp \
audiocapture.cpp
HEADERS += \
mainwindow.h \
audiocapture.h
FORMS += \
widget.ui
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 设置应用程序信息
app.setApplicationName("音频采集应用");
app.setApplicationVersion("1.0");
app.setOrganizationName("MyCompany");
MainWindow window;
window.show();
return app.exec();
}
2. audiocapture.h
#ifndef AUDIOCAPTURE_H
#define AUDIOCAPTURE_H
#include <QObject>
#include <QAudioInput>
#include <QAudioDeviceInfo>
#include <QByteArray>
#include <QTimer>
#include <QDebug>
class AudioCapture : public QObject
{
Q_OBJECT
public:
explicit AudioCapture(QObject *parent = nullptr);
~AudioCapture();
// 音频设备操作
bool initializeAudio();
void startCapture();
void stopCapture();
void pauseCapture();
void resumeCapture();
// 设备信息
QList<QAudioDeviceInfo> getAvailableDevices() const;
QString getCurrentDeviceName() const;
// 音频参数设置
void setSampleRate(int rate);
void setChannelCount(int channels);
void setSampleSize(int size);
// 状态查询
bool isCapturing() const { return m_isCapturing; }
qint64 getBytesCaptured() const { return m_bytesCaptured; }
signals:
// 音频数据可用信号
void audioDataAvailable(const QByteArray &data);
// 音频电平信号(用于显示波形)
void audioLevelChanged(qreal level);
// 状态变化信号
void captureStateChanged(bool capturing);
// 错误信号
void errorOccurred(const QString &errorMessage);
public slots:
void setAudioDevice(const QAudioDeviceInfo &device);
private slots:
void handleAudioDataReady();
void updateAudioLevel();
private:
void setupAudioFormat();
void calculateAudioLevel(const QByteArray &data);
void calculateMaxAmplitude();
private:
QAudioInput *m_audioInput;
QIODevice *m_audioDevice;
QAudioFormat m_audioFormat;
QAudioDeviceInfo m_currentDevice;
QTimer *m_levelTimer;
bool m_isCapturing;
bool m_isPaused;
qint64 m_bytesCaptured;
qreal m_currentLevel;
quint32 m_maxAmplitude;
qreal m_smoothedLevel;
const qreal SMOOTHING_FACTOR = 0.2;
};
#endif // AUDIOCAPTURE_H
3. audiocapture.cpp
#include "audiocapture.h"
#include <QtEndian>
AudioCapture::AudioCapture(QObject *parent)
: QObject(parent)
, m_audioInput(nullptr)
, m_audioDevice(nullptr)
, m_isCapturing(false)
, m_isPaused(false)
, m_bytesCaptured(0)
, m_currentLevel(0.0)
, m_maxAmplitude(0)
, m_smoothedLevel(0.0)
{
m_levelTimer = new QTimer(this);
connect(m_levelTimer, &QTimer::timeout, this, &AudioCapture::updateAudioLevel);
}
AudioCapture::~AudioCapture()
{
stopCapture();
}
bool AudioCapture::initializeAudio()
{
// 获取默认输入设备
m_currentDevice = QAudioDeviceInfo::defaultInputDevice();
if (m_currentDevice.isNull()) {
emit errorOccurred("未找到可用的音频输入设备");
return false;
}
// 设置音频格式
setupAudioFormat();
// 检查格式支持
if (!m_currentDevice.isFormatSupported(m_audioFormat)) {
m_audioFormat = m_currentDevice.nearestFormat(m_audioFormat);
qDebug() << "使用最接近的音频格式:" << m_audioFormat.sampleRate()
<< "Hz," << m_audioFormat.channelCount() << "channels,"
<< m_audioFormat.sampleSize() << "bits";
}
// 计算最大振幅
calculateMaxAmplitude();
// 创建音频输入对象
m_audioInput = new QAudioInput(m_currentDevice, m_audioFormat, this);
// 设置缓冲区大小
m_audioInput->setBufferSize(4096);
return true;
}
void AudioCapture::setupAudioFormat()
{
m_audioFormat.setSampleRate(44100); // 44.1 kHz
m_audioFormat.setChannelCount(1); // 单声道
m_audioFormat.setSampleSize(16); // 16位
m_audioFormat.setCodec("audio/pcm"); // PCM编码
m_audioFormat.setByteOrder(QAudioFormat::LittleEndian);
m_audioFormat.setSampleType(QAudioFormat::SignedInt);
}
void AudioCapture::calculateMaxAmplitude()
{
switch (m_audioFormat.sampleSize()) {
case 8:
switch (m_audioFormat.sampleType()) {
case QAudioFormat::UnSignedInt:
m_maxAmplitude = 255;
break;
case QAudioFormat::SignedInt:
m_maxAmplitude = 127;
break;
default:
m_maxAmplitude = 255;
break;
}
break;
case 16:
switch (m_audioFormat.sampleType()) {
case QAudioFormat::UnSignedInt:
m_maxAmplitude = 65535;
break;
case QAudioFormat::SignedInt:
m_maxAmplitude = 32767;
break;
default:
m_maxAmplitude = 32767;
break;
}
break;
case 32:
switch (m_audioFormat.sampleType()) {
case QAudioFormat::UnSignedInt:
m_maxAmplitude = 0xffffffff;
break;
case QAudioFormat::SignedInt:
m_maxAmplitude = 0x7fffffff;
break;
case QAudioFormat::Float:
m_maxAmplitude = 0x7fffffff; // Kind of
default:
m_maxAmplitude = 0x7fffffff;
break;
}
break;
default:
m_maxAmplitude = 32767;
break;
}
qDebug() << "Max amplitude:" << m_maxAmplitude
<< "Sample size:" << m_audioFormat.sampleSize()
<< "Sample type:" << m_audioFormat.sampleType();
}
void AudioCapture::calculateAudioLevel(const QByteArray &data)
{
if (m_maxAmplitude) {
const int channelBytes = m_audioFormat.sampleSize() / 8;
const int sampleBytes = m_audioFormat.channelCount() * channelBytes;
// 使用 data.size() 而不是 len
if (data.size() % sampleBytes != 0) {
qDebug() << "Audio data length is not multiple of sample bytes";
return;
}
const int numSamples = data.size() / sampleBytes;
quint32 maxValue = 0;
const unsigned char* ptr = reinterpret_cast<const unsigned char*>(data.constData());
for (int i = 0; i < numSamples; ++i) {
for (int j = 0; j < m_audioFormat.channelCount(); ++j) {
quint32 value = 0;
if (m_audioFormat.sampleSize() == 8 &&
m_audioFormat.sampleType() == QAudioFormat::UnSignedInt) {
value = *reinterpret_cast<const quint8*>(ptr);
}
else if (m_audioFormat.sampleSize() == 8 &&
m_audioFormat.sampleType() == QAudioFormat::SignedInt) {
value = qAbs(*reinterpret_cast<const qint8*>(ptr));
}
else if (m_audioFormat.sampleSize() == 16 &&
m_audioFormat.sampleType() == QAudioFormat::UnSignedInt) {
if (m_audioFormat.byteOrder() == QAudioFormat::LittleEndian)
value = qFromLittleEndian<quint16>(ptr);
else
value = qFromBigEndian<quint16>(ptr);
}
else if (m_audioFormat.sampleSize() == 16 &&
m_audioFormat.sampleType() == QAudioFormat::SignedInt) {
if (m_audioFormat.byteOrder() == QAudioFormat::LittleEndian)
value = qAbs(qFromLittleEndian<qint16>(ptr));
else
value = qAbs(qFromBigEndian<qint16>(ptr));
}
else if (m_audioFormat.sampleSize() == 32 &&
m_audioFormat.sampleType() == QAudioFormat::UnSignedInt) {
if (m_audioFormat.byteOrder() == QAudioFormat::LittleEndian)
value = qFromLittleEndian<quint32>(ptr);
else
value = qFromBigEndian<quint32>(ptr);
}
else if (m_audioFormat.sampleSize() == 32 &&
m_audioFormat.sampleType() == QAudioFormat::SignedInt) {
if (m_audioFormat.byteOrder() == QAudioFormat::LittleEndian)
value = qAbs(qFromLittleEndian<qint32>(ptr));
else
value = qAbs(qFromBigEndian<qint32>(ptr));
}
else if (m_audioFormat.sampleSize() == 32 &&
m_audioFormat.sampleType() == QAudioFormat::Float) {
value = qAbs(*reinterpret_cast<const float*>(ptr) * 0x7fffffff);
}
maxValue = qMax(value, maxValue);
ptr += channelBytes;
}
}
maxValue = qMin(maxValue, m_maxAmplitude);
qreal rawLevel = qreal(maxValue) / m_maxAmplitude;
// 添加轻微平滑,避免过度跳动
m_smoothedLevel = SMOOTHING_FACTOR * rawLevel + (1 - SMOOTHING_FACTOR) * m_smoothedLevel;
m_currentLevel = m_smoothedLevel;
}
}
void AudioCapture::startCapture()
{
if (!m_audioInput) {
if (!initializeAudio()) {
return;
}
}
if (m_isCapturing && !m_isPaused) {
return;
}
if (m_isPaused) {
resumeCapture();
return;
}
// 开始音频采集
m_audioDevice = m_audioInput->start();
if (!m_audioDevice) {
emit errorOccurred("无法启动音频设备");
return;
}
// 连接数据可用信号
connect(m_audioDevice, &QIODevice::readyRead,
this, &AudioCapture::handleAudioDataReady);
m_isCapturing = true;
m_isPaused = false;
m_bytesCaptured = 0;
m_smoothedLevel = 0.0; // 重置平滑电平
// 启动电平计时器
m_levelTimer->start(50);
emit captureStateChanged(true);
qDebug() << "开始音频采集,设备:" << m_currentDevice.deviceName();
}
void AudioCapture::stopCapture()
{
if (m_audioInput) {
m_audioInput->stop();
if (m_audioDevice) {
m_audioDevice->disconnect();
m_audioDevice = nullptr;
}
}
m_levelTimer->stop();
m_isCapturing = false;
m_isPaused = false;
m_currentLevel = 0.0;
m_smoothedLevel = 0.0;
emit captureStateChanged(false);
emit audioLevelChanged(0.0);
qDebug() << "停止音频采集,总采集数据:" << m_bytesCaptured << "字节";
}
void AudioCapture::pauseCapture()
{
if (m_isCapturing && !m_isPaused) {
m_audioInput->suspend();
m_isPaused = true;
m_levelTimer->stop();
emit audioLevelChanged(0.0);
}
}
void AudioCapture::resumeCapture()
{
if (m_isCapturing && m_isPaused) {
m_audioInput->resume();
m_isPaused = false;
m_levelTimer->start(50);
}
}
void AudioCapture::handleAudioDataReady()
{
if (!m_audioDevice) return;
// 读取音频数据
QByteArray audioData = m_audioDevice->readAll();
if (audioData.isEmpty()) return;
m_bytesCaptured += audioData.size();
// 计算音频电平
calculateAudioLevel(audioData);
// 发送数据可用信号
emit audioDataAvailable(audioData);
}
void AudioCapture::updateAudioLevel()
{
emit audioLevelChanged(m_currentLevel);
}
QList<QAudioDeviceInfo> AudioCapture::getAvailableDevices() const
{
return QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
}
QString AudioCapture::getCurrentDeviceName() const
{
return m_currentDevice.deviceName();
}
void AudioCapture::setAudioDevice(const QAudioDeviceInfo &device)
{
if (m_isCapturing) {
stopCapture();
}
m_currentDevice = device;
// 重新初始化音频输入
if (m_audioInput) {
delete m_audioInput;
m_audioInput = nullptr;
}
initializeAudio();
}
void AudioCapture::setSampleRate(int rate)
{
m_audioFormat.setSampleRate(rate);
if (m_isCapturing) {
// 需要重新启动采集
stopCapture();
startCapture();
}
}
void AudioCapture::setChannelCount(int channels)
{
m_audioFormat.setChannelCount(channels);
if (m_isCapturing) {
stopCapture();
startCapture();
}
}
void AudioCapture::setSampleSize(int size)
{
m_audioFormat.setSampleSize(size);
if (m_isCapturing) {
stopCapture();
startCapture();
}
}
4. mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QLabel>
#include <QComboBox>
#include <QProgressBar>
#include <QCheckBox>
#include <QSpinBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGroupBox>
#include <QFileDialog>
#include <QMessageBox>
#include "audiocapture.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onStartStopClicked();
void onPauseResumeClicked();
void onSaveDataClicked();
void onDeviceChanged(int index);
void handleAudioDataAvailable(const QByteArray &data);
void handleAudioLevelChanged(qreal level);
void handleCaptureStateChanged(bool capturing);
void handleErrorOccurred(const QString &errorMessage);
private:
void setupUI();
void setupConnections();
void updateDeviceList();
void updateUIState();
private:
// UI 组件
QWidget *m_centralWidget;
QVBoxLayout *m_mainLayout;
QGroupBox *m_deviceGroup;
QComboBox *m_deviceCombo;
QLabel *m_statusLabel;
QProgressBar *m_levelBar;
QGroupBox *m_controlGroup;
QPushButton *m_startStopButton;
QPushButton *m_pauseResumeButton;
QPushButton *m_saveButton;
QGroupBox *m_settingsGroup;
QCheckBox *m_autoSaveCheck;
QSpinBox *m_sampleRateSpin;
QSpinBox *m_channelsSpin;
QLabel *m_statsLabel;
// 音频采集
AudioCapture *m_audioCapture;
// 数据存储
QByteArray m_capturedData;
bool m_isCapturing;
bool m_isPaused;
};
#endif // MAINWINDOW_H
5. mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, m_audioCapture(new AudioCapture(this))
, m_isCapturing(false)
, m_isPaused(false)
{
setWindowTitle("Qt 音频采集应用");
setMinimumSize(600, 400);
setupUI();
setupConnections();
updateDeviceList();
updateUIState();
}
MainWindow::~MainWindow()
{
if (m_isCapturing) {
m_audioCapture->stopCapture();
}
}
void MainWindow::setupUI()
{
m_centralWidget = new QWidget(this);
setCentralWidget(m_centralWidget);
m_mainLayout = new QVBoxLayout(m_centralWidget);
// 设备选择组
m_deviceGroup = new QGroupBox("音频设备", this);
QVBoxLayout *deviceLayout = new QVBoxLayout(m_deviceGroup);
m_deviceCombo = new QComboBox(this);
deviceLayout->addWidget(m_deviceCombo);
m_statusLabel = new QLabel("状态: 未启动", this);
deviceLayout->addWidget(m_statusLabel);
m_levelBar = new QProgressBar(this);
m_levelBar->setRange(0, 100);
m_levelBar->setValue(0);
m_levelBar->setFormat("音频电平: %p%");
deviceLayout->addWidget(m_levelBar);
m_mainLayout->addWidget(m_deviceGroup);
// 控制按钮组
m_controlGroup = new QGroupBox("控制", this);
QHBoxLayout *controlLayout = new QHBoxLayout(m_controlGroup);
m_startStopButton = new QPushButton("开始采集", this);
m_pauseResumeButton = new QPushButton("暂停", this);
m_saveButton = new QPushButton("保存数据", this);
m_pauseResumeButton->setEnabled(false);
m_saveButton->setEnabled(false);
controlLayout->addWidget(m_startStopButton);
controlLayout->addWidget(m_pauseResumeButton);
controlLayout->addWidget(m_saveButton);
m_mainLayout->addWidget(m_controlGroup);
// 设置组
m_settingsGroup = new QGroupBox("设置", this);
QHBoxLayout *settingsLayout = new QHBoxLayout(m_settingsGroup);
m_autoSaveCheck = new QCheckBox("自动保存", this);
m_sampleRateSpin = new QSpinBox(this);
m_sampleRateSpin->setRange(8000, 192000);
m_sampleRateSpin->setValue(44100);
m_sampleRateSpin->setSuffix(" Hz");
m_channelsSpin = new QSpinBox(this);
m_channelsSpin->setRange(1, 2);
m_channelsSpin->setValue(1);
m_channelsSpin->setSuffix(" 声道");
settingsLayout->addWidget(new QLabel("采样率:", this));
settingsLayout->addWidget(m_sampleRateSpin);
settingsLayout->addWidget(new QLabel("声道数:", this));
settingsLayout->addWidget(m_channelsSpin);
settingsLayout->addWidget(m_autoSaveCheck);
settingsLayout->addStretch();
m_mainLayout->addWidget(m_settingsGroup);
// 统计信息
m_statsLabel = new QLabel("采集数据: 0 字节", this);
m_mainLayout->addWidget(m_statsLabel);
m_mainLayout->addStretch();
}
void MainWindow::setupConnections()
{
connect(m_startStopButton, &QPushButton::clicked,
this, &MainWindow::onStartStopClicked);
connect(m_pauseResumeButton, &QPushButton::clicked,
this, &MainWindow::onPauseResumeClicked);
connect(m_saveButton, &QPushButton::clicked,
this, &MainWindow::onSaveDataClicked);
connect(m_deviceCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::onDeviceChanged);
connect(m_audioCapture, &AudioCapture::audioDataAvailable,
this, &MainWindow::handleAudioDataAvailable);
connect(m_audioCapture, &AudioCapture::audioLevelChanged,
this, &MainWindow::handleAudioLevelChanged);
connect(m_audioCapture, &AudioCapture::captureStateChanged,
this, &MainWindow::handleCaptureStateChanged);
connect(m_audioCapture, &AudioCapture::errorOccurred,
this, &MainWindow::handleErrorOccurred);
}
void MainWindow::updateDeviceList()
{
m_deviceCombo->clear();
QList<QAudioDeviceInfo> devices = m_audioCapture->getAvailableDevices();
for (const QAudioDeviceInfo &device : devices) {
m_deviceCombo->addItem(device.deviceName(), QVariant::fromValue(device));
}
if (devices.isEmpty()) {
m_deviceCombo->addItem("未找到音频设备");
m_startStopButton->setEnabled(false);
}
}
void MainWindow::updateUIState()
{
if (m_isCapturing) {
m_startStopButton->setText("停止采集");
m_pauseResumeButton->setEnabled(true);
m_saveButton->setEnabled(false); // 采集时不能保存
m_deviceCombo->setEnabled(false);
m_sampleRateSpin->setEnabled(false);
m_channelsSpin->setEnabled(false);
if (m_isPaused) {
m_pauseResumeButton->setText("继续");
m_statusLabel->setText("状态: 已暂停");
} else {
m_pauseResumeButton->setText("暂停");
m_statusLabel->setText("状态: 采集中...");
}
} else {
m_startStopButton->setText("开始采集");
m_pauseResumeButton->setEnabled(false);
m_pauseResumeButton->setText("暂停");
m_saveButton->setEnabled(!m_capturedData.isEmpty());
m_deviceCombo->setEnabled(true);
m_sampleRateSpin->setEnabled(true);
m_channelsSpin->setEnabled(true);
m_statusLabel->setText("状态: 未启动");
}
m_statsLabel->setText(QString("采集数据: %1 字节").arg(m_capturedData.size()));
}
void MainWindow::onStartStopClicked()
{
if (m_isCapturing) {
m_audioCapture->stopCapture();
} else {
// 应用设置
m_audioCapture->setSampleRate(m_sampleRateSpin->value());
m_audioCapture->setChannelCount(m_channelsSpin->value());
// 开始采集
m_audioCapture->startCapture();
m_capturedData.clear(); // 清空之前的数据
}
}
void MainWindow::onPauseResumeClicked()
{
if (m_isPaused) {
m_audioCapture->resumeCapture();
m_isPaused = false;
} else {
m_audioCapture->pauseCapture();
m_isPaused = true;
}
updateUIState();
}
void MainWindow::onSaveDataClicked()
{
if (m_capturedData.isEmpty()) {
QMessageBox::information(this, "提示", "没有可保存的音频数据");
return;
}
QString fileName = QFileDialog::getSaveFileName(
this, "保存音频数据", "", "原始音频数据 (*.raw);;所有文件 (*)");
if (!fileName.isEmpty()) {
QFile file(fileName);
if (file.open(QIODevice::WriteOnly)) {
file.write(m_capturedData);
file.close();
QMessageBox::information(this, "成功", "音频数据已保存");
} else {
QMessageBox::warning(this, "错误", "无法保存文件");
}
}
}
void MainWindow::onDeviceChanged(int index)
{
if (index >= 0) {
QAudioDeviceInfo device = m_deviceCombo->itemData(index).value<QAudioDeviceInfo>();
m_audioCapture->setAudioDevice(device);
}
}
void MainWindow::handleAudioDataAvailable(const QByteArray &data)
{
m_capturedData.append(data);
updateUIState();
// 如果启用了自动保存,可以在这里实现自动保存逻辑
if (m_autoSaveCheck->isChecked()) {
// 自动保存实现(可选)
}
}
void MainWindow::handleAudioLevelChanged(qreal level)
{
int levelPercent = qMin(100, static_cast<int>(level * 100));
m_levelBar->setValue(levelPercent);
}
void MainWindow::handleCaptureStateChanged(bool capturing)
{
m_isCapturing = capturing;
m_isPaused = false;
updateUIState();
}
void MainWindow::handleErrorOccurred(const QString &errorMessage)
{
QMessageBox::critical(this, "音频采集错误", errorMessage);
m_isCapturing = false;
m_isPaused = false;
updateUIState();
}
功能特点
-
设备选择:自动检测并列出所有可用的音频输入设备
-
实时监控:显示音频电平,可视化音频输入强度
-
灵活控制:开始/停止、暂停/继续采集
-
参数设置:可调节采样率、声道数等音频参数
-
数据保存:将采集的原始PCM数据保存为文件
-
状态显示:实时显示采集状态和统计数据
编译和运行
-
使用 Qt Creator 打开项目文件
AudioCaptureApp.pro -
配置正确的 Qt 版本(确保包含 multimedia 模块)
-
编译并运行项目
使用说明
-
启动应用程序后,选择要使用的音频输入设备
-
根据需要调整采样率和声道数设置
-
点击"开始采集"按钮开始录音
-
观察音频电平显示,确认设备正常工作
-
点击"暂停"可以临时停止采集
-
点击"停止采集"结束录音
-
点击"保存数据"将采集的音频保存为文件


浙公网安备 33010602011771号