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

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

 相关链接:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



推荐链接:

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

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

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

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

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

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

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

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

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

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

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

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

推荐链接:

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

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

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

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

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

内容:Qt实现的WAV音频播放器应用程序。实现win10系统下,播放wav音频的功能。

目录:

1.功能介绍

2.核心代码分析

3.所有源码

4.显示效果

一.功能介绍

这是一个基于QMediaPlayer的音频播放器,主要功能包括:

音频播放控制:播放、停止、进度控制

文件检查:验证音频文件的存在和有效性

状态监控:实时显示播放状态和进度

错误处理:完善的错误检测和处理机制

日志系统:详细的运行日志记录

二、核心代码分析
头文件 (mainwindow.h)
 

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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 += widgets

CONFIG += 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.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    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_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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,点击播放按钮则开始播放。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值