开源 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实现的WAV音频播放器应用程序。实现win10系统下,播放wav音频的功能。
目录:
1.功能介绍
2.核心代码分析
3.所有源码
4.显示效果
一.功能介绍
这是一个基于QMediaPlayer的音频播放器,主要功能包括:
音频播放控制:播放、停止、进度控制
文件检查:验证音频文件的存在和有效性
状态监控:实时显示播放状态和进度
错误处理:完善的错误检测和处理机制
日志系统:详细的运行日志记录
二、核心代码分析
头文件 (mainwindow.h)
class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_playButton_clicked(); // 播放按钮点击void updatePlaybackProgress(); // 更新播放进度void handlePlayerStateChanged(QMediaPlayer::State state); // 播放状态变化void handlePlayerError(QMediaPlayer::Error error); // 错误处理private:Ui::MainWindow *ui;QMediaPlayer *player; // 媒体播放器核心QTimer *progressTimer; // 进度更新计时器QString audioFilePath; // 音频文件路径void logMessage(const QString &message); // 日志记录bool checkAudioFile(); // 文件检查
};
函数功能详细分析
1. 构造函数 MainWindow::MainWindow()
功能:初始化播放器界面和组件
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), player(new QMediaPlayer(this)) // 创建媒体播放器, progressTimer(new QTimer(this)) // 创建进度计时器
{ui->setupUi(this);setWindowTitle("16K WAV音频播放器 - Qt5.14"); // 设置窗口标题audioFilePath = QDir::currentPath() + "/Output.wav"; // 设置默认文件路径ui->filePathLabel->setText("文件: " + audioFilePath); // 显示文件路径player->setVolume(80); // 设置默认音量(80%)// 信号槽连接connect(player, SIGNAL(stateChanged(QMediaPlayer::State)),this, SLOT(handlePlayerStateChanged(QMediaPlayer::State)));connect(player, SIGNAL(error(QMediaPlayer::Error)),this, SLOT(handlePlayerError(QMediaPlayer::Error)));connect(progressTimer, &QTimer::timeout, this, &MainWindow::updatePlaybackProgress);// UI初始化ui->playButton->setText("开始播放");ui->statusLabel->setText("就绪");ui->progressBar->setValue(0);// 文件检查if (checkAudioFile()) {logMessage("音频文件检查正常,准备就绪");} else {ui->playButton->setEnabled(false); // 文件无效时禁用播放按钮}
}
2. 析构函数 MainWindow::~MainWindow()
功能:安全清理资源
MainWindow::~MainWindow()
{if (player->state() == QMediaPlayer::PlayingState) {player->stop(); // 如果正在播放,先停止}delete ui;
}
3. checkAudioFile() - 音频文件检查
功能:验证音频文件的存在和有效性
bool MainWindow::checkAudioFile()
{QFileInfo fileInfo(audioFilePath);if (!fileInfo.exists()) {logMessage("错误:音频文件不存在 - " + audioFilePath);ui->statusLabel->setText("错误:文件不存在");return false;}if (fileInfo.size() == 0) {logMessage("警告:音频文件为空");ui->statusLabel->setText("警告:文件为空");return false;}logMessage(QString("找到音频文件:%1 (%2 字节)").arg(audioFilePath).arg(fileInfo.size()));return true;
}
4. on_playButton_clicked() - 播放控制
功能:处理播放/停止按钮点击事件
void MainWindow::on_playButton_clicked()
{if (player->state() == QMediaPlayer::PlayingState) {// 停止播放logMessage("用户停止播放");player->stop();progressTimer->stop();ui->playButton->setText("开始播放");} else {// 开始播放if (!checkAudioFile()) {logMessage("播放失败:音频文件不可用");return;}logMessage("开始播放音频文件...");player->setMedia(QUrl::fromLocalFile(audioFilePath)); // 设置媒体源if (player->mediaStatus() == QMediaPlayer::InvalidMedia) {logMessage("错误:无法加载媒体文件");ui->statusLabel->setText("错误:无效的媒体文件");return;}player->play(); // 开始播放progressTimer->start(100); // 启动进度更新计时器(100ms间隔)logMessage("播放器启动完成");}
}
5. updatePlaybackProgress() - 进度更新
功能:实时更新播放进度显示
void MainWindow::updatePlaybackProgress()
{if (player->duration() > 0) {qint64 position = player->position(); // 当前位置(毫秒)qint64 duration = player->duration(); // 总时长(毫秒)int seconds = position / 1000; // 转换为秒int totalSeconds = duration / 1000;// 格式化时间显示 (MM:SS / MM:SS)ui->timeLabel->setText(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 progress = (duration > 0) ? static_cast<int>(position * 100 / duration) : 0;ui->progressBar->setValue(progress);// 每秒记录一次进度日志(避免过于频繁)static int lastSecond = -1;if (seconds != lastSecond) {lastSecond = seconds;logMessage(QString("播放进度: %1% (%2/%3 秒)").arg(progress).arg(seconds).arg(totalSeconds));}}
}
6. handlePlayerStateChanged() - 状态变化处理
功能:响应播放器状态变化
void MainWindow::handlePlayerStateChanged(QMediaPlayer::State state)
{switch (state) {case QMediaPlayer::StoppedState:logMessage("播放停止");ui->statusLabel->setText("播放完成");ui->playButton->setText("开始播放");progressTimer->stop(); // 停止进度更新ui->progressBar->setValue(100); // 进度条置满break;case QMediaPlayer::PlayingState:logMessage("播放进行中...");ui->statusLabel->setText("正在播放");ui->playButton->setText("停止播放");logMessage(QString("媒体时长: %1 毫秒").arg(player->duration()));break;case QMediaPlayer::PausedState:logMessage("播放暂停");ui->statusLabel->setText("播放暂停");ui->playButton->setText("继续播放");break;}
}
7. handlePlayerError() - 错误处理
功能:处理播放器错误
void MainWindow::handlePlayerError(QMediaPlayer::Error error)
{QString errorMsg;switch (error) {case QMediaPlayer::NoError:return;case QMediaPlayer::ResourceError:errorMsg = "资源错误:无法访问媒体文件";break;case QMediaPlayer::FormatError:errorMsg = "格式错误:不支持的媒体格式"; // WAV格式可能不支持break;case QMediaPlayer::NetworkError:errorMsg = "网络错误";break;case QMediaPlayer::AccessDeniedError:errorMsg = "访问被拒绝:没有足够的权限";break;default:errorMsg = "未知错误";break;}logMessage(errorMsg);ui->statusLabel->setText("播放错误");ui->playButton->setText("开始播放");QMessageBox::warning(this, "播放错误", errorMsg); // 弹出错误对话框
}
8. logMessage() - 日志记录
功能:统一的日志记录系统
void MainWindow::logMessage(const QString &message)
{QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);ui->logTextEdit->append(logEntry); // 添加到UI日志框qDebug() << logEntry; // 输出到控制台// 自动滚动到日志底部QTextCursor cursor = ui->logTextEdit->textCursor();cursor.movePosition(QTextCursor::End);ui->logTextEdit->setTextCursor(cursor);
}
三、所有源码
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 <QMediaPlayer>
#include <QTimer>
#include <QFileInfo>QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_playButton_clicked();void updatePlaybackProgress();void handlePlayerStateChanged(QMediaPlayer::State state);void handlePlayerError(QMediaPlayer::Error error);private:Ui::MainWindow *ui;QMediaPlayer *player;QTimer *progressTimer;QString audioFilePath;void logMessage(const QString &message);bool checkAudioFile();
};#endif // MAINWINDOW_H
3.mianwindow.cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDir>
#include <QDateTime>
#include <QFileInfo>
#include <QDebug>
#include <QMessageBox>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), player(new QMediaPlayer(this)), progressTimer(new QTimer(this))
{ui->setupUi(this);// 设置窗口标题setWindowTitle("16K WAV音频播放器 - Qt5.14");// 设置音频文件路径audioFilePath = QDir::currentPath() + "/Output.wav";ui->filePathLabel->setText("文件: " + audioFilePath);// 配置播放器player->setVolume(80); // 设置音量 0-100// 连接信号槽connect(player, SIGNAL(stateChanged(QMediaPlayer::State)),this, SLOT(handlePlayerStateChanged(QMediaPlayer::State)));connect(player, SIGNAL(error(QMediaPlayer::Error)),this, SLOT(handlePlayerError(QMediaPlayer::Error)));connect(progressTimer, &QTimer::timeout, this, &MainWindow::updatePlaybackProgress);// 初始化UI状态ui->playButton->setText("开始播放");ui->statusLabel->setText("就绪");ui->progressBar->setValue(0);// 检查音频文件if (checkAudioFile()) {logMessage("音频文件检查正常,准备就绪");} else {ui->playButton->setEnabled(false);}
}MainWindow::~MainWindow()
{if (player->state() == QMediaPlayer::PlayingState) {player->stop();}delete ui;
}bool MainWindow::checkAudioFile()
{QFileInfo fileInfo(audioFilePath);if (!fileInfo.exists()) {logMessage("错误:音频文件不存在 - " + audioFilePath);ui->statusLabel->setText("错误:文件不存在");return false;}if (fileInfo.size() == 0) {logMessage("警告:音频文件为空");ui->statusLabel->setText("警告:文件为空");return false;}logMessage(QString("找到音频文件:%1 (%2 字节)").arg(audioFilePath).arg(fileInfo.size()));return true;
}void MainWindow::on_playButton_clicked()
{if (player->state() == QMediaPlayer::PlayingState) {// 停止播放logMessage("用户停止播放");player->stop();progressTimer->stop();ui->playButton->setText("开始播放");} else {// 开始播放if (!checkAudioFile()) {logMessage("播放失败:音频文件不可用");return;}logMessage("开始播放音频文件...");player->setMedia(QUrl::fromLocalFile(audioFilePath));if (player->mediaStatus() == QMediaPlayer::InvalidMedia) {logMessage("错误:无法加载媒体文件");ui->statusLabel->setText("错误:无效的媒体文件");return;}player->play();// 启动进度更新计时器progressTimer->start(100);logMessage("播放器启动完成");}
}void MainWindow::updatePlaybackProgress()
{if (player->duration() > 0) {qint64 position = player->position();qint64 duration = player->duration();int seconds = position / 1000;int totalSeconds = duration / 1000;// 更新时间显示ui->timeLabel->setText(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 progress = (duration > 0) ? static_cast<int>(position * 100 / duration) : 0;ui->progressBar->setValue(progress);// 记录播放进度static int lastSecond = -1;if (seconds != lastSecond) {lastSecond = seconds;logMessage(QString("播放进度: %1% (%2/%3 秒)").arg(progress).arg(seconds).arg(totalSeconds));}}
}void MainWindow::handlePlayerStateChanged(QMediaPlayer::State state)
{switch (state) {case QMediaPlayer::StoppedState:logMessage("播放停止");ui->statusLabel->setText("播放完成");ui->playButton->setText("开始播放");progressTimer->stop();ui->progressBar->setValue(100);break;case QMediaPlayer::PlayingState:logMessage("播放进行中...");ui->statusLabel->setText("正在播放");ui->playButton->setText("停止播放");logMessage(QString("媒体时长: %1 毫秒").arg(player->duration()));break;case QMediaPlayer::PausedState:logMessage("播放暂停");ui->statusLabel->setText("播放暂停");ui->playButton->setText("继续播放");break;}
}void MainWindow::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);ui->statusLabel->setText("播放错误");ui->playButton->setText("开始播放");QMessageBox::warning(this, "播放错误", errorMsg);
}void MainWindow::logMessage(const QString &message)
{QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);ui->logTextEdit->append(logEntry);qDebug() << logEntry;// 自动滚动到底部QTextCursor cursor = ui->logTextEdit->textCursor();cursor.movePosition(QTextCursor::End);ui->logTextEdit->setTextCursor(cursor);
}
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>600</width><height>500</height></rect></property><property name="minimumSize"><size><width>600</width><height>500</height></size></property><property name="windowTitle"><string>16K WAV音频播放器</string></property><widget class="QWidget" name="centralWidget"><layout class="QVBoxLayout" name="verticalLayout"><item><widget class="QLabel" name="titleLabel"><property name="text"><string>16K WAV音频播放器 (Qt5.14)</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="filePathLabel"><property name="text"><string>文件路径显示</string></property><property name="frameShape"><enum>QFrame::Panel</enum></property><property name="frameShadow"><enum>QFrame::Sunken</enum></property><property name="wordWrap"><bool>true</bool></property></widget></item><item><widget class="QLabel" name="timeLabel"><property name="text"><string>00:00 / 00:00</string></property><property name="alignment"><set>Qt::AlignCenter</set></property><property name="font"><font><pointsize>18</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><property name="format"><string>%p%</string></property></widget></item><item><widget class="QPushButton" name="playButton"><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: #4CAF50;color: white;font-weight: bold;font-size: 14px;border-radius: 5px;border: none;
}
QPushButton:hover {background-color: #45a049;
}
QPushButton:pressed {background-color: #3d8b40;
}
QPushButton:disabled {background-color: #cccccc;
}</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><bold>true</bold></font></property></widget></item><item><widget class="QGroupBox" name="groupBox"><property name="title"><string>播放日志</string></property><layout class="QVBoxLayout" name="verticalLayout_2"><item><widget class="QTextEdit" name="logTextEdit"><property name="maximumHeight"><number>200</number></property><property name="readOnly"><bool>true</bool></property><property name="placeholderText"><string>播放日志将显示在这里...</string></property></widget></item></layout></widget></item></layout></widget></widget><resources/><connections/>
</ui>
四、显示效果
工程文件夹下需要有Output.wav,点击播放按钮则开始播放。