飞凌嵌入式
直播中

fsdzdzy

10年用户 260经验值
擅长:嵌入式技术 控制/MCU opencv
私信 关注
[技术]

【飞凌RK3568开发板试用体验】3-Qt开发一个音乐播放器

上篇文章:【飞凌RK3568开发板试用体验】2-RK3568源码编译与交叉编译环境搭建,进行了OK3568开发板从软件开发环境搭建,通过编译RK3568的源码,可以得到Qt开发的交叉编译相关工具。

本篇,就来在搭建好的软件开发中,进行Qt软件的开发测评。

在第一篇的开箱测评中,体验了OK3568板子自带的界面,有视频播放器、音乐播放器等,这些都实现了基本的播放功能,但没有对操作界面做更加丰富的开发,所以,本篇先来实现一个界面更加优美,操作更新方便的音乐播放器软件,可以实现音乐列表的显示与选择播放、歌词显示等,先来看下最终的效果:

本篇的Qt代码从野火开发板的例程中移植修改而来,下面分析下程序的代码结构。

1 音乐播放器开发总体结构

整个Qt音乐播放器项目的代码结构如下:

  • 主代码中是音乐播放器相关的代码,包括:
    • 音乐播放器主界面
    • 唱片动画界面:在音乐播放时显示一个唱片转动的动画效果
    • 操作按钮界面:实现播放、暂停、继续、上一首、下一首、进度调节,音量调节
    • 歌词显示界面:当有对应的歌词时,显示歌词
    • 歌词加载与计算
  • Ui代码中使用一些Qt的基本功能,包括:
    • 一个Qt界面基类
    • 滑条功能类
    • 图标按钮显示类
    • 列表功能类
  • Skin中是一些图片资源和字体/皮肤定义
  • 最后是编译的中间文件和编译结果存储的目录

下面分类介绍了程序的主要代码实现。

2 软件开发

首先是整体的主界面部分,进行各项功能的初始化显示,主要包括:

  • 唱片界面初始化
  • 歌词界面初始化
  • 按钮界面初始化
  • 播放列表初始化
void MusicPlayer::InitWidget()
{
    QtWidgetTitleBar *widgetTitle = new QtWidgetTitleBar(this);
    widgetTitle->SetScalSize(Skin::m_nScreenWidth, 60);
    widgetTitle->SetBackground(Qt::transparent);
    widgetTitle->setFont(QFont(Skin::m_strAppFontNormal));
    widgetTitle->SetTitle(tr("音乐播放器"), "#ffffff", 24);
    connect(widgetTitle, SIGNAL(signalBackHome()), this, SIGNAL(signalBackHome()));
​
    QHBoxLayout *horLayoutCentor = new QHBoxLayout();
    horLayoutCentor->setContentsMargins(0, 0, 0, 0);
    horLayoutCentor->setSpacing(0);
​
    //唱片界面
    m_recorder = new WidgetRecord(this);
    horLayoutCentor->addWidget(m_recorder, 1);
​
    //歌词界面
    m_lyricWidget = new LyricWidget(this);
    horLayoutCentor->addWidget(m_lyricWidget, 1);
​
    //按钮界面
    m_playerToolBar = new WidgetToolBar(this);
​
    connect(m_playerToolBar, SIGNAL(play()), this, SLOT(SltMusicPlay()));
    connect(m_playerToolBar, SIGNAL(pause()), this, SLOT(SltMusicPause()));
    connect(m_playerToolBar, SIGNAL(toolBarClicked(int)), this, SLOT(SltToolbarClicked(int)));
​
    QVBoxLayout *verLayoutAll = new QVBoxLayout(this);
    verLayoutAll->setContentsMargins(0, 0, 0, 0);
    verLayoutAll->setSpacing(0);
    verLayoutAll->addWidget(widgetTitle, 1);
    verLayoutAll->addLayout(horLayoutCentor, 6);
    verLayoutAll->addWidget(m_playerToolBar);
​
    // 播放列表
    m_widgetMusicList = new MusicPlayListWidget(this);
    connect(m_widgetMusicList, SIGNAL(signalMediaChanged(QString, QString)), this, SLOT(SltCurrentSongChanged(QString, QString)));
    m_widgetMusicList->hide();
​
    connect(m_playerToolBar, SIGNAL(next()), m_widgetMusicList->playList(), SLOT(next()));
    connect(m_playerToolBar, SIGNAL(previous()), m_widgetMusicList->playList(), SLOT(previous()));
    connect(m_playerToolBar, SIGNAL(currentPostionChanged(int)), this, SLOT(SltChangePostion(int)));
}

初始化各个界面后,还要连接对应的槽函数,如点击操作按钮后的处理、切换播放列表中的音乐时的处理等。

2.1 音乐播放

音乐播放部分,主要有三部分功能:

  • 音频解码播放:使用Qt自带的媒体播放器组件进行音频播放
  • 播放时的按钮操作:实现播放、暂停、继续、上一首、下一首、进度调节,音量调节
  • 唱片动画界面:在音乐播放时显示一个唱片转动的动画效果

2.1.1 QMediaPlayer播放音频

这里使用Qt自带的QMediaPlayer组件进行音频的播放,QMediaPlayer播放音频的基本步骤为:

  • 创建一个QMediaPlayer
  • 连接播放位置变化的槽函数
  • 设置播放的音乐文件
  • 设置音量
  • 开始播放
player = new QMediaPlayer;
connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
player->setMedia(QUrl::fromLocalFile("./Music/test.mp3"));
player->setVolume(50);
player->play();

本项目的音频播放初始化部分:

void MusicPlayer::InitPlayer()
{
    m_player = new QMediaPlayer(this);
    m_player->setPlaylist(m_widgetMusicList->playList());
    connect(m_player, SIGNAL(durationChanged(qint64)), this, SLOT(SltDurationChanged(qint64)));
    connect(m_player, SIGNAL(positionChanged(qint64)), this, SLOT(SltPostionChanged(qint64)));
    connect(m_player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(SltMediaError(QMediaPlayer::Error)));
​
    m_volumeSlider = new QtSliderBar(this);
    m_volumeSlider->SetHorizontal(false);
    m_volumeSlider->SetValue(100);
    m_volumeSlider->hide();
    connect(m_volumeSlider, SIGNAL(currentValueChanged(int)), m_player, SLOT(setVolume(int)));
}

2.1.2 播放操作按钮

音乐播放时,需要用到播放、暂停、继续、上一首、下一首、进度调节、音量调节等操作按钮,这里的对应按钮使用图标显示为对应的按钮,通过加载QPixmap来实现:

void WidgetToolBar::InitWidget()
{
    m_progressBar = new QtSliderBar(this);
    m_progressBar->SetHorizontal(true);
    m_progressBar->SetMaxValue(0);
    m_progressBar->SetValue(0);
    connect(m_progressBar, SIGNAL(currentValueChanged(int)), this, SIGNAL(currentPostionChanged(int)));
​
    m_btns.insert(0, new QtPixmapButton(0, QRect(12, 4, 60, 60), QPixmap(":/images/music/ic_prev.png"), QPixmap(":/images/music/ic_prev_pre.png")));
    m_btns.insert(1, new QtPixmapButton(1, QRect(64, 4, 60, 60), QPixmap(":/images/music/ic_play.png"), QPixmap(":/images/music/ic_pause.png")));
    m_btns.insert(2, new QtPixmapButton(2, QRect(128, 4, 60, 60), QPixmap(":/images/music/ic_next.png"), QPixmap(":/images/music/ic_next_pre.png")));
    m_btns.insert(3, new QtPixmapButton(3, QRect(680, 4, 60, 60), QPixmap(":/images/music/ic_volume.png"), QPixmap(":/images/music/ic_volume_pre.png")));
    m_btns.insert(4, new QtPixmapButton(4, QRect(740, 4, 60, 60), QPixmap(":/images/music/ic_list.png"), QPixmap(":/images/music/ic_list_pre.png")));
    m_btns.value(1)->setCheckAble(true);
    connect(this, SIGNAL(signalBtnClicked(int)), this, SLOT(SltBtnClicket(int)));
}

2.1.3 播放时的动画界面

为了在播放时能有视觉上的动态效果,这里加了一个唱片转动的动画效果,当音乐播放时,唱针移动到唱片上,唱片开始转动。

WidgetRecord::WidgetRecord(QWidget *parent) : QtWidgetBase(parent)
{
    m_nRotateCD = 0;
    m_nRotateHandle = 0;
    m_bPlay = false;
​
    m_nBaseWidth = 400;
    m_nBaseHeight = 350;
​
    m_handlePixmap = new PixmapItem(QPixmap(":/images/music/ic_handle.png"), this);
    m_pixmapRecord = QPixmap(":/images/music/ic_blackrecord.png");
    m_pixmapArtist = QPixmap(":/images/music/ic_cd.png");
​
    m_timer = new QTimer(this);
    m_timer->setInterval(100);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(SltCicleRun()));
​
    m_animationHandle = new QPropertyAnimation(m_handlePixmap, "ratote");
    m_animationHandle->setDuration(300);
    m_animationHandle->setEasingCurve(QEasingCurve::Linear);
    connect(m_animationHandle, SIGNAL(valueChanged(QVariant)), this, SLOT(SltHandleMove(QVariant)));
}

2.2 播放列表

播放列表功能用来实现音频文件的显示,以及手动指定切换要播放的音乐。

2.2.1 读取音乐文件

这里使用Qt的QMediaPlaylist组件来实现音乐列表的管理。QMediaPlaylist是一个列表,它可以保存媒体文件,包括媒体路径等信息,它具有着列表的性质,比如添加删除插入等,但它能做的,比单纯的储存要多得多。设置播放顺序,对播放的控制,保存到本地,从本地读取,都可以很方便地实现。

void MusicPlayListWidget::ScanDirMedias(const QString &path)
{
    QDir dir(path);
    if (!dir.exists())
        return;
    dir.setFilter(QDir::Dirs | QDir::Files);
    dir.setSorting(QDir::DirsFirst);
    QFileInfoList list = dir.entryInfoList();
    int index = m_mapItems.size();
    for (int i = 0; i < list.size(); i++)
    {
        QFileInfo fileInfo = list.at(i);
        if (fileInfo.fileName() == "." || fileInfo.fileName() == "..")
        {
            continue;
        }
​
        if (fileInfo.isDir())
        {
            ScanDirMedias(fileInfo.filePath());
        }
        else if (fileInfo.suffix() == "mp3")
        {
            QString strName = fileInfo.baseName().toLocal8Bit().constData();
            QString strPath = fileInfo.absoluteFilePath().toLocal8Bit().constData();
            m_playList->addMedia(QUrl::fromLocalFile(strPath));
            m_mapItems.insert(index, new QtListWidgetItem(index, strPath, strName, QPixmap()));
            index++;
        }
    }
}

2.2.2 音乐列表界面

音乐列表的具体界面实现,主要是在对应的矩形位置,显示各个音乐文件的名称:

MusicPlayListWidget::MusicPlayListWidget(QWidget *parent) : QtAnimationWidget(parent)
{
    this->setAttribute(Qt::WA_TranslucentBackground);
    m_colorBackground = QColor("#5362b5");
    m_nBaseWidth = 405;
    m_nBaseHeight = 350;
    m_nYPos = 60;
​
    m_listWidget = new PlayListWidget(this);
    QVBoxLayout *verLayout = new QVBoxLayout(this);
    verLayout->setContentsMargins(0, 10, 0, 10);
    verLayout->setSpacing(0);
    verLayout->addStretch(1);
    verLayout->addWidget(m_listWidget, 9);
​
    // 播放列表(创建一个QMediaPlaylist))
    m_playList = new QMediaPlaylist(this);
    m_playList->setPlaybackMode(QMediaPlaylist::Loop);
    connect(m_playList, SIGNAL(currentIndexChanged(int)), this, SLOT(SltCurrMediaChanged(int)));
    connect(m_listWidget, SIGNAL(currentIndexClicked(int)), m_playList, SLOT(setCurrentIndex(int)));
}

2.3 歌词显示

歌词显示功能,需要在音乐播放的时候,能根据播放的进度,显示对应的歌词。

2.3.1 歌词显示界面

歌词界面的具体界面实现,主要是在对应的矩形位置,显示当前音乐播放位置前后的音乐歌词,并高亮当前播放的那句歌词:

void LyricWidget::drawLyricLines(QPainter *painter)
{
    painter->save();
    QTextOption option;
    option.setWrapMode(QTextOption::WordWrap);
    option.setAlignment(Qt::AlignCenter);
​
    QRect rect(0, 0, m_nBaseWidth, 52);
    int index = 0;
    for (int i = 0; i < m_nShowCount; i++)
    {
        rect = QRect(rect.left(), rect.bottom(), rect.width(), LYRIC_LINE_HEIGHT);
        index = m_nStartIndex + i;
        QString strText = index < m_strLyricLines.size() ? m_strLyricLines.at(index) : "";
        painter->setPen(index == m_nCurrentIndex ? Qt::white : Qt::black);
        painter->drawText(rect, strText, option);
    }
    painter->restore();
}

2.3.1 歌词显示时间计算

通过下面这个函数,计算当前音乐播放的位置:

void LyricWidget::SetLyricPostion(int postion)
{
    if (!m_bLyricLoad)
        return;
    m_lyricFactory->findIndex(postion);
}

3 交叉编译与测试

Qt程序编写好后,通过交叉编译,来测试音乐播放器在OK3568-C板子上的播放效果。

为了方便每次的编译,我这里编写一个my3568build.sh的编译脚本:

#! /bin/bash
​
mkdir -p build
cd build
​
export PATH=/home/xxpcb/myTest/OK3568/sourcecode/OK3568-linux-source/buildroot/output/OK3568/host/bin:$PATH
​
qmake .. && make

然后执行该脚本即可编译:

./my3568build.sh

将编译成功的可执行文件MusicPlayer复制到OK3568-C板子中,然后在其同目录下创建一个music文件夹,里面放入若干个mp3音乐文件和对应的歌词文件,然后就可以测试了:

文件复制到板子,我这里使用的ADB无线传输的方式,比较方便,具体ADB操作演示,可参考上篇文章最后的Qt交叉编译与测试部分:【飞凌RK3568开发板试用体验】2-RK3568源码编译与交叉编译环境搭建

需注意的是,板子里的这个Linux系统,不支持中文的显示,所以我把歌名都先改成了字母后再复制到板子中进行测试。

另外,板子上没有自带扬声器,为了演示时能听到声音,我从其它的板子上找了一个小喇叭接了上去,可以播放声音。

实测效果见文末视频,整体体验播放流畅,界面操作也ok。

4 总结

本篇介绍了在OK3568-C开发板上进行音乐播放器的测评过程,首先使用Qt编写音乐播放器的代码,然后在Ubuntu中,使用搭建好的交叉编译环境进行代码编译,最后把编译出的可执行文件放到板子中进行实际测试。该音乐播放器实现的功能包括基础的播放功能、暂停与继续,音量调节,歌词显示,音乐列表显示,下一首、下一首切换,进度条调节播放进度等。实际测试在板子中播放操作流畅。

演示视频:

RK3568-Qt做一个音乐播放器

更多回帖

发帖
×
20
完善资料,
赚取积分