简介
QMediaPlayer 可以用于播放经过压缩的音频文件,如 MP3 文件和 WMA 文件。QSoundEffect 可以 用于播放低延迟音效文件,例如无压缩的 WAV 文件。这两个类都可以用于播放本地文件和网络文件。
QMediaPlayer 与播放音频相关的接口函数如下:
void setAudioOutput(QAudioOutput *output) //设置一个音频输出设备
QAudioOutput *audioOutput() //返回播放器关联的音频输出设备信息
void setSource(const QUrl &source) //设置播放媒介来源,本地文件或网络文件
QUrl source() //当前播放的媒介来源
void setActiveAudioTrack(int index) //设置当前的音频轨道
void setPlaybackRate(qreal rate) //设置播放速度,1.0 表示正常速度
void setLoops(int loops) //设置播放的循环次数
QMediaPlayer::PlaybackState playbackState() //返回当前播放器状态
QMediaMetaData metaData() //返回当前媒介的元数据
QMediaPlayer::MediaStatus mediaStatus() //媒介状态(正在缓冲、已下载等),对于网络媒介比较有用
bool hasAudio() //当前媒介是否有音频
bool hasVideo() //当前媒介是否有视频
qint64 duration() //媒介的持续时间,单位为 ms
void setPosition(qint64 position) //设置当前的播放位置,单位为 ms
qint64 position() //返回当前的播放位置,单位为 ms
void play() //开始播放
void pause() //暂停播放
void stop() //停止播放
一般使用步骤
1、创建QMediaPlayer 对象,使用函数setAudioOutput()设置一个音频输出设备,
2、使用函数setSource()设置播放媒介来源(可以是本地文件或网络文件)
3、使用函数数 play() 开始播放了。使用 pause()和 stop()函数可以暂停和停止播放。
QMediaPlayer常用的一些信号如下:
void durationChanged(qint64 duration) //媒介的持续时间发生变化
void mediaStatusChanged(QMediaPlayer::MediaStatus status) //媒介状态发生变化
void metaDataChanged() //媒介的元数据发生变化
void playbackStateChanged(QMediaPlayer::PlaybackState newState) //播放器状态发生变化
void positionChanged(qint64 position) //播放位置发生变化
void sourceChanged(const QUrl &media) //媒介来源发生变化
QMediaPlayer 在开始、暂停或停止播放时,播放器状态发生变化,会发playbackStateChanged()
信号,函数 playbackState()会返回当前播放器状态。
媒介有元数据,函数 metaData()可以返回当前媒介的元数据,重新设置媒介时会发射 metaDataChanged()信号。媒介的元数据是 QMediaMetaData 类型数据,元数据用“key-value”形式 的键值对表示,QMediaMetaData 主要有以下几个函数:
QList<QMediaMetaData::Key> keys() //返回键名称列表
QString stringValue(QMediaMetaData::Key key) //以字符串形式返回一个键的数据
QVariant value(QMediaMetaData::Key key) //以 QVariant 类型返回一个键的数据
媒介元数据的键用枚举类型 QMediaMetaData::Key 的常量表示,常见枚举常量表示的元数据的类型和意义如下:
示例程序
该播放器可以打开多个文件后连续播放,可以显示播 放进度、歌曲对应的图片,还可以设置静音、调节音 量。文件列表里的项可以被拖动,从而改变其在列表 里的位置。
主窗口头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtMultimedia>
#include <QListWidgetItem>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QMediaPlayer *player; //播放器
bool loopPlay=true; //是否循环播放
QString durationTime; //文件总长度,mm:ss字符串
QString positionTime; //当前播放到位置,mm:ss字符串
QUrl getUrlFromItem(QListWidgetItem *item); //获取item的用户数据
bool eventFilter(QObject *watched, QEvent *event); //事件过滤处理
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
//自定义槽函数
void do_stateChanged(QMediaPlayer::PlaybackState state); //播放器状态发生变化
void do_sourceChanged(const QUrl &media); //文件发生变化
void do_durationChanged(qint64 duration); //文件长度发生变化
void do_positionChanged(qint64 position); //播放位置发生变化
void do_metaDataChanged(); //元数据发生变化
void on_btnAdd_clicked(); //添加按钮
void on_btnPlay_clicked(); //播放按钮
void on_btnPause_clicked(); //暂停按钮
void on_btnStop_clicked(); //停止按钮
void on_listWidget_doubleClicked(const QModelIndex &index); //双击事件
void on_btnClear_clicked(); //清空列表
void on_sliderVolumn_valueChanged(int value); //音量条数值改变
void on_btnSound_clicked(); //点击音量按钮
void on_sliderPosition_valueChanged(int value); //音频条数值改变
void on_btnPrevious_clicked(); //上一曲
void on_btnNext_clicked(); //下一曲
void on_btnLoop_clicked(bool checked); //循环播放
void on_doubleSpinBox_valueChanged(double arg1); //播放倍数
void on_btnRemove_clicked(); //移除
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
这里为主窗口定义了一个事件过滤器eventFilter(),后续listWidget的事件委托给窗口来监视并处理。被监视对象(这里是listWidget)使用函数 installEventFilter()将自己注册给监视对象(Mainwindow),监视对象就是事件过滤器。监视对象重新实现函数 eventFilter(),对监视到的事件进行处理。
在主窗口构造函数中,listWidget将自己的事件通过installEventFilter()委托给主窗口的eventFilter,接下来创建了QMediaPlayer对象用于音频播放。创建的QAudioOutput指向默认音频输出设备,然后用 setAudioOutput() 函数设置播放器player的音频输出设备。最后为QMediaPlayer对象发射的一些信号设置了相关的槽函数用于界面响应和控制。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->listWidget->installEventFilter(this); //安装事件过滤器,将窗口对象设置为事件过滤器
ui->listWidget->setDragEnabled(true); //允许拖放操作
ui->listWidget->setDragDropMode(QAbstractItemView::InternalMove); //列表项可移动
player = new QMediaPlayer(this); //创建音视频对象
QAudioOutput *audioOutput = new QAudioOutput(this); //音频输出,指向默认的音频输出设备
player->setAudioOutput(audioOutput); //设置音频输出
connect(player,&QMediaPlayer::positionChanged, //播放位置发生变化
this, &MainWindow::do_positionChanged);
connect(player,&QMediaPlayer::durationChanged, //播放源长度发生变化
this, &MainWindow::do_durationChanged);
connect(player, &QMediaPlayer::sourceChanged, //播放源发生变化
this, &MainWindow::do_sourceChanged);
connect(player, &QMediaPlayer::playbackStateChanged, //播放器状态发生变化
this, &MainWindow::do_stateChanged);
connect(player, &QMediaPlayer::metaDataChanged, //元数据发生变化
this, &MainWindow::do_metaDataChanged);
}
//为listWidget安装事件过滤器,用于delete按键移除曲目
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() != QEvent::KeyPress) //不是KeyPress事件,退出
return QWidget::eventFilter(watched,event);
QKeyEvent *keyEvent=static_cast<QKeyEvent *>(event);
if (keyEvent->key() != Qt::Key_Delete) //按下的不是Delete键,退出
return QWidget::eventFilter(watched,event);
if (watched==ui->listWidget) //判断被监视的对象是否是listWidget
{
QListWidgetItem *item= ui->listWidget->takeItem(ui->listWidget->currentRow());
delete item;
}
return true; //表示事件已经被处理
}
QAudioOutput 是指向音频输出设备的类,它有如下几个函数:
void setDevice(const QAudioDevice &device) //设置一个 QAudioDevice 设备
QAudioDevice device() //返回当前的 QAudioDevice 设备
void setMuted(bool muted) //设置是否静音
bool isMuted() //是否静音了
void setVolume(float volume) //设置音量
float volume() //返回当前音量
一般创建QAudioOutput时就已经将其指向了默认的音频输出设备,如果要设置其他输出设备使用setDevice()函数,device()函数返回当前的输出设备,类型是QMediaDevices。
QMediaDevices 类提供系统内 的多媒体设备信息,它有以下几个静态函数,用于返回系统中的默认多媒体设备:
QAudioDevice QMediaDevices::defaultAudioInput() //返回默认的音频输入设备(如麦克风)信息
QAudioDevice QMediaDevices::defaultAudioOutput() //返回默认的音频输出设备(如音箱)信息
QCameraDevice QMediaDevices::defaultVideoInput() //返回默认的视频输入设备(如摄像头)信息
添加文件和移除文件
添加文件按钮的槽函数为on_btnAdd_clicked(),可以一次打开多个文件,文件被添加到界面上的列表组件 listWidget 里。
void MainWindow::on_btnAdd_clicked()
{//"添加"按钮,添加文件
QString curPath=QDir::homePath(); //获取系统当前目录
QString dlgTitle="选择音频文件";
QString filter="音频文件(*.mp3 *.wav *.wma);;所有文件(*.*)"; //文件过滤器
QStringList fileList=QFileDialog::getOpenFileNames(this,dlgTitle,curPath,filter); //可以选择多个文件
if (fileList.count()<1)
return;
for (int i=0; i<fileList.size();i++) //依次将选择的文件添加到listWidget,并将文件路径保存到用户数据中
{
QString aFile=fileList.at(i);
QFileInfo fileInfo(aFile);
QListWidgetItem *aItem =new QListWidgetItem(fileInfo.fileName());
aItem->setIcon(QIcon(":/images/images/musicFile.png"));
aItem->setData(Qt::UserRole, QUrl::fromLocalFile(aFile)); //设置用户数据,QUrl对象
ui->listWidget->addItem(aItem);
}
if (player->playbackState() != QMediaPlayer::PlayingState) //添加后开始播放第一个文件
{ //当前没有在播放,就播放第1个文件
ui->listWidget->setCurrentRow(0);
QUrl source= getUrlFromItem(ui->listWidget->currentItem());
player->setSource(source); //设置播放文件的路径
}
player->play(); //播放文件
}
QUrl MainWindow::getUrlFromItem(QListWidgetItem *item)
{
QVariant itemData= item->data(Qt::UserRole); //获取用户数据
QUrl source =itemData.value<QUrl>(); //QVariant转换为QUrl类型
return source;
}
根据选择的音频文件将其文件名添加到listWidget中,并设置其用户数据为QUrl类型的本地文件路径,后续播放listWidget中的曲目时,通过自定义函数getUrlFromItem从listWidget条目中获取到保存的本地文件路径,就可以使用setSource()函数设置播放媒介。
移除按钮对应的槽函数如下:
void MainWindow::on_btnRemove_clicked()
{//"移除"按钮,移除列表中的当前项
int index =ui->listWidget->currentRow();
if (index>=0)
{
QListWidgetItem *item= ui->listWidget->takeItem(index); //new出来的对象移除时要使用delete
delete item;
}
}
在这个函数中,选择当前的listWidget进行删除,前面创建条目时使用的是new,那就对应使用delete删除
清空按钮对应的槽函数如下:
void MainWindow::on_btnClear_clicked()
{//"清空"按钮,清空播放列表
loopPlay=false; //防止do_stateChanged()里切换曲目
ui->listWidget->clear();
player->stop();
}
清空可以直接调用listWidget的clear()函数删除全部条目。
上一曲下一曲
这实际上还是对于listWidget中条目的处理,获取到当前的前一个/下一个条目后,取出条目中保存的用户数据(QUrl类型的本地文件路径)获得对应文件的路径,然后设置播放媒介即可。
void MainWindow::on_btnPrevious_clicked()
{//前一曲
int curRow=ui->listWidget->currentRow();
curRow--;
curRow= curRow<0? 0:curRow;
ui->listWidget->setCurrentRow(curRow); //设置当前行
loopPlay=false; //暂时设置为false,防止do_stateChanged()里切换曲目
player->setSource(getUrlFromItem(ui->listWidget->currentItem()));
player->play();
loopPlay=ui->btnLoop->isChecked();
// if(ui->btnLoop->isChecked())
// {
// loopPlay=false; //暂时设置为false,防止do_stateChanged()里切换曲目
// player->setSource(getUrlFromItem(ui->listWidget->currentItem()));
// player->play();
// loopPlay=true;
// }
}
void MainWindow::on_btnNext_clicked()
{//下一曲
int count=ui->listWidget->count();
int curRow=ui->listWidget->currentRow();
curRow++;
curRow= curRow>=count? count-1:curRow;
ui->listWidget->setCurrentRow(curRow);
loopPlay=false; //暂时设置为false,防止do_stateChanged()里切换曲目
player->setSource(getUrlFromItem(ui->listWidget->currentItem()));
player->play();
loopPlay=ui->btnLoop->isChecked();
// if(ui->btnLoop->isChecked())
// {
// loopPlay=false; //暂时设置为false,防止do_stateChanged()里切换曲目
// player->setSource(getUrlFromItem(ui->listWidget->currentItem()));
// player->play();
// loopPlay=true;
// }
}
这里切换曲目时要注意循环播放的问题。如果界面上的“循环”按钮是被选中的,那么 loopPlay 值为 true。如果直接重新设置播放源,播放器的状态会变为停止状态,那么 do_stateChanged()函数就会自动切换曲目,导致混乱。因此,程序里先把变量 loopPlay 设置为 false,避免do_stateChanged()函数切换曲目,重新设置曲目并开始播放后,再重新设置变量 loopPlay 的值。
QMediaPlayer 各信号的处理
切换播放文件时 player 会发射 sourceChanged()和 metaDataChanged()信号。
sourceChanged()信号对应的槽函数如下,主要用于更新界面label的显示
void MainWindow::do_sourceChanged(const QUrl &media)
{//播放的文件发生变化时的响应
ui->labCurMedia->setText(media.fileName());
}
metaDataChanged()信号对应的槽函数如下:
void MainWindow::do_metaDataChanged()
{//元数据变化时执行,显示歌曲图片
QMediaMetaData metaData=player->metaData(); //元数据对象
QVariant metaImg= metaData.value(QMediaMetaData::ThumbnailImage); //获取ThumbnailImage元数据,无效
// QVariant metaImg= metaData.value(QMediaMetaData::CoverArtImage); //获取 CoverArtImage 元数据,无效
if (metaImg.isValid())
{
QImage img= metaImg.value<QImage>(); //QVariant转换为QImage
QPixmap musicPixmp= QPixmap::fromImage(img);
if (ui->scrollArea->width() <musicPixmp.width())
ui->labPic->setPixmap(musicPixmp.scaledToWidth(ui->scrollArea->width()-30));
else
ui->labPic->setPixmap(musicPixmp);
}
else
ui->labPic->clear();
}
当播放媒介的元数据发生变化时,就读取媒介元数据中的 QMediaMetaData::ThumbnailImage
键的数据,这是歌曲的内嵌图片,是 QImage 类型的,如果判断有效就使用QPixmap显示到Label中,这里需要判断一下显示区域是否足够,并进行相应的调整。
播放源时长和播放位置发生变化时,player 会发射 durationChanged()和 positionChanged()信号。
durationChanged()信号对应的槽函数如下:
void MainWindow::do_durationChanged(qint64 duration)
{//播放源时长变化时执行,更新进度显示,一般切换曲目时会触发
ui->sliderPosition->setMaximum(duration);
int secs=duration/1000; //秒
int mins=secs/60; //分钟
secs=secs % 60; //余数秒
durationTime=QString::asprintf("%d:%d",mins,secs);
ui->labRatio->setText(positionTime+"/"+durationTime);
}
一般曲目发生更改时,播放源的时长会发生变化,在这个槽函数中重新设置了进度条
positionChanged()信号对应的槽函数如下:
void MainWindow::do_positionChanged(qint64 position)
{//播放位置变化时执行,更新进度显示
if (ui->sliderPosition->isSliderDown()) //滑条正被鼠标拖动
return;
ui->sliderPosition->setSliderPosition(position);
int secs=position/1000; //秒
int mins=secs/60; //分钟
secs=secs % 60; //余数秒
positionTime=QString::asprintf("%d:%d",mins,secs);
ui->labRatio->setText(positionTime+"/"+durationTime);
}
这个槽函数主要是用于更新界面进度条显示的,以实时显示播放的进度。
播放器开始播放、暂停播放和停止播放时会发射 playbackStateChanged()信号,该信号对应的槽函数如下所示:
void MainWindow::do_stateChanged(QMediaPlayer::PlaybackState state)
{//播放器状态变化时执行,更新按钮状态,或播放下一曲
ui->btnPlay->setEnabled(state!=QMediaPlayer::PlayingState);
ui->btnPause->setEnabled(state==QMediaPlayer::PlayingState);
ui->btnStop->setEnabled(state==QMediaPlayer::PlayingState);
//播放完一曲后停止了,如果loopPlay为true,自动播放下一曲
if (loopPlay && (state ==QMediaPlayer::StoppedState))
{
int count=ui->listWidget->count();
int curRow=ui->listWidget->currentRow();
curRow++;
curRow= curRow>=count? 0:curRow; //最后一曲播放完后切换到第一曲
ui->listWidget->setCurrentRow(curRow);
player->setSource(getUrlFromItem(ui->listWidget->currentItem()));
player->play();
}
}
MainWindow 类的私有变量 loopPlay 表示是否要循环播放,使用界面上的“循环”按钮可以 对应设置这个变量的值。QMediaPlayer 播放完当前曲目后就进入停止状态,不会自动播放下一曲。 为了实现循环播放,我们将界面组件 listWidget 的当前行下移或重设为 0,然后重新设置播放器的播放媒介并开始播放。
其他播放控制
界面下方的一些按钮和组件用于进行播放控制和设置,包括控制播放器开始播放、暂停播放和停止播放,设置播放倍速,设置是否循环播放,设置静音和音量,拖动播放进度条的滑块直接改变播放位置。对应的槽函数如下所示:
void MainWindow::on_btnPlay_clicked()
{//开始播放
if (ui->listWidget->currentRow()<0) //没有选择文件,就播放第1个
ui->listWidget->setCurrentRow(0);
player->setSource(getUrlFromItem(ui->listWidget->currentItem()));
player->play();
loopPlay=ui->btnLoop->isChecked(); //是否循环播放
}
void MainWindow::on_btnPause_clicked()
{//暂停播放
player->pause();
}
void MainWindow::on_btnStop_clicked()
{//停止播放
loopPlay=false;
player->stop();
}
void MainWindow::on_sliderVolumn_valueChanged(int value)
{//调整音量
player->audioOutput()->setVolume(value/100.0); //0~ 1之间
}
void MainWindow::on_btnSound_clicked()
{//静音控制
bool mute=player->audioOutput()->isMuted();
player->audioOutput()->setMuted(!mute);
if (mute)
ui->btnSound->setIcon(QIcon(":/images/images/volumn.bmp"));
else
ui->btnSound->setIcon(QIcon(":/images/images/mute.bmp"));
}
void MainWindow::on_sliderPosition_valueChanged(int value)
{//播放进度调控
player->setPosition(value);
}
void MainWindow::on_doubleSpinBox_valueChanged(double arg1)
{//"倍速" DoubleSpinbox
player->setPlaybackRate(arg1);
}
QSoundEffect 播放音效文件
QSoundEffect 用于播放低延迟音效文件,例如无压缩的 WAV 文件,从而实现一些音效,例如按键音、提示音,游戏中的爆炸音、开枪音等。QSoundEffect 不仅可以播放本地文件,还可以播放网络文件。通常使用该类播放一些时长较短的音频。
示例代码如下:
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPaintEvent>
void Widget::defense(QString weapon)
{
QUrl url=QUrl::fromLocalFile(appPath+"/sound/"+weapon);
player1->setSource(url);
player1->play();
}
void Widget::attack(QString weapon)
{
QUrl url=QUrl::fromLocalFile(appPath+"/sound/"+weapon);
player2->setSource(url);
player2->play();
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawPixmap(0,0,this->width(), this->height(),pixBackground);
event->accept();
}
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
appPath=QCoreApplication::applicationDirPath(); // 无“/”
pixBackground.load(appPath+"/sound/background.jpg");
player1=new QSoundEffect(this);
player1->setLoopCount(3);
player2=new QSoundEffect(this);
player2->setLoopCount(3);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
defense("Ak7.wav");
}
void Widget::on_pushButton_2_clicked()
{
defense("machinegun.wav");
}
void Widget::on_pushButton_5_clicked()
{
attack("Ak7.wav");
}
void Widget::on_pushButton_6_clicked()
{
attack("machinegun.wav");
}
void Widget::on_pushButton_8_clicked()
{
attack("blast.wav");
}
void Widget::on_pushButton_12_clicked()
{
attack("tank.wav");
}
void Widget::on_pushButton_11_clicked()
{
attack("mine.wav");
}
void Widget::on_pushButton_9_clicked()
{
defense("shell.wav");
}
void Widget::on_pushButton_4_clicked()
{
defense("blast.wav");
}
void Widget::on_pushButton_10_clicked()
{
defense("blast2.wav");
}
void Widget::on_pushButton_3_clicked()
{
defense("fire.wav");
}
void Widget::on_pushButton_7_clicked()
{
attack("fire2.wav");
}
参考
QT6 C++开发指南
标签:多媒体,QT,void,MainWindow,player,ui,listWidget,播放,音频 From: https://blog.csdn.net/qq_46144191/article/details/144577632