开源 C++ QT QML 开发(十九)多媒体--音频录制
文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT QML 开发(一)基本介绍
开源 C++ QT QML 开发(二)工程结构
开源 C++ QT QML 开发(三)常用控件
开源 C++ QT QML 开发(四)复杂控件--Listview
开源 C++ QT QML 开发(五)复杂控件--Gridview
开源 C++ QT QML 开发(六)自定义控件--波形图
开源 C++ QT QML 开发(七)自定义控件--仪表盘
开源 C++ QT QML 开发(八)自定义控件--圆环
开源 C++ QT QML 开发(九)文件--文本和二进制
开源 C++ QT QML 开发(十)通讯--串口
开源 C++ QT QML 开发(十一)通讯--TCP服务器端
开源 C++ QT QML 开发(十二)通讯--TCP客户端
开源 C++ QT QML 开发(十三)多线程
开源 C++ QT QML 开发(十四)进程用途
开源 C++ QT QML 开发(十五)通讯--http下载
开源 C++ QT QML 开发(十六)进程--共享内存
开源 C++ QT QML 开发(十七)进程--LocalSocket
开源 C++ QT QML 开发(十八)多媒体--音频播放
推荐链接:
开源 C# 快速开发(一)基础知识
开源 C# 快速开发(二)基础控件
开源 C# 快速开发(三)复杂控件
开源 C# 快速开发(四)自定义控件--波形图
开源 C# 快速开发(五)自定义控件--仪表盘
开源 C# 快速开发(六)自定义控件--圆环
开源 C# 快速开发(七)通讯--串口
开源 C# 快速开发(八)通讯--Tcp服务器端
开源 C# 快速开发(九)通讯--Tcp客户端
开源 C# 快速开发(十)通讯--http客户端
开源 C# 快速开发(十一)线程
开源 C# 快速开发(十二)进程监控
开源 C# 快速开发(十三)进程--管道通讯
开源 C# 快速开发(十四)进程--内存映射
开源 C# 快速开发(十五)进程--windows消息
开源 C# 快速开发(十六)数据库--sqlserver增删改查
本章节主要内容是:qml的多媒体音频的录制,保存为wav的16KHz格式。
1.代码分析
2.所有源码
3.效果演示
一、代码分析
1. AudioManager 类分析
1.1 构造函数 AudioManager::AudioManager()
AudioManager::AudioManager(QObject *parent): QObject(parent), m_audioInput(nullptr), m_outputFile(nullptr), m_buffer(nullptr), m_timer(new QTimer(this)), m_isRecording(false), m_audioDuration(0), m_statusText("就绪"), m_timeText("00:00"), m_progress(0), m_logText("")
{// 连接计时器信号到更新进度槽函数connect(m_timer, &QTimer::timeout, this, &AudioManager::updateProgress);// 记录启动日志logMessage("程序启动完成");
}
功能分析:
初始化所有成员变量:将指针变量初始化为nullptr,布尔值初始化为false,数值初始化为0,字符串初始化为默认值
创建计时器:QTimer用于定期更新录音进度显示
信号槽连接:将计时器的timeout信号连接到updateProgress槽函数,每100ms触发一次
初始状态设置:设置初始状态文本、时间显示和进度
日志记录:调用logMessage记录程序启动信息
1.2 析构函数 AudioManager::~AudioManager()
AudioManager::~AudioManager()
{stopRecording();
}
功能分析:
资源清理:在对象销毁时自动调用stopRecording()确保录音被正确停止
防止资源泄漏:确保所有动态分配的资源被正确释放
1.3 音频输入设置 AudioManager::setupAudioInput()
void AudioManager::setupAudioInput()
{// 设置音频格式:16kHz, 16位, 单声道QAudioFormat format;format.setSampleRate(16000); // 采样率16kHzformat.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()));// 创建音频输入对象m_audioInput = new QAudioInput(format, this);
}
功能分析:
音频格式配置:设置标准的PCM音频格式参数
硬件兼容性检查:检查默认音频设备是否支持指定格式
格式自适应:如果不支持则使用最接近的格式
日志记录:记录最终使用的音频格式信息
对象创建:创建QAudioInput对象用于音频输入
1.4 开始录音 AudioManager::startRecording()
void AudioManager::startRecording()
{if (m_isRecording) return; // 防止重复开始logMessage("开始录音...");// 设置音频输入setupAudioInput();// 创建输出文件QString filePath = QDir::currentPath() + "/input.wav";m_outputFile = new QFile(filePath, this);if (!m_outputFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {logMessage("错误:无法创建文件");delete m_outputFile;m_outputFile = nullptr;return;}// 先写入空的WAV文件头setupWavHeader(*m_outputFile, 0);// 创建缓冲区m_buffer = new QBuffer(this);m_buffer->open(QIODevice::ReadWrite);// 开始录音m_audioInput->start(m_buffer);m_isRecording = true;// 启动计时器m_timer->start(100); // 100ms间隔m_audioDuration = 0;// 更新状态m_statusText = "正在录音...";m_progress = 0;m_timeText = "00:00";// 发出属性变化信号emit isRecordingChanged();emit statusTextChanged();emit timeTextChanged();emit progressChanged();logMessage("录音进行中");
}
功能分析:
状态检查:防止在录音状态下重复开始
资源初始化:依次初始化音频输入、输出文件、缓冲区
文件操作:创建WAV文件并写入初始文件头
缓冲区设置:创建内存缓冲区存储音频数据
启动录音:调用QAudioInput的start()方法开始录音
计时器启动:开始计时器用于更新UI
状态更新:更新所有相关状态变量
信号发射:通知QML界面更新显示
1.5 停止录音 AudioManager::stopRecording()
void AudioManager::stopRecording()
{if (!m_isRecording) return; // 如果不在录音状态,直接返回logMessage("停止录音...");// 停止录音m_audioInput->stop();m_isRecording = false;// 停止计时器m_timer->stop();// 写入音频数据到文件if (m_buffer && m_outputFile) {QByteArray audioData = m_buffer->data();m_outputFile->seek(0); // 回到文件开头setupWavHeader(*m_outputFile, audioData.size()); // 重新写入正确的文件头m_outputFile->write(audioData); // 写入音频数据m_outputFile->close();logMessage(QString("录音完成,文件大小:%1 字节").arg(audioData.size()));}// 清理资源if (m_buffer) {m_buffer->close();m_buffer->deleteLater();m_buffer = nullptr;}if (m_outputFile) {m_outputFile->deleteLater();m_outputFile = nullptr;}if (m_audioInput) {m_audioInput->deleteLater();m_audioInput = nullptr;}// 更新状态m_statusText = "录音完成";m_progress = 100;// 发出属性变化信号emit isRecordingChanged();emit statusTextChanged();emit progressChanged();
}
功能分析:
状态检查:确保在录音状态下才执行停止操作
停止录音:调用QAudioInput的stop()方法
停止计时器:停止进度更新
文件处理:
获取缓冲区中的音频数据
重新写入正确的WAV文件头(包含实际数据大小)
写入音频数据并关闭文件
资源清理:使用deleteLater安全删除对象
状态更新:更新UI状态为完成
1.6 更新进度 AudioManager::updateProgress()
void AudioManager::updateProgress()
{m_audioDuration += 100; // 每次增加100msint seconds = m_audioDuration / 1000; // 转换为秒// 格式化时间显示:MM:SSm_timeText = QString("%1:%2").arg(seconds / 60, 2, 10, QLatin1Char('0')) // 分钟,2位,补0.arg(seconds % 60, 2, 10, QLatin1Char('0')); // 秒钟,2位,补0m_progress = qMin(100, seconds); // 进度最大100%// 发出变化信号emit timeTextChanged();emit progressChanged();
}
功能分析:
时间累计:每次调用增加100ms录音时间
时间格式化:将毫秒转换为MM:SS格式的字符串
进度计算:每秒增加1%进度,最大100%
信号发射:通知QML更新时间显示和进度条
1.7 WAV文件头设置 AudioManager::setupWavHeader()
void AudioManager::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=PCMquint16 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 * 1 * 16 / 8; // 采样率 × 声道数 × 位深度 / 8header.blockAlign = 1 * 16 / 8; // 声道数 × 位深度 / 8header.dataChunkSize = dataSize; // 音频数据大小// 写入文件头到文件file.write(reinterpret_cast<const char*>(&header), sizeof(WavHeader));
}
功能分析:
WAV文件结构:定义标准的WAV文件头结构
参数计算:
chunkSize = 36 + 数据大小(文件总大小-8)
byteRate = 采样率 × 声道数 × 位深度 ÷ 8
blockAlign = 声道数 × 位深度 ÷ 8
二进制写入:将结构体以二进制形式写入文件
1.8 日志记录 AudioManager::logMessage()
void AudioManager::logMessage(const QString &message)
{QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);// 添加换行符(如果不是第一条日志)if (!m_logText.isEmpty()) {m_logText += "\n";}m_logText += logEntry;// 发出日志变化信号emit logTextChanged();// 同时输出到调试控制台qDebug() << message;
}
功能分析:
时间戳生成:获取当前时间并格式化为hh:mm:ss
日志格式化:组合时间戳和消息内容
日志累积:在现有日志后追加新日志,用换行符分隔
信号通知:通知QML界面更新日志显示
调试输出:同时输出到qDebug便于调试
2. Main函数分析
2.1 应用程序初始化 main()
int main(int argc, char *argv[])
{// 启用高DPI缩放QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);// 创建GUI应用程序QGuiApplication app(argc, argv);// 设置应用程序信息app.setApplicationName("音频录音机");app.setApplicationVersion("1.0");app.setOrganizationName("MyCompany");// 注册AudioManager类到QML系统qmlRegisterType<AudioManager>("AudioRecorder", 1, 0, "AudioManager");// 创建QML引擎QQmlApplicationEngine engine;// 创建全局AudioManager实例并设置为上下文属性AudioManager *audioManager = new AudioManager(&app);engine.rootContext()->setContextProperty("audioManager", audioManager);// 加载QML文件const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);engine.load(url);// 检查QML加载是否成功if (engine.rootObjects().isEmpty()) {return -1;}// 进入事件循环return app.exec();
}
功能分析:
应用程序配置:设置高DPI缩放和应用信息
QML类型注册:将C++类注册到QML系统中
上下文属性设置:创建AudioManager实例并使其在QML中可用
QML加载:加载并显示QML界面
错误处理:检查QML加载是否成功
事件循环:启动Qt事件循环
3. QML界面分析
3.1 主窗口结构
ApplicationWindow {id: windowwidth: 600height: 500title: "音频录音机 - QML版本"visible: true// 背景渐变Rectangle {anchors.fill: parentgradient: Gradient {GradientStop { position: 0.0; color: "#2c3e50" }GradientStop { position: 1.0; color: "#34495e" }}}// ... 其他组件
}
功能分析:
窗口设置:定义窗口大小、标题和可见性
背景设计:使用渐变背景创造现代感界面
3.2 录音按钮实现
Button {id: recordButtontext: audioManager.isRecording ? "停止录音" : "开始录音"// ... 其他属性background: Rectangle {radius: 30color: audioManager.isRecording ? "#e74c3c" : "#2ecc71"// ... 其他属性Behavior on color {ColorAnimation { duration: 300 }}}onClicked: {if (audioManager.isRecording) {audioManager.stopRecording()} else {audioManager.startRecording()}}
}
功能分析:
动态文本:根据录音状态显示不同文本
颜色切换:录音时显示红色,停止时显示绿色
动画效果:颜色变化时有300ms的过渡动画
点击处理:调用C++的start/stopRecording方法
3.3 数据绑定机制
// 状态文本绑定
Label {text: audioManager.statusText // 自动绑定到C++属性
}// 时间显示绑定
Label {text: audioManager.timeText // 自动绑定到C++属性
}// 进度条绑定
ProgressBar {value: audioManager.progress // 自动绑定到C++属性
}// 日志显示绑定
TextArea {text: audioManager.logText // 自动绑定到C++属性
}
功能分析:
自动更新:当C++端属性变化并发出信号时,QML界面自动更新
双向通信:C++逻辑控制业务,QML界面负责显示和用户交互
4. 程序架构总结
这个录音程序采用了典型的MVC架构:
Model:AudioManager类,负责所有录音逻辑和数据处理
View:QML界面,负责用户界面显示和交互
Controller:QML中的事件处理和数据绑定
数据流:
用户点击QML按钮
QML调用C++的录音方法
C++处理录音逻辑并更新属性
C++发出属性变化信号
QML自动更新界面显示
二、所有源码
audiomanager.h文件源码
#ifndef AUDIOMANAGER_H
#define AUDIOMANAGER_H#include <QObject>
#include <QAudioInput>
#include <QAudioFormat>
#include <QFile>
#include <QBuffer>
#include <QTimer>
#include <QDateTime>class AudioManager : public QObject
{Q_OBJECTQ_PROPERTY(bool isRecording READ isRecording NOTIFY isRecordingChanged)Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)Q_PROPERTY(QString timeText READ timeText NOTIFY timeTextChanged)Q_PROPERTY(int progress READ progress NOTIFY progressChanged)Q_PROPERTY(QString logText READ logText NOTIFY logTextChanged)public:explicit AudioManager(QObject *parent = nullptr);~AudioManager();bool isRecording() const { return m_isRecording; }QString statusText() const { return m_statusText; }QString timeText() const { return m_timeText; }int progress() const { return m_progress; }QString logText() const { return m_logText; }Q_INVOKABLE void startRecording();Q_INVOKABLE void stopRecording();private slots:void updateProgress();private:void setupAudioInput();void setupWavHeader(QFile &file, quint32 dataSize);void logMessage(const QString &message);QAudioInput *m_audioInput;QFile *m_outputFile;QBuffer *m_buffer;QTimer *m_timer;bool m_isRecording;int m_audioDuration;// Q_PROPERTY backing fieldsQString m_statusText;QString m_timeText;int m_progress;QString m_logText;signals:void isRecordingChanged();void statusTextChanged();void timeTextChanged();void progressChanged();void logTextChanged();
};#endif // AUDIOMANAGER_H
audiomanager.cpp文件源码
#include "audiomanager.h"
#include <QAudioDeviceInfo>
#include <QDir>
#include <QDebug>
#include <cmath>AudioManager::AudioManager(QObject *parent): QObject(parent), m_audioInput(nullptr), m_outputFile(nullptr), m_buffer(nullptr), m_timer(new QTimer(this)), m_isRecording(false), m_audioDuration(0), m_statusText("就绪"), m_timeText("00:00"), m_progress(0), m_logText("")
{connect(m_timer, &QTimer::timeout, this, &AudioManager::updateProgress);logMessage("程序启动完成");
}AudioManager::~AudioManager()
{stopRecording();
}void AudioManager::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()));m_audioInput = new QAudioInput(format, this);
}void AudioManager::startRecording()
{if (m_isRecording) return;logMessage("开始录音...");// 设置音频输入setupAudioInput();// 创建输出文件QString filePath = QDir::currentPath() + "/input.wav";m_outputFile = new QFile(filePath, this);if (!m_outputFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {logMessage("错误:无法创建文件");delete m_outputFile;m_outputFile = nullptr;return;}// 先写入空的WAV文件头setupWavHeader(*m_outputFile, 0);// 创建缓冲区m_buffer = new QBuffer(this);m_buffer->open(QIODevice::ReadWrite);// 开始录音m_audioInput->start(m_buffer);m_isRecording = true;// 启动计时器m_timer->start(100);m_audioDuration = 0;// 更新状态m_statusText = "正在录音...";m_progress = 0;m_timeText = "00:00";emit isRecordingChanged();emit statusTextChanged();emit timeTextChanged();emit progressChanged();logMessage("录音进行中");
}void AudioManager::stopRecording()
{if (!m_isRecording) return;logMessage("停止录音...");// 停止录音m_audioInput->stop();m_isRecording = false;// 停止计时器m_timer->stop();// 写入音频数据到文件if (m_buffer && m_outputFile) {QByteArray audioData = m_buffer->data();m_outputFile->seek(0);setupWavHeader(*m_outputFile, audioData.size());m_outputFile->write(audioData);m_outputFile->close();logMessage(QString("录音完成,文件大小:%1 字节").arg(audioData.size()));}// 清理资源if (m_buffer) {m_buffer->close();m_buffer->deleteLater();m_buffer = nullptr;}if (m_outputFile) {m_outputFile->deleteLater();m_outputFile = nullptr;}if (m_audioInput) {m_audioInput->deleteLater();m_audioInput = nullptr;}// 更新状态m_statusText = "录音完成";m_progress = 100;emit isRecordingChanged();emit statusTextChanged();emit progressChanged();
}void AudioManager::updateProgress()
{m_audioDuration += 100;int seconds = m_audioDuration / 1000;m_timeText = QString("%1:%2").arg(seconds / 60, 2, 10, QLatin1Char('0')).arg(seconds % 60, 2, 10, QLatin1Char('0'));m_progress = qMin(100, seconds);emit timeTextChanged();emit progressChanged();
}void AudioManager::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 * 1 * 16 / 8; // sampleRate * channels * bitsPerSample / 8header.blockAlign = 1 * 16 / 8; // channels * bitsPerSample / 8header.dataChunkSize = dataSize;file.write(reinterpret_cast<const char*>(&header), sizeof(WavHeader));
}void AudioManager::logMessage(const QString &message)
{QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);if (!m_logText.isEmpty()) {m_logText += "\n";}m_logText += logEntry;emit logTextChanged();qDebug() << message;
}
main.qml文件源码
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14ApplicationWindow {id: windowwidth: 600height: 500title: "音频录音机 - QML版本"visible: true// 背景渐变Rectangle {anchors.fill: parentgradient: Gradient {GradientStop { position: 0.0; color: "#2c3e50" }GradientStop { position: 1.0; color: "#34495e" }}}ColumnLayout {anchors.fill: parentanchors.margins: 20spacing: 15// 标题Label {text: "音频录音机"Layout.alignment: Qt.AlignHCentercolor: "white"font.pixelSize: 28font.bold: true}// 状态显示区域Rectangle {Layout.fillWidth: trueLayout.preferredHeight: 120color: "#34495e"radius: 10border.color: "#3498db"border.width: 2ColumnLayout {anchors.fill: parentanchors.margins: 15spacing: 10// 状态标签Label {text: audioManager.statusTextLayout.fillWidth: truecolor: "white"font.pixelSize: 18horizontalAlignment: Text.AlignHCenter}// 时间显示Label {text: audioManager.timeTextLayout.fillWidth: truecolor: "#e74c3c"font.pixelSize: 32font.bold: truehorizontalAlignment: Text.AlignHCenter}// 进度条ProgressBar {id: progressBarLayout.fillWidth: truefrom: 0to: 100value: audioManager.progressbackground: Rectangle {implicitWidth: 200implicitHeight: 6color: "#2c3e50"radius: 3}contentItem: Item {implicitWidth: 200implicitHeight: 4Rectangle {width: progressBar.visualPosition * parent.widthheight: parent.heightradius: 2gradient: Gradient {GradientStop { position: 0.0; color: "#3498db" }GradientStop { position: 1.0; color: "#2980b9" }}}}}}}// 录音按钮Button {id: recordButtonLayout.alignment: Qt.AlignHCenterLayout.preferredWidth: 200Layout.preferredHeight: 60text: audioManager.isRecording ? "停止录音" : "开始录音"font.pixelSize: 18font.bold: truebackground: Rectangle {radius: 30color: audioManager.isRecording ? "#e74c3c" : "#2ecc71"border.color: audioManager.isRecording ? "#c0392b" : "#27ae60"border.width: 3Behavior on color {ColorAnimation { duration: 300 }}}contentItem: Text {text: recordButton.textfont: recordButton.fontcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}onClicked: {if (audioManager.isRecording) {audioManager.stopRecording()} else {audioManager.startRecording()}}}// 日志区域Rectangle {Layout.fillWidth: trueLayout.fillHeight: truecolor: "#1e272e"radius: 10border.color: "#7f8c8d"border.width: 1ColumnLayout {anchors.fill: parentspacing: 5Label {text: "操作日志"color: "#bdc3c7"font.pixelSize: 16font.bold: trueLayout.leftMargin: 10Layout.topMargin: 5}ScrollView {Layout.fillWidth: trueLayout.fillHeight: trueLayout.margins: 10TextArea {id: logTextAreatext: audioManager.logTextcolor: "#ecf0f1"font.pixelSize: 12font.family: "Consolas, Monaco, monospace"wrapMode: Text.WrapreadOnly: trueselectByMouse: truebackground: Rectangle {color: "transparent"}}}}}// 底部信息Label {text: "基于Qt5.14 QML的录音程序 | 支持WAV格式 | 16kHz 16位 单声道"Layout.alignment: Qt.AlignHCentercolor: "#95a5a6"font.pixelSize: 12}}// 录音时的动画效果Rectangle {id: recordingIndicatorwidth: 20height: 20radius: 10color: "#e74c3c"visible: audioManager.isRecordinganchors.top: parent.topanchors.right: parent.rightanchors.margins: 15SequentialAnimation on opacity {running: recordingIndicator.visibleloops: Animation.InfiniteNumberAnimation { from: 0.3; to: 1.0; duration: 800 }NumberAnimation { from: 1.0; to: 0.3; duration: 800 }}}
}
main.cpp文件源码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "audiomanager.h"int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);// 设置应用程序信息app.setApplicationName("音频录音机");app.setApplicationVersion("1.0");app.setOrganizationName("MyCompany");// 注册AudioManager类到QMLqmlRegisterType<AudioManager>("AudioRecorder", 1, 0, "AudioManager");QQmlApplicationEngine engine;// 创建全局AudioManager实例AudioManager *audioManager = new AudioManager(&app);engine.rootContext()->setContextProperty("audioManager", audioManager);const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);engine.load(url);if (engine.rootObjects().isEmpty()) {return -1;}return app.exec();
}
三、效果演示