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

开源 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增删改查

本章节主要内容是:实现多媒体控制中的音频播放,实时显示进度和控制。

1.代码分析

2.所有源码

3.效果演示

一、代码分析1. main.cpp 详细分析
 

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QIcon>
#include <QDebug>
#include "audioplayer.h"int main(int argc, char *argv[])
{// 启用高DPI缩放支持QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);// 创建GUI应用程序实例QGuiApplication app(argc, argv);app.setApplicationName("WAV音频播放器");app.setApplicationVersion("1.0");qDebug() << "应用程序启动...";// 注册AudioPlayer类到QML系统,使其可以在QML中使用// 参数说明:"AudioPlayer" - QML中的类型名,1,0 - 主版本和次版本,"AudioPlayer" - QML中的类名qmlRegisterType<AudioPlayer>("AudioPlayer", 1, 0, "AudioPlayer");// 创建QML引擎,负责加载和解释QML文件QQmlApplicationEngine engine;// 创建音频播放器实例并设置为QML上下文属性// 这样在QML中可以直接通过"audioPlayer"访问C++对象AudioPlayer *audioPlayer = new AudioPlayer(&app);engine.rootContext()->setContextProperty("audioPlayer", audioPlayer);// 定义要加载的QML文件URL(使用资源系统)const QUrl url(QStringLiteral("qrc:/main.qml"));// 连接对象创建信号,用于检测QML加载是否成功QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {// 回调函数:当QML对象创建完成后调用if (!obj && url == objUrl) {// 如果对象创建失败且URL匹配,输出错误并退出qCritical() << "QML加载失败:" << url;QCoreApplication::exit(-1);} else {// 加载成功,输出日志qDebug() << "QML加载成功,窗口已创建";}}, Qt::QueuedConnection);  // 使用队列连接确保在事件循环中执行// 连接警告信号,捕获QML中的警告和错误QObject::connect(&engine, &QQmlApplicationEngine::warnings, [](const QList<QQmlError> &warnings) {// 遍历所有警告并输出for (const QQmlError &error : warnings) {qWarning() << "QML警告:" << error.toString();}});// 加载QML文件engine.load(url);// 检查是否成功创建了根对象if (engine.rootObjects().isEmpty()) {qCritical() << "没有创建任何QML根对象";return -1;}qDebug() << "应用程序进入事件循环...";// 启动事件循环,程序开始运行return app.exec();
}


关键函数分析:

qmlRegisterType(): 将C++类注册到QML系统中

setContextProperty(): 将C++对象暴露给QML上下文

engine.load(): 加载并解析QML文件

app.exec(): 启动Qt事件循环


2. audioplayer.h 详细分析
 

#ifndef AUDIOPLAYER_H
#define AUDIOPLAYER_H#include <QObject>
#include <QMediaPlayer>
#include <QTimer>
#include <QFileInfo>
#include <QDir>
#include <QDateTime>class AudioPlayer : public QObject
{Q_OBJECT// Q_PROPERTY宏定义QML可访问的属性Q_PROPERTY(QString filePath READ filePath NOTIFY filePathChanged)Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)Q_PROPERTY(QString timeDisplay READ timeDisplay NOTIFY timeDisplayChanged)Q_PROPERTY(int progress READ progress NOTIFY progressChanged)Q_PROPERTY(int volume READ volume WRITE setVolume NOTIFY volumeChanged)Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY playingStateChanged)public:explicit AudioPlayer(QObject *parent = nullptr);// 属性读取函数QString filePath() const;QString statusText() const;QString timeDisplay() const;int progress() const;int volume() const;bool isPlaying() const;// Q_INVOKABLE宏标记的函数可以在QML中调用Q_INVOKABLE void playPause();  // 播放/暂停切换Q_INVOKABLE void stop();       // 停止播放Q_INVOKABLE void setVolume(int volume);  // 设置音量signals:// 信号定义,当属性改变时发射void filePathChanged();void statusTextChanged();void timeDisplayChanged();void progressChanged();void volumeChanged();void playingStateChanged();void logMessageReceived(const QString &message);  // 日志消息信号private slots:// 私有槽函数,处理播放器状态变化void handlePlayerStateChanged(QMediaPlayer::State state);void handlePlayerError(QMediaPlayer::Error error);void updatePlaybackProgress();  // 更新播放进度private:// 私有辅助函数bool checkAudioFile();  // 检查音频文件void logMessage(const QString &message);  // 记录日志// 成员变量QMediaPlayer *m_player;        // Qt多媒体播放器QTimer *m_progressTimer;       // 进度更新定时器QString m_audioFilePath;       // 音频文件路径QString m_statusText;          // 状态文本QString m_timeDisplay;         // 时间显示文本int m_progress;                // 播放进度(0-100)int m_volume;                  // 音量(0-100)bool m_isPlaying;              // 是否正在播放
};#endif // AUDIOPLAYER_H


关键设计模式:

属性绑定: 使用Q_PROPERTY实现C++与QML的数据绑定

信号槽机制: 实现对象间的松耦合通信

命令模式: Q_INVOKABLE方法提供QML可调用的接口


3. audioplayer.cpp 详细分析
构造函数
 

AudioPlayer::AudioPlayer(QObject *parent): QObject(parent), m_player(new QMediaPlayer(this))        // 创建播放器,设置父对象用于自动内存管理, m_progressTimer(new QTimer(this))       // 创建定时器, m_progress(0)                           // 初始化进度为0, m_volume(80)                            // 初始化音量为80%, m_isPlaying(false)                      // 初始化播放状态为false
{// 设置音频文件路径为当前目录下的Output.wavm_audioFilePath = QDir::currentPath() + "/Output.wav";// 配置播放器初始音量m_player->setVolume(m_volume);// 连接信号槽 - 使用旧式语法兼容Qt5.12connect(m_player, SIGNAL(stateChanged(QMediaPlayer::State)),this, SLOT(handlePlayerStateChanged(QMediaPlayer::State)));connect(m_player, SIGNAL(error(QMediaPlayer::Error)),this, SLOT(handlePlayerError(QMediaPlayer::Error)));// 使用新式语法连接定时器超时信号connect(m_progressTimer, &QTimer::timeout, this, &AudioPlayer::updatePlaybackProgress);// 初始化状态文本m_statusText = "就绪";m_timeDisplay = "00:00 / 00:00";// 检查音频文件可用性if (checkAudioFile()) {logMessage("音频文件检查正常,准备就绪");} else {m_statusText = "文件不可用";emit statusTextChanged();  // 发射信号通知QML更新}
}


属性读取函数

 

QString AudioPlayer::filePath() const { return m_audioFilePath; }
QString AudioPlayer::statusText() const { return m_statusText; }
QString AudioPlayer::timeDisplay() const { return m_timeDisplay; }
int AudioPlayer::progress() const { return m_progress; }
int AudioPlayer::volume() const { return m_volume; }
bool AudioPlayer::isPlaying() const { return m_isPlaying; }


核心控制函数
 

void AudioPlayer::playPause()
{if (m_player->state() == QMediaPlayer::PlayingState) {// 如果正在播放,则暂停logMessage("用户暂停播放");m_player->pause();} else {// 否则开始播放if (!checkAudioFile()) {logMessage("播放失败:音频文件不可用");return;}logMessage("开始播放音频文件...");// 设置媒体文件(使用本地文件URL)m_player->setMedia(QUrl::fromLocalFile(m_audioFilePath));// 检查媒体状态if (m_player->mediaStatus() == QMediaPlayer::InvalidMedia) {logMessage("错误:无法加载媒体文件");m_statusText = "错误:无效的媒体文件";emit statusTextChanged();return;}m_player->play();  // 开始播放// 启动进度更新定时器,每100ms更新一次m_progressTimer->start(100);logMessage("播放器启动完成");}
}void AudioPlayer::stop()
{if (m_player->state() != QMediaPlayer::StoppedState) {logMessage("用户停止播放");m_player->stop();m_progressTimer->stop();  // 停止进度更新}
}void AudioPlayer::setVolume(int volume)
{if (m_volume != volume) {m_volume = volume;m_player->setVolume(volume);  // 设置播放器音量logMessage(QString("音量设置: %1").arg(volume));emit volumeChanged();  // 通知QML音量已改变}
}


状态处理槽函数
 

void AudioPlayer::handlePlayerStateChanged(QMediaPlayer::State state)
{switch (state) {case QMediaPlayer::StoppedState:logMessage("播放停止");m_statusText = "播放完成";m_isPlaying = false;m_progressTimer->stop();  // 停止定时器m_progress = 100;         // 设置进度为100%emit progressChanged();   // 通知进度更新break;case QMediaPlayer::PlayingState:logMessage("播放进行中...");m_statusText = "正在播放";m_isPlaying = true;logMessage(QString("媒体时长: %1 毫秒").arg(m_player->duration()));break;case QMediaPlayer::PausedState:logMessage("播放暂停");m_statusText = "播放暂停";m_isPlaying = false;break;}// 发射状态改变信号,触发QML更新emit statusTextChanged();emit playingStateChanged();
}


错误处理槽函数
 

void AudioPlayer::handlePlayerError(QMediaPlayer::Error error)
{QString errorMsg;// 根据错误类型设置相应的错误消息switch (error) {case QMediaPlayer::NoError:return;  // 没有错误,直接返回case QMediaPlayer::ResourceError:errorMsg = "资源错误:无法访问媒体文件";break;case QMediaPlayer::FormatError:errorMsg = "格式错误:不支持的媒体格式";break;case QMediaPlayer::NetworkError:errorMsg = "网络错误";break;case QMediaPlayer::AccessDeniedError:errorMsg = "访问被拒绝:没有足够的权限";break;default:errorMsg = "未知错误";break;}logMessage(errorMsg);m_statusText = "播放错误";m_isPlaying = false;// 通知QML状态更新emit statusTextChanged();emit playingStateChanged();
}


进度更新函数
 

void AudioPlayer::updatePlaybackProgress()
{if (m_player->duration() > 0) {qint64 position = m_player->position();  // 当前播放位置(毫秒)qint64 duration = m_player->duration();  // 总时长(毫秒)int seconds = position / 1000;          // 转换为秒int totalSeconds = duration / 1000;// 格式化时间显示:分:秒 / 分:秒m_timeDisplay = QString("%1:%2 / %3:%4").arg(seconds / 60, 2, 10, QLatin1Char('0'))     // 分钟,2位,十进制,用0填充.arg(seconds % 60, 2, 10, QLatin1Char('0'))     // 秒.arg(totalSeconds / 60, 2, 10, QLatin1Char('0')).arg(totalSeconds % 60, 2, 10, QLatin1Char('0'));// 计算播放进度百分比int newProgress = (duration > 0) ? static_cast<int>(position * 100 / duration) : 0;if (m_progress != newProgress) {m_progress = newProgress;emit progressChanged();  // 进度改变时发射信号}emit timeDisplayChanged();  // 时间显示更新// 记录播放进度(每秒记录一次,避免过于频繁)static int lastSecond = -1;  // 静态变量保存上次记录的秒数if (seconds != lastSecond) {lastSecond = seconds;logMessage(QString("播放进度: %1% (%2/%3 秒)").arg(m_progress).arg(seconds).arg(totalSeconds));}}
}


文件检查和日志函数
 

bool AudioPlayer::checkAudioFile()
{QFileInfo fileInfo(m_audioFilePath);if (!fileInfo.exists()) {logMessage("错误:音频文件不存在 - " + m_audioFilePath);m_statusText = "错误:文件不存在";return false;}if (fileInfo.size() == 0) {logMessage("警告:音频文件为空");m_statusText = "警告:文件为空";return false;}logMessage(QString("找到音频文件:%1 (%2 字节)").arg(m_audioFilePath).arg(fileInfo.size()));return true;
}void AudioPlayer::logMessage(const QString &message)
{// 添加时间戳QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);// 输出到控制台qDebug() << logEntry;// 发射信号通知QML更新日志显示emit logMessageReceived(logEntry);
}


4. main.qml 详细分析
窗口和基础布局
 

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Window 2.12ApplicationWindow {id: windowwidth: 800height: 600minimumWidth: 600minimumHeight: 400title: "16K WAV音频播放器 - Qt5.12 QML"visible: true  // 关键:确保窗口可见// 背景渐变 - 使用Rectangle实现渐变背景Rectangle {anchors.fill: parent  // 填充整个父窗口gradient: Gradient {GradientStop { position: 0.0; color: "#2c3e50" }  // 顶部颜色GradientStop { position: 1.0; color: "#34495e" }  // 底部颜色}}// 主布局 - 使用ColumnLayout实现垂直排列ColumnLayout {anchors.fill: parent    // 填充父窗口anchors.margins: 20     // 外边距spacing: 15            // 子项间距


标题区域
 

        // 标题Label {text: "16K WAV音频播放器"Layout.alignment: Qt.AlignHCenter  // 水平居中color: "white"font.pixelSize: 24font.bold: true}


文件信息卡片

 

        // 文件信息卡片Rectangle {Layout.fillWidth: true  // 填充布局宽度height: 80color: "#34495e"        // 卡片背景色radius: 8               // 圆角半径border.color: "#3498db" // 边框颜色border.width: 1ColumnLayout {anchors.fill: parentanchors.margins: 10Label {text: "文件路径:"color: "#bdc3c7"  // 浅灰色font.pixelSize: 12}// 文件路径显示 - 绑定到C++对象的filePath属性Label {text: audioPlayer.filePathLayout.fillWidth: truecolor: "white"font.pixelSize: 14elide: Text.ElideMiddle  // 文本过长时中间显示省略号}// 状态显示 - 根据播放状态改变颜色Label {text: "状态: " + audioPlayer.statusTextcolor: audioPlayer.isPlaying ? "#2ecc71" : "#e74c3c"  // 播放时绿色,停止时红色font.pixelSize: 14font.bold: true}}}


播放控制区域
 

        // 播放控制区域Rectangle {Layout.fillWidth: trueheight: 120color: "transparent"  // 透明背景ColumnLayout {anchors.fill: parentspacing: 10// 时间显示 - 绑定到C++对象的timeDisplay属性Label {text: audioPlayer.timeDisplayLayout.alignment: Qt.AlignHCentercolor: "white"font.pixelSize: 18font.bold: true}// 进度条 - 绑定到C++对象的progress属性ProgressBar {id: progressBarLayout.fillWidth: truevalue: audioPlayer.progress / 100  // 转换为0-1范围background: Rectangle {implicitHeight: 6color: "#34495e"  // 背景轨道颜色radius: 3}contentItem: Item {implicitHeight: 6Rectangle {width: progressBar.visualPosition * parent.width  // 根据进度计算宽度height: parent.heightradius: 3gradient: Gradient {  // 进度条渐变效果GradientStop { position: 0.0; color: "#3498db" }GradientStop { position: 1.0; color: "#2980b9" }}}}}


控制按钮
 

                // 控制按钮RowLayout {Layout.alignment: Qt.AlignHCenterspacing: 20// 停止按钮Button {text: "停止"onClicked: audioPlayer.stop()  // 调用C++对象的stop方法background: Rectangle {color: "#e74c3c"  // 红色radius: 5}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}}// 播放/暂停按钮 - 文本根据状态动态变化Button {text: audioPlayer.isPlaying ? "暂停" : "播放"onClicked: audioPlayer.playPause()  // 调用C++对象的playPause方法background: Rectangle {color: "#2ecc71"  // 绿色radius: 5}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}}}


音量控制
 

                // 音量控制RowLayout {Layout.alignment: Qt.AlignHCenterspacing: 10Label {text: "音量:"color: "white"font.pixelSize: 14}// 音量滑块Slider {id: volumeSliderfrom: 0to: 100value: audioPlayer.volume  // 绑定到C++对象的volume属性onMoved: audioPlayer.setVolume(value)  // 拖动时设置音量background: Rectangle {implicitWidth: 200implicitHeight: 4color: "#34495e"radius: 2}handle: Rectangle {  // 滑块手柄x: volumeSlider.leftPadding + volumeSlider.visualPosition * (volumeSlider.availableWidth - width)y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2implicitWidth: 20implicitHeight: 20radius: 10  // 圆形手柄color: volumeSlider.pressed ? "#3498db" : "white"  // 按下时变色border.color: "#3498db"}}// 音量百分比显示Label {text: volumeSlider.value + "%"color: "white"font.pixelSize: 14Layout.minimumWidth: 40}}}}


日志区域

// 日志区域Rectangle {Layout.fillWidth: trueLayout.fillHeight: true  // 填充剩余高度color: "#2c3e50"radius: 8border.color: "#34495e"border.width: 2ColumnLayout {anchors.fill: parentspacing: 5Label {text: "播放日志:"color: "#bdc3c7"font.pixelSize: 14font.bold: trueLayout.leftMargin: 10Layout.topMargin: 5}// 滚动视图,包含日志文本区域ScrollView {Layout.fillWidth: trueLayout.fillHeight: trueclip: true  // 裁剪超出部分TextArea {id: logTextAreareadOnly: true  // 只读color: "#ecf0f1"font.pixelSize: 12font.family: "Consolas, Monaco, monospace"  // 等宽字体background: Rectangle {color: "transparent"}}}}}}


信号连接和初始化
 

    // 连接C++对象的日志信号到QMLConnections {target: audioPlayeronLogMessageReceived: {logTextArea.append(message)  // 收到日志时添加到文本区域}}// 组件完成时的初始化Component.onCompleted: {console.log("QML窗口初始化完成")logTextArea.append("音频播放器初始化完成")logTextArea.append("等待用户操作...")}
}

二、所有源码

audioplayer.h文件源码

#ifndef AUDIOPLAYER_H
#define AUDIOPLAYER_H#include <QObject>
#include <QMediaPlayer>
#include <QTimer>
#include <QFileInfo>
#include <QDir>
#include <QDateTime>class AudioPlayer : public QObject
{Q_OBJECTQ_PROPERTY(QString filePath READ filePath NOTIFY filePathChanged)Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)Q_PROPERTY(QString timeDisplay READ timeDisplay NOTIFY timeDisplayChanged)Q_PROPERTY(int progress READ progress NOTIFY progressChanged)Q_PROPERTY(int volume READ volume WRITE setVolume NOTIFY volumeChanged)Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY playingStateChanged)public:explicit AudioPlayer(QObject *parent = nullptr);QString filePath() const;QString statusText() const;QString timeDisplay() const;int progress() const;int volume() const;bool isPlaying() const;Q_INVOKABLE void playPause();Q_INVOKABLE void stop();Q_INVOKABLE void setVolume(int volume);signals:void filePathChanged();void statusTextChanged();void timeDisplayChanged();void progressChanged();void volumeChanged();void playingStateChanged();void logMessageReceived(const QString &message);private slots:void handlePlayerStateChanged(QMediaPlayer::State state);void handlePlayerError(QMediaPlayer::Error error);void updatePlaybackProgress();private:bool checkAudioFile();void logMessage(const QString &message);QMediaPlayer *m_player;QTimer *m_progressTimer;QString m_audioFilePath;QString m_statusText;QString m_timeDisplay;int m_progress;int m_volume;bool m_isPlaying;
};#endif // AUDIOPLAYER_H

audioplayer.cpp文件源码

#include "audioplayer.h"
#include <QDebug>AudioPlayer::AudioPlayer(QObject *parent): QObject(parent), m_player(new QMediaPlayer(this)), m_progressTimer(new QTimer(this)), m_progress(0), m_volume(80), m_isPlaying(false)
{// 设置音频文件路径m_audioFilePath = QDir::currentPath() + "/Output.wav";// 配置播放器m_player->setVolume(m_volume);// 连接信号槽connect(m_player, SIGNAL(stateChanged(QMediaPlayer::State)),this, SLOT(handlePlayerStateChanged(QMediaPlayer::State)));connect(m_player, SIGNAL(error(QMediaPlayer::Error)),this, SLOT(handlePlayerError(QMediaPlayer::Error)));connect(m_progressTimer, &QTimer::timeout, this, &AudioPlayer::updatePlaybackProgress);// 初始化状态m_statusText = "就绪";m_timeDisplay = "00:00 / 00:00";// 检查音频文件if (checkAudioFile()) {logMessage("音频文件检查正常,准备就绪");} else {m_statusText = "文件不可用";emit statusTextChanged();}
}QString AudioPlayer::filePath() const
{return m_audioFilePath;
}QString AudioPlayer::statusText() const
{return m_statusText;
}QString AudioPlayer::timeDisplay() const
{return m_timeDisplay;
}int AudioPlayer::progress() const
{return m_progress;
}int AudioPlayer::volume() const
{return m_volume;
}bool AudioPlayer::isPlaying() const
{return m_isPlaying;
}void AudioPlayer::playPause()
{if (m_player->state() == QMediaPlayer::PlayingState) {// 暂停播放logMessage("用户暂停播放");m_player->pause();} else {// 开始播放if (!checkAudioFile()) {logMessage("播放失败:音频文件不可用");return;}logMessage("开始播放音频文件...");m_player->setMedia(QUrl::fromLocalFile(m_audioFilePath));if (m_player->mediaStatus() == QMediaPlayer::InvalidMedia) {logMessage("错误:无法加载媒体文件");m_statusText = "错误:无效的媒体文件";emit statusTextChanged();return;}m_player->play();// 启动进度更新计时器m_progressTimer->start(100);logMessage("播放器启动完成");}
}void AudioPlayer::stop()
{if (m_player->state() != QMediaPlayer::StoppedState) {logMessage("用户停止播放");m_player->stop();m_progressTimer->stop();}
}void AudioPlayer::setVolume(int volume)
{if (m_volume != volume) {m_volume = volume;m_player->setVolume(volume);logMessage(QString("音量设置: %1").arg(volume));emit volumeChanged();}
}void AudioPlayer::handlePlayerStateChanged(QMediaPlayer::State state)
{switch (state) {case QMediaPlayer::StoppedState:logMessage("播放停止");m_statusText = "播放完成";m_isPlaying = false;m_progressTimer->stop();m_progress = 100;emit progressChanged();break;case QMediaPlayer::PlayingState:logMessage("播放进行中...");m_statusText = "正在播放";m_isPlaying = true;logMessage(QString("媒体时长: %1 毫秒").arg(m_player->duration()));break;case QMediaPlayer::PausedState:logMessage("播放暂停");m_statusText = "播放暂停";m_isPlaying = false;break;}emit statusTextChanged();emit playingStateChanged();
}void AudioPlayer::handlePlayerError(QMediaPlayer::Error error)
{QString errorMsg;switch (error) {case QMediaPlayer::NoError:return;case QMediaPlayer::ResourceError:errorMsg = "资源错误:无法访问媒体文件";break;case QMediaPlayer::FormatError:errorMsg = "格式错误:不支持的媒体格式";break;case QMediaPlayer::NetworkError:errorMsg = "网络错误";break;case QMediaPlayer::AccessDeniedError:errorMsg = "访问被拒绝:没有足够的权限";break;default:errorMsg = "未知错误";break;}logMessage(errorMsg);m_statusText = "播放错误";m_isPlaying = false;emit statusTextChanged();emit playingStateChanged();
}void AudioPlayer::updatePlaybackProgress()
{if (m_player->duration() > 0) {qint64 position = m_player->position();qint64 duration = m_player->duration();int seconds = position / 1000;int totalSeconds = duration / 1000;// 更新时间显示m_timeDisplay = QString("%1:%2 / %3:%4").arg(seconds / 60, 2, 10, QLatin1Char('0')).arg(seconds % 60, 2, 10, QLatin1Char('0')).arg(totalSeconds / 60, 2, 10, QLatin1Char('0')).arg(totalSeconds % 60, 2, 10, QLatin1Char('0'));// 更新进度int newProgress = (duration > 0) ? static_cast<int>(position * 100 / duration) : 0;if (m_progress != newProgress) {m_progress = newProgress;emit progressChanged();}emit timeDisplayChanged();// 记录播放进度(每秒一次)static int lastSecond = -1;if (seconds != lastSecond) {lastSecond = seconds;logMessage(QString("播放进度: %1% (%2/%3 秒)").arg(m_progress).arg(seconds).arg(totalSeconds));}}
}bool AudioPlayer::checkAudioFile()
{QFileInfo fileInfo(m_audioFilePath);if (!fileInfo.exists()) {logMessage("错误:音频文件不存在 - " + m_audioFilePath);m_statusText = "错误:文件不存在";return false;}if (fileInfo.size() == 0) {logMessage("警告:音频文件为空");m_statusText = "警告:文件为空";return false;}logMessage(QString("找到音频文件:%1 (%2 字节)").arg(m_audioFilePath).arg(fileInfo.size()));return true;
}void AudioPlayer::logMessage(const QString &message)
{QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);qDebug() << logEntry;emit logMessageReceived(logEntry);
}

main.qml文件源码

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Window 2.12ApplicationWindow {id: windowwidth: 800height: 600minimumWidth: 600minimumHeight: 400title: "16K WAV音频播放器 - Qt5.12 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: "16K WAV音频播放器"Layout.alignment: Qt.AlignHCentercolor: "white"font.pixelSize: 24font.bold: true}// 文件信息卡片Rectangle {Layout.fillWidth: trueheight: 80color: "#34495e"radius: 8border.color: "#3498db"border.width: 1ColumnLayout {anchors.fill: parentanchors.margins: 10Label {text: "文件路径:"color: "#bdc3c7"font.pixelSize: 12}Label {text: audioPlayer.filePathLayout.fillWidth: truecolor: "white"font.pixelSize: 14elide: Text.ElideMiddle}Label {text: "状态: " + audioPlayer.statusTextcolor: audioPlayer.isPlaying ? "#2ecc71" : "#e74c3c"font.pixelSize: 14font.bold: true}}}// 播放控制区域Rectangle {Layout.fillWidth: trueheight: 120color: "transparent"ColumnLayout {anchors.fill: parentspacing: 10// 时间显示Label {text: audioPlayer.timeDisplayLayout.alignment: Qt.AlignHCentercolor: "white"font.pixelSize: 18font.bold: true}// 进度条ProgressBar {id: progressBarLayout.fillWidth: truevalue: audioPlayer.progress / 100background: Rectangle {implicitHeight: 6color: "#34495e"radius: 3}contentItem: Item {implicitHeight: 6Rectangle {width: progressBar.visualPosition * parent.widthheight: parent.heightradius: 3gradient: Gradient {GradientStop { position: 0.0; color: "#3498db" }GradientStop { position: 1.0; color: "#2980b9" }}}}}// 控制按钮RowLayout {Layout.alignment: Qt.AlignHCenterspacing: 20Button {text: "停止"onClicked: audioPlayer.stop()background: Rectangle {color: "#e74c3c"radius: 5}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}}Button {text: audioPlayer.isPlaying ? "暂停" : "播放"onClicked: audioPlayer.playPause()background: Rectangle {color: "#2ecc71"radius: 5}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}}}// 音量控制RowLayout {Layout.alignment: Qt.AlignHCenterspacing: 10Label {text: "音量:"color: "white"font.pixelSize: 14}Slider {id: volumeSliderfrom: 0to: 100value: audioPlayer.volumeonMoved: audioPlayer.setVolume(value)background: Rectangle {implicitWidth: 200implicitHeight: 4color: "#34495e"radius: 2}handle: Rectangle {x: volumeSlider.leftPadding + volumeSlider.visualPosition * (volumeSlider.availableWidth - width)y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2implicitWidth: 20implicitHeight: 20radius: 10color: volumeSlider.pressed ? "#3498db" : "white"border.color: "#3498db"}}Label {text: volumeSlider.value + "%"color: "white"font.pixelSize: 14Layout.minimumWidth: 40}}}}// 日志区域Rectangle {Layout.fillWidth: trueLayout.fillHeight: truecolor: "#2c3e50"radius: 8border.color: "#34495e"border.width: 2ColumnLayout {anchors.fill: parentspacing: 5Label {text: "播放日志:"color: "#bdc3c7"font.pixelSize: 14font.bold: trueLayout.leftMargin: 10Layout.topMargin: 5}ScrollView {Layout.fillWidth: trueLayout.fillHeight: trueclip: trueTextArea {id: logTextAreareadOnly: truecolor: "#ecf0f1"font.pixelSize: 12font.family: "Consolas, Monaco, monospace"background: Rectangle {color: "transparent"}}}}}}// 连接日志信号Connections {target: audioPlayeronLogMessageReceived: {logTextArea.append(message)}}Component.onCompleted: {console.log("QML窗口初始化完成")logTextArea.append("音频播放器初始化完成")logTextArea.append("等待用户操作...")}
}

main.cpp文件源码

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QIcon>
#include <QDebug>
#include "audioplayer.h"int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);app.setApplicationName("WAV音频播放器");app.setApplicationVersion("1.0");qDebug() << "应用程序启动...";// 注册音频播放器到QMLqmlRegisterType<AudioPlayer>("AudioPlayer", 1, 0, "AudioPlayer");QQmlApplicationEngine engine;// 创建音频播放器实例并设置为上下文属性AudioPlayer *audioPlayer = new AudioPlayer(&app);engine.rootContext()->setContextProperty("audioPlayer", audioPlayer);// 加载QML文件const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl) {qCritical() << "QML加载失败:" << url;QCoreApplication::exit(-1);} else {qDebug() << "QML加载成功,窗口已创建";}}, Qt::QueuedConnection);// 处理引擎加载错误QObject::connect(&engine, &QQmlApplicationEngine::warnings,[](const QList<QQmlError> &warnings) {for (const QQmlError &error : warnings) {qWarning() << "QML警告:" << error.toString();}});engine.load(url);if (engine.rootObjects().isEmpty()) {qCritical() << "没有创建任何QML根对象";return -1;}qDebug() << "应用程序进入事件循环...";return app.exec();
}

三、效果演示

点击启动,播放进度和操作日志开始更新。

http://www.dtcms.com/a/477445.html

相关文章:

  • 【开题答辩全过程】以 宾馆客房管理系统为例,包含答辩的问题和答案
  • 宁波网站建设设计价格我需要做网站
  • 使用 PyTorch 实现 MNIST 手写数字识别
  • ComfyUI安装和启动攻略1
  • h5移动端开发民治网站优化培训
  • uniapp 微信小程序蓝牙接收中文乱码
  • 多制式基站综合测试线的架构与验证实践 (1)
  • Ceph 分布式存储学习笔记(四):文件系统存储管理
  • ceph设置标志位
  • 系统升级丨让VR全景制作更全面、更简单
  • PyTorch 实现 MNIST 手写数字识别全流程
  • PyTorch实现MNIST手写数字识别:从数据到模型全解析
  • PostgreSQL 测试磁盘性能
  • 北京网站开发科技企业网站
  • 干货|腾讯 Linux C/C++ 后端开发岗面试
  • 【深度学习新浪潮】如何入门分布式大模型推理?
  • 基于单片机的螺旋藻生长大棚PH智能控制设计
  • 分布式专题——42 MQ常见问题梳理
  • mapbox基础,使用矢量切片服务(pbf)加载symbol符号图层
  • Linux中setup_arch和setup_memory相关函数的实现
  • 智能合约在分布式密钥管理系统中的应用
  • Spark大数据分析与实战笔记(第六章 Kafka分布式发布订阅消息系统-01)
  • 做网络竞拍的网站需要什么厦门网站设计哪家公司好
  • React Native:从react的解构看编程众多语言中的解构
  • C++ 手写 List 容器实战:从双向链表原理到完整功能落地,附源码与测试验证
  • 化工课设代做网站网络宣传网站建设价格
  • 【第1篇】2025年羊城工匠杯nl2sql比赛介绍
  • 2025年ASP.NETMVC面试题库全解析
  • 机器学习:支持向量机
  • C 标准库 - `<locale.h>`