当前位置: 首页 > news >正文

开源 C++ QT Widget 开发(十四)多媒体--录音机

   文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
 

 相关链接:

开源 C++ QT Widget 开发(一)工程文件结构-CSDN博客

开源 C++ QT Widget 开发(二)基本控件应用-CSDN博客

开源 C++ QT Widget 开发(三)图表--波形显示器-CSDN博客

开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-CSDN博客

 开源 C++ QT Widget 开发(五)通讯--串口调试-CSDN博客

开源 C++ QT Widget 开发(六)通讯--TCP调试-CSDN博客

开源 C++ QT Widget 开发(七)线程--多线程及通讯-CSDN博客

开源 C++ QT Widget 开发(八)网络--Http文件下载-CSDN博客

开源 C++ QT Widget 开发(九)图表--仪表盘-CSDN博客

开源 C++ QT Widget 开发(十)IPC进程间通信--共享内存-CSDN博客

开源 C++ QT Widget 开发(十一)进程间通信--Windows 窗口通信-CSDN博客

开源 C++ QT Widget 开发(十二)图表--环境监测表盘-CSDN博客

开源 C++ QT Widget 开发(十三)IPC通讯--本地套接字 (Local Socket)

开源 C++ QT Widget 开发(十四)多媒体--录音机

开源 C++ QT Widget 开发(十五)多媒体--音频播放



推荐链接:

开源 java android app 开发(一)开发环境的搭建-CSDN博客

开源 java android app 开发(二)工程文件结构-CSDN博客

开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客

开源 java android app 开发(四)GUI界面重要组件-CSDN博客

开源 java android app 开发(五)文件和数据库存储-CSDN博客

开源 java android app 开发(六)多媒体使用-CSDN博客

开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客

开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客

开源 java android app 开发(九)后台之线程和服务-CSDN博客

开源 java android app 开发(十)广播机制-CSDN博客

开源 java android app 开发(十一)调试、发布-CSDN博客

开源 java android app 开发(十二)封库.aar-CSDN博客

推荐链接:

开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

内容:Qt音频录音机应用程序。实现win10系统下,用笔记本进行录音的功能。

目录:

1.功能介绍

2.核心代码分析

3.所有源码

4.显示效果

一、功能介绍

1. 音频录制功能
使用QAudioInput进行音频输入捕获

设置16kHz采样率、16位深度、单声道PCM格式

自动处理不支持的格式,使用nearestFormat()适配

2. WAV文件处理
实时生成符合标准的WAV文件头

先预留头部空间,录音完成后填充实际数据大小

支持标准的RIFF WAVE格式

3. 用户界面交互
录音按钮切换开始/停止状态

实时显示录音时长和进度

状态标签和日志系统提供用户反馈

二、核心代码分析

1. 构造函数 MainWindow::MainWindow()
功能:初始化主窗口和所有成员变量

详细分析:


// 初始化列表:正确初始化所有成员变量
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)          // 创建UI对象, audioInput(nullptr)             // 音频输入设备初始为空, outputFile(nullptr)             // 输出文件初始为空, buffer(nullptr)                 // 缓冲区初始为空, timer(new QTimer(this))         // 创建计时器,设置父对象, isRecording(false)              // 录音状态初始为false, audioDuration(0)                // 录音时长初始为0
{ui->setupUi(this);                // 设置UI界面setWindowTitle("音频录音机");      // 设置窗口标题// 连接计时器信号到更新进度槽函数connect(timer, &QTimer::timeout, this, &MainWindow::updateProgress);// 初始化UI控件状态ui->recordButton->setText("开始录音");ui->statusLabel->setText("就绪");ui->progressBar->setValue(0);logMessage("程序启动完成");        // 记录启动日志
}


2. 析构函数 MainWindow::~MainWindow()
功能:清理资源和内存

详细分析:

MainWindow::~MainWindow()
{stopRecording();    // 确保停止正在进行的录音delete ui;          // 删除UI对象(自动删除所有子控件)
}



3. setupAudioInput() - 音频输入设置
功能:配置音频输入设备和格式参数

详细分析:


void MainWindow::setupAudioInput()
{// 设置PCM音频格式参数QAudioFormat format;format.setSampleRate(16000);     // 16kHz采样率(适合语音)format.setChannelCount(1);       // 单声道format.setSampleSize(16);        // 16位采样深度format.setCodec("audio/pcm");    // PCM编码format.setByteOrder(QAudioFormat::LittleEndian); // 小端字节序format.setSampleType(QAudioFormat::SignedInt);   // 有符号整数// 检查设备支持QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();if (!info.isFormatSupported(format)) {logMessage("警告:默认格式不支持,使用最近似格式");format = info.nearestFormat(format); // 自适应调整格式}// 记录实际使用的格式logMessage(QString("音频格式:%1Hz, %2位, %3声道").arg(format.sampleRate()).arg(format.sampleSize()).arg(format.channelCount()));// 创建音频输入对象audioInput = new QAudioInput(format, this);
}


4. on_recordButton_clicked() - 录音按钮点击处理
功能:处理开始/停止录音的逻辑切换

详细分析:

void MainWindow::on_recordButton_clicked()
{if (!isRecording) {// 开始录音分支logMessage("开始录音...");setupAudioInput(); // 设置音频输入// 创建输出文件QString filePath = QDir::currentPath() + "/input.wav";outputFile = new QFile(filePath, this);if (!outputFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {logMessage("错误:无法创建文件");delete outputFile;outputFile = nullptr;return; // 错误处理:文件创建失败时返回}setupWavHeader(*outputFile, 0); // 写入空的WAV文件头// 创建内存缓冲区buffer = new QBuffer(this);buffer->open(QIODevice::ReadWrite);audioInput->start(buffer); // 开始录音到缓冲区isRecording = true;timer->start(100); // 启动100ms间隔的计时器audioDuration = 0; // 重置录音时长// 更新UI状态ui->recordButton->setText("停止录音");ui->statusLabel->setText("正在录音...");logMessage("录音进行中");} else {// 停止录音分支stopRecording();}
}


5. stopRecording() - 停止录音
功能:停止录音并完成文件保存

详细分析:

void MainWindow::stopRecording()
{if (!isRecording) return; // 安全检查:如果不在录音状态则返回logMessage("停止录音...");audioInput->stop();     // 停止音频输入isRecording = false;    // 更新状态timer->stop();          // 停止计时器// 处理录音数据if (buffer && outputFile) {QByteArray audioData = buffer->data(); // 获取缓冲区数据outputFile->seek(0); // 回到文件开头setupWavHeader(*outputFile, audioData.size()); // 写入正确的文件头outputFile->write(audioData); // 写入音频数据outputFile->close(); // 关闭文件logMessage(QString("录音完成,文件大小:%1 字节").arg(audioData.size()));}// 清理资源if (buffer) {buffer->close();buffer = nullptr;}if (outputFile) {delete outputFile;outputFile = nullptr;}if (audioInput) {delete audioInput;audioInput = nullptr;}// 更新UI状态ui->recordButton->setText("开始录音");ui->statusLabel->setText("录音完成");ui->progressBar->setValue(100);
}



6. updateProgress() - 更新进度
功能:定时更新录音进度和显示

详细分析:

void MainWindow::updateProgress()
{audioDuration += 100; // 增加100ms(计时器间隔)int seconds = audioDuration / 1000; // 转换为秒// 格式化时间显示 (MM:SS)ui->timeLabel->setText(QString("%1:%2").arg(seconds / 60, 2, 10, QLatin1Char('0'))  // 分钟.arg(seconds % 60, 2, 10, QLatin1Char('0'))); // 秒// 更新进度条(最大100秒)ui->progressBar->setValue(qMin(100, seconds));
}


7. setupWavHeader() - 设置WAV文件头
功能:生成标准的WAV文件头结构

详细分析:

void MainWindow::setupWavHeader(QFile &file, quint32 dataSize)
{// WAV文件头结构定义struct WavHeader {char riff[4] = {'R','I','F','F'};      // RIFF标识quint32 chunkSize;                     // 文件总大小-8char wave[4] = {'W','A','V','E'};      // WAVE标识char fmt[4] = {'f','m','t',' '};       // fmt块标识quint32 fmtChunkSize = 16;             // fmt块大小quint16 audioFormat = 1;               // 格式代码(1=PCM)quint16 numChannels = 1;               // 声道数quint32 sampleRate = 16000;            // 采样率quint32 byteRate;                      // 字节率quint16 blockAlign;                    // 块对齐quint16 bitsPerSample = 16;            // 位深度char data[4] = {'d','a','t','a'};      // data块标识quint32 dataChunkSize;                 // 数据块大小};WavHeader header;header.chunkSize = 36 + dataSize;     // 文件总大小-8header.byteRate = 16000 * 2;          // 采样率×位深度÷8×声道数header.blockAlign = 2;                // 位深度÷8×声道数header.dataChunkSize = dataSize;      // 音频数据大小// 写入文件头file.write(reinterpret_cast<const char*>(&header), sizeof(WavHeader));
}


8. logMessage() - 日志记录
功能:统一的消息日志记录系统

详细分析:


void MainWindow::logMessage(const QString &message)
{QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");ui->logTextEdit->append(QString("[%1] %2").arg(timestamp).arg(message));qDebug() << message; // 同时输出到控制台
}

三、所有源码

1. pro文件

QT       += core gui
QT       += core gui multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \mainwindow.cppHEADERS += \mainwindow.hFORMS += \mainwindow.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

2.mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QAudioInput>
#include <QFile>
#include <QBuffer>
#include <QTimer>QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_recordButton_clicked();void updateProgress();private:Ui::MainWindow *ui;QAudioInput *audioInput;QFile *outputFile;QBuffer *buffer;QTimer *timer;bool isRecording;qint64 audioDuration;void setupAudioInput();void stopRecording();void setupWavHeader(QFile &file, quint32 dataSize);void logMessage(const QString &message);
};#endif // MAINWINDOW_H

3.mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QAudioDeviceInfo>
#include <QAudioFormat>
#include <QDir>
#include <QDateTime>
#include <QDebug>
#include <cmath>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), audioInput(nullptr), outputFile(nullptr), buffer(nullptr), timer(new QTimer(this)), isRecording(false), audioDuration(0)
{ui->setupUi(this);// 设置窗口标题setWindowTitle("音频录音机");// 连接计时器connect(timer, &QTimer::timeout, this, &MainWindow::updateProgress);// 初始化UI状态ui->recordButton->setText("开始录音");ui->statusLabel->setText("就绪");ui->progressBar->setValue(0);logMessage("程序启动完成");
}MainWindow::~MainWindow()
{stopRecording();delete ui;
}void MainWindow::setupAudioInput()
{// 设置音频格式:16kHz, 16位, 单声道QAudioFormat format;format.setSampleRate(16000);format.setChannelCount(1);format.setSampleSize(16);format.setCodec("audio/pcm");format.setByteOrder(QAudioFormat::LittleEndian);format.setSampleType(QAudioFormat::SignedInt);// 检查格式支持QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();if (!info.isFormatSupported(format)) {logMessage("警告:默认格式不支持,使用最近似格式");format = info.nearestFormat(format);}logMessage(QString("音频格式:%1Hz, %2位, %3声道").arg(format.sampleRate()).arg(format.sampleSize()).arg(format.channelCount()));audioInput = new QAudioInput(format, this);
}void MainWindow::on_recordButton_clicked()
{if (!isRecording) {// 开始录音logMessage("开始录音...");// 设置音频输入setupAudioInput();// 创建输出文件QString filePath = QDir::currentPath() + "/input.wav";outputFile = new QFile(filePath, this);if (!outputFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {logMessage("错误:无法创建文件");delete outputFile;outputFile = nullptr;return;}// 先写入空的WAV文件头setupWavHeader(*outputFile, 0);// 创建缓冲区buffer = new QBuffer(this);buffer->open(QIODevice::ReadWrite);// 开始录音audioInput->start(buffer);isRecording = true;// 启动计时器timer->start(100);audioDuration = 0;// 更新UI状态ui->recordButton->setText("停止录音");ui->statusLabel->setText("正在录音...");logMessage("录音进行中");} else {// 停止录音stopRecording();}
}void MainWindow::stopRecording()
{if (!isRecording) return;logMessage("停止录音...");// 停止录音audioInput->stop();isRecording = false;// 停止计时器timer->stop();// 写入音频数据到文件if (buffer && outputFile) {QByteArray audioData = buffer->data();outputFile->seek(0);setupWavHeader(*outputFile, audioData.size());outputFile->write(audioData);outputFile->close();logMessage(QString("录音完成,文件大小:%1 字节").arg(audioData.size()));}// 清理资源if (buffer) {buffer->close();buffer = nullptr;}if (outputFile) {delete outputFile;outputFile = nullptr;}if (audioInput) {delete audioInput;audioInput = nullptr;}// 更新UI状态ui->recordButton->setText("开始录音");ui->statusLabel->setText("录音完成");ui->progressBar->setValue(100);
}void MainWindow::updateProgress()
{audioDuration += 100;int seconds = audioDuration / 1000;ui->timeLabel->setText(QString("%1:%2").arg(seconds / 60, 2, 10, QLatin1Char('0')).arg(seconds % 60, 2, 10, QLatin1Char('0')));ui->progressBar->setValue(qMin(100, seconds));
}void MainWindow::setupWavHeader(QFile &file, quint32 dataSize)
{// WAV文件头结构struct WavHeader {char riff[4] = {'R','I','F','F'};quint32 chunkSize;char wave[4] = {'W','A','V','E'};char fmt[4] = {'f','m','t',' '};quint32 fmtChunkSize = 16;quint16 audioFormat = 1; // PCMquint16 numChannels = 1;quint32 sampleRate = 16000;quint32 byteRate;quint16 blockAlign;quint16 bitsPerSample = 16;char data[4] = {'d','a','t','a'};quint32 dataChunkSize;};WavHeader header;header.chunkSize = 36 + dataSize;header.byteRate = 16000 * 2;header.blockAlign = 2;header.dataChunkSize = dataSize;file.write(reinterpret_cast<const char*>(&header), sizeof(WavHeader));
}void MainWindow::logMessage(const QString &message)
{QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");ui->logTextEdit->append(QString("[%1] %2").arg(timestamp).arg(message));qDebug() << message;
}

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>500</height></rect></property><property name="windowTitle"><string>音频录音机</string></property><widget class="QWidget" name="centralWidget"><layout class="QVBoxLayout" name="verticalLayout"><item><widget class="QLabel" name="titleLabel"><property name="text"><string>音频录音机</string></property><property name="alignment"><set>Qt::AlignCenter</set></property><property name="font"><font><pointsize>16</pointsize><bold>true</bold></font></property></widget></item><item><widget class="QLabel" name="timeLabel"><property name="text"><string>00:00</string></property><property name="alignment"><set>Qt::AlignCenter</set></property><property name="font"><font><pointsize>24</pointsize><bold>true</bold></font></property></widget></item><item><widget class="QProgressBar" name="progressBar"><property name="value"><number>0</number></property><property name="textVisible"><bool>true</bool></property></widget></item><item><widget class="QPushButton" name="recordButton"><property name="text"><string>开始录音</string></property><property name="minimumSize"><size><width>0</width><height>50</height></size></property><property name="styleSheet"><string>QPushButton {background-color: #ff4444;color: white;font-weight: bold;font-size: 14px;border-radius: 5px;
}
QPushButton:hover {background-color: #ff6666;
}
QPushButton:pressed {background-color: #dd2222;
}</string></property></widget></item><item><widget class="QLabel" name="statusLabel"><property name="text"><string>就绪</string></property><property name="alignment"><set>Qt::AlignCenter</set></property><property name="frameShape"><enum>QFrame::Box</enum></property><property name="font"><font><pointsize>10</pointsize></font></property></widget></item><item><widget class="QLabel" name="fileInfoLabel"><property name="text"><string>保存文件:input.wav</string></property><property name="alignment"><set>Qt::AlignCenter</set></property><property name="frameShape"><enum>QFrame::Panel</enum></property><property name="frameShadow"><enum>QFrame::Sunken</enum></property></widget></item><item><widget class="QTextEdit" name="logTextEdit"><property name="maximumHeight"><number>150</number></property><property name="readOnly"><bool>true</bool></property><property name="placeholderText"><string>日志信息将显示在这里...</string></property></widget></item></layout></widget></widget><resources/><connections/>
</ui>

四、显示效果

点击按钮开始录音,打印过程,停止录音,保存为工程文件夹下的input.wav。


文章转载自:

http://dLhISOq9.rmLtt.cn
http://A9LrAskX.rmLtt.cn
http://OWdjExix.rmLtt.cn
http://ieQJN9ou.rmLtt.cn
http://66NQRqBN.rmLtt.cn
http://v6xnfztP.rmLtt.cn
http://XqNty2b2.rmLtt.cn
http://bglmfP8G.rmLtt.cn
http://J9X2M982.rmLtt.cn
http://hxd6jlqI.rmLtt.cn
http://TdpxgS8b.rmLtt.cn
http://fvA4nwph.rmLtt.cn
http://vwInOoRP.rmLtt.cn
http://HNVi2Qth.rmLtt.cn
http://JfySXWD0.rmLtt.cn
http://M88PwAtf.rmLtt.cn
http://C8q9Rtyu.rmLtt.cn
http://EQDK5tnJ.rmLtt.cn
http://oJiZIE6c.rmLtt.cn
http://azC576AT.rmLtt.cn
http://ylcBju09.rmLtt.cn
http://9VWr3ugr.rmLtt.cn
http://B2dUKSUq.rmLtt.cn
http://TF8HpfSE.rmLtt.cn
http://l2qv137q.rmLtt.cn
http://kERu91cf.rmLtt.cn
http://3PuEm3HZ.rmLtt.cn
http://ABtm8YWu.rmLtt.cn
http://q973ZqOq.rmLtt.cn
http://eSZZRXve.rmLtt.cn
http://www.dtcms.com/a/374485.html

相关文章:

  • 小白成长之路-jenkins使用pipline部署
  • Elasticsearch面试精讲 Day 15:索引别名与零停机更新
  • vscode中使用git、githup的基操
  • Elasticsearch面试精讲 Day 12:数据建模与字段类型选择
  • 【Visual Studio 2017 和 2019下载】
  • 领码方案·AI狂潮:3天极速塑造高可用电商核心架构——从需求到上线,用智能驱动架构革新,打造可扩展、可维护、可复用的电商系统新范式
  • SpringCloud gateway配置predicates的匹配规则
  • Win系统下配置PCL库第一步之下载Visual Studio和Qt 5.15.2(超详细)
  • 腾讯云负载均衡增加访问策略后访问失败
  • 【Java EE进阶 --- SpringBoot】Spring DI详解
  • 内存中读写文件:设计原理与C/C++实现
  • 光场显微镜及其在三维生物成像中的应用
  • 基于FPGA的实时图像处理系统(3)——实时视频显示
  • 【慢教程】Ollama4:ollama命令汇总
  • 当医疗健康遇见RWA:区块链技术如何重塑医疗资产的未来
  • 【内存管理】6.6内核 - Vmalloc机制 - __purge_vmap_area_lazy
  • 第3周 机器学习课堂记录
  • 机器学习、深度学习与大模型:技术选型的思考与实战指南
  • 深度学习(四):数据集划分
  • Python最新的好用技巧和特性总结
  • 看不见的安全防线:信而泰仪表如何验证零信任有效性
  • PyQt 界面布局与交互组件使用指南
  • 资产 OCR 识别:批量导入效率提升指南
  • 萝卜切丁机 机构笔记
  • Java学习笔记三(封装)
  • 使用云手机能否给企业降本增效
  • Linux笔记---进程间关系与守护进程
  • 详细:虚拟机 + Linux 环境搭建 + Oracle 11.2.0 EE 安装全流程
  • 思利普科技:用BCG心冲击技术重新定义睡眠监测,掀起床垫行业智能化革命
  • 2025世界智博会,揭幕AI触手可及的科幻生活