From 10e5af05881d438708f5db6b2b723553f758907b Mon Sep 17 00:00:00 2001 From: Maurice O'Neal Date: Mon, 10 Oct 2016 13:29:41 -0400 Subject: [PATCH] I've fully written out code for the base of the app and tested to to be working, also added svg icons to the user interface. the settings button currently does nothing (still work in progress). during testing, i discovered QMediaPlayer would have trouble playing some music files that have an ID3 tag so AudFile was created as a way to read the size of the ID3 tag and step over it when QMediaPlayer reads from the file. although it fixed the playback issue, QMediaPlayer is now having trouble calculating the duration properly without the tag. i already have a solution to the porblem in mind, more to come in the next commit. --- JustAudio.pro | 20 +++--- gui/file_item.cpp | 6 -- gui/file_item.h | 17 ----- gui/icon.cpp | 83 ++++++++++++++++++++++ gui/icon.h | 53 ++++++++++++++ gui/ui.cpp | 169 ++++++++++++++++++++++++++++++++++++-------- gui/ui.h | 47 ++++++++---- icon_files.qrc | 8 +++ io/aud_file.cpp | 57 +++++++++++++++ io/aud_file.h | 26 +++++++ svg/eject.svg | 46 ++++++++++++ svg/list.svg | 46 ++++++++++++ svg/pause.svg | 41 +++++++++++ svg/play-button.svg | 40 +++++++++++ 14 files changed, 582 insertions(+), 77 deletions(-) delete mode 100644 gui/file_item.cpp delete mode 100644 gui/file_item.h create mode 100644 gui/icon.cpp create mode 100644 gui/icon.h create mode 100644 icon_files.qrc create mode 100644 io/aud_file.cpp create mode 100644 io/aud_file.h create mode 100644 svg/eject.svg create mode 100644 svg/list.svg create mode 100644 svg/pause.svg create mode 100644 svg/play-button.svg diff --git a/JustAudio.pro b/JustAudio.pro index 4002db3..87d0795 100644 --- a/JustAudio.pro +++ b/JustAudio.pro @@ -4,18 +4,22 @@ # #------------------------------------------------- -QT += core gui +QT += core gui +QT += multimedia +QT += svg greaterThan(QT_MAJOR_VERSION, 4): QT += widgets -TARGET = JustAudio +TARGET = JustAudio TEMPLATE = app - SOURCES += main.cpp\ - gui/ui.cpp \ - gui/file_item.cpp + gui/ui.cpp\ + gui/icon.cpp \ + io/aud_file.cpp -HEADERS += \ - gui/ui.h \ - gui/file_item.h +HEADERS += gui/ui.h\ + gui/icon.h \ + io/aud_file.h + +RESOURCES += icon_files.qrc diff --git a/gui/file_item.cpp b/gui/file_item.cpp deleted file mode 100644 index 4bcfd9f..0000000 --- a/gui/file_item.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "file_item.h" - -FileItem::FileItem(QWidget *parent) : QLabel(parent) -{ - -} diff --git a/gui/file_item.h b/gui/file_item.h deleted file mode 100644 index 130f0bd..0000000 --- a/gui/file_item.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef FILE_ITEM_H -#define FILE_ITEM_H - -#include -#include -#include - -class FileItem : public QLabel -{ - Q_OBJECT - -public: - - FileItem(QWidget *parent = 0); -}; - -#endif // FILE_ITEM_H diff --git a/gui/icon.cpp b/gui/icon.cpp new file mode 100644 index 0000000..4e0b8b2 --- /dev/null +++ b/gui/icon.cpp @@ -0,0 +1,83 @@ +#include "icon.h" + +Icon::Icon(IconType t, QWidget *parent) : QLabel(parent) +{ + QRect rect = QApplication::desktop()->screenGeometry(); + + setMinimumHeight(rect.height() / 40); + setMinimumWidth(rect.height() / 40); + setCursor(Qt::PointingHandCursor); + + switch (t) + { + case PAUSE_PLAY: stateChanged(QMediaPlayer::StoppedState); break; + case SETTINGS: loadImg(":/settings"); break; + case OPEN: loadImg(":/open"); break; + } + + type = t; +} + +void Icon::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + { + switch (type) + { + case PAUSE_PLAY: + { + if (playerState == QMediaPlayer::PlayingState) + { + emit pause(); + } + else + { + emit play(); + } + + break; + } + case SETTINGS: + { + emit settings(); + + break; + } + case OPEN: + { + emit open(); + + break; + } + } + } +} + +void Icon::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + QSvgRenderer svg(svgFile, this); + + svg.render(&painter); +} + +void Icon::stateChanged(QMediaPlayer::State state) +{ + playerState = state; + + if (state == QMediaPlayer::PlayingState) + { + loadImg(":/pause"); + } + else + { + loadImg(":/play"); + } +} + +void Icon::loadImg(const QString &path) +{ + svgFile = path; + + update(); +} diff --git a/gui/icon.h b/gui/icon.h new file mode 100644 index 0000000..0c47c57 --- /dev/null +++ b/gui/icon.h @@ -0,0 +1,53 @@ +#ifndef ICON_H +#define ICON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Icon : public QLabel +{ + Q_OBJECT + +public: + + enum IconType + { + PAUSE_PLAY, + SETTINGS, + OPEN + }; + + Icon(IconType t, QWidget *parent = 0); + +public slots: + + void stateChanged(QMediaPlayer::State state); + +private: + + IconType type; + QMediaPlayer::State playerState; + QString svgFile; + + void mouseReleaseEvent(QMouseEvent *event); + void paintEvent(QPaintEvent *); + void loadImg(const QString &path); + +signals: + + void pause(); + void play(); + void settings(); + void open(); +}; + +#endif // ICON_H diff --git a/gui/ui.cpp b/gui/ui.cpp index 53c9ce8..8731e5f 100644 --- a/gui/ui.cpp +++ b/gui/ui.cpp @@ -2,53 +2,160 @@ Ui::Ui(const QStringList &args, QWidget *parent) : QWidget(parent) { - QWidget *listWid = new QWidget(this); - QScrollArea *mid = new QScrollArea(this); + QWidget *btnWid = new QWidget(this); + QHBoxLayout *btnLayout = new QHBoxLayout(btnWid); QVBoxLayout *mainLayout = new QVBoxLayout(this); - slider = new QSlider(this); - listLayout = new QVBoxLayout(listWid); - search = new QLineEdit(this); + fileName = new QLabel(this); + slider = new QSlider(this); + pausePlay = new Icon(Icon::PAUSE_PLAY, this); + settings = new Icon(Icon::SETTINGS, this); + open = new Icon(Icon::OPEN, this); + player = new QMediaPlayer(this); + ioDev = new AudFile(this); + pressed = false; + QFont fnt = fileName->font(); + QRect rect = QApplication::desktop()->screenGeometry(); + + fnt.setBold(true); + + setMinimumHeight(rect.height() / 6); + setMinimumWidth(rect.width() / 5); + setMaximumHeight(rect.height() / 6); + setMaximumWidth(rect.width() / 5); + setStyleSheet("background-color:white;"); + + fileName->setText(QApplication::applicationName()); + fileName->setFont(fnt); slider->setOrientation(Qt::Horizontal); - mid->setWidget(listWid); - mid->setWidgetResizable(true); - mainLayout->addWidget(search); - mainLayout->addWidget(mid); - mainLayout->addWidget(slider); - mainLayout->setSpacing(0); - mainLayout->setContentsMargins(0, 0, 0, 0); + slider->setMinimumWidth((rect.width() / 5) - 100); + btnLayout->addWidget(pausePlay, 0, Qt::AlignCenter); + btnLayout->addSpacing(rect.height() / 40); + btnLayout->addWidget(open, 0, Qt::AlignCenter); + btnLayout->addSpacing(rect.height() / 40); + btnLayout->addWidget(settings, 0, Qt::AlignCenter); + mainLayout->addWidget(fileName, 0, Qt::AlignCenter); + mainLayout->addWidget(slider, 0, Qt::AlignCenter); + mainLayout->addWidget(btnWid, 0, Qt::AlignCenter); + + connect(pausePlay, SIGNAL(pause()), player, SLOT(pause())); + connect(pausePlay, SIGNAL(play()), player, SLOT(play())); + connect(open, SIGNAL(open()), this, SLOT(openDialog())); + connect(player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(error(QMediaPlayer::Error))); + connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), pausePlay, SLOT(stateChanged(QMediaPlayer::State))); + connect(player, SIGNAL(durationChanged(qint64)), this, SLOT(durationChanged(qint64))); + connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(posChanged(qint64))); + connect(slider, SIGNAL(sliderPressed()), this, SLOT(sliderPressed())); + connect(slider, SIGNAL(sliderReleased()), this, SLOT(sliderReleased())); if (args.size() > 1) { - QFileInfo info(args[1]); + play(args[1]); + } +} - info.makeAbsolute(); +void Ui::play(const QString &path) +{ + QFileInfo info(path); - if (info.isFile()) - { - setActiveDir(info.path()); - setActiveFile(info.filePath()); - } - else if (info.isDir()) - { - setActiveDir(info.path()); - } - else - { - setActiveDir(QDir::homePath()); - } + fileDir = info.path(); + + slider->setMinimum(0); + fileName->setText(info.fileName()); + player->stop(); + ioDev->close(); + ioDev->openFile(path); + player->setMedia(0, ioDev); + player->play(); +} + +void Ui::openDialog() +{ + QFileDialog fileDialog(this); + + fileDialog.setFileMode(QFileDialog::ExistingFile); + fileDialog.setNameFilter(tr("Audio Files (*.mp3 *.ogg *.wav *.flac)")); + + if (fileDialog.exec()) + { + play(fileDialog.selectedFiles()[0]); + } +} + +void Ui::error(QMediaPlayer::Error error) +{ + QMessageBox box(this); + + if (error == QMediaPlayer::NoError) + { + box.setText(tr("An unknown error has occured")); } else { - setActiveDir(QDir::homePath()); + box.setText(player->errorString()); } + + if (error == QMediaPlayer::FormatError) + { + box.setIcon(QMessageBox::Warning); + } + else + { + box.setIcon(QMessageBox::Critical); + } + + box.exec(); } -void Ui::setActiveDir(const QString &path) +void Ui::sliderPressed() { - for (int i = 0; i < fileList.size(); ++i) + pressed = true; +} + +void Ui::sliderReleased() +{ + pressed = false; + + player->setPosition(slider->value()); +} + +void Ui::posChanged(qint64 pos) +{ + if (!pressed) slider->setSliderPosition(pos); + +// if (slider->sliderPosition() == slider->maximum()) +// { +// QTimer::singleShot(750, this, SLOT(nextFile())); +// } +} + +void Ui::durationChanged(qint64 len) +{ + slider->setMaximum(len); +} + +void Ui::nextFile() +{ + QDir dir(fileDir); + + QStringList filterList; + + filterList.append("*.mp3"); + filterList.append("*.ogg"); + filterList.append("*.flac"); + filterList.append("*.wav"); + + QStringList list = dir.entryList(filterList, QDir::Files | QDir::Readable, QDir::Name); + int pos = list.indexOf(fileName->text()); + + if (pos != -1) { - listLayout->removeWidget(fileList[i]); + pos++; + + if (pos < list.size()) + { + play(fileDir + list[pos]); + } } } diff --git a/gui/ui.h b/gui/ui.h index 4c5a93a..24534a1 100644 --- a/gui/ui.h +++ b/gui/ui.h @@ -4,17 +4,23 @@ #include #include #include -#include #include #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "file_item.h" +#include "icon.h" +#include "../io/aud_file.h" class Ui : public QWidget { @@ -22,19 +28,30 @@ class Ui : public QWidget private: - QSlider *slider; - QLineEdit *search; - QVBoxLayout *listLayout; - QList fileList; + QLabel *fileName; + QSlider *slider; + QMediaPlayer *player; + AudFile *ioDev; + Icon *pausePlay; + Icon *settings; + Icon *open; + QString fileDir; + bool pressed; + +private slots: + + void error(QMediaPlayer::Error error); + void sliderPressed(); + void sliderReleased(); + void posChanged(qint64 pos); + void durationChanged(qint64 len); + void openDialog(); + void nextFile(); + void play(const QString &path); public: Ui(const QStringList &args, QWidget *parent = 0); - -public slots: - - void setActiveFile(const QString &name); - void setActiveDir(const QString &path); }; #endif // UI_H diff --git a/icon_files.qrc b/icon_files.qrc new file mode 100644 index 0000000..e0580bf --- /dev/null +++ b/icon_files.qrc @@ -0,0 +1,8 @@ + + + svg/play-button.svg + svg/pause.svg + svg/list.svg + svg/eject.svg + + diff --git a/io/aud_file.cpp b/io/aud_file.cpp new file mode 100644 index 0000000..e6d9727 --- /dev/null +++ b/io/aud_file.cpp @@ -0,0 +1,57 @@ +#include "aud_file.h" + +AudFile::AudFile(QObject *parent) : QFile(parent) +{ + offset = 0; +} + +AudFile::~AudFile() +{ + close(); +} + +bool AudFile::openFile(const QString &path) +{ + setFileName(path); + + bool ret = open(QFile::ReadOnly); + + if (ret) + { + if (peek(3) == "ID3") + { + QByteArray header = read(10); + QByteArray intBytes = header.mid(6); + quint64 num = 0; + quint64 bit = 1; + + offset += 10; + + if (header[5] & 16) //Footer flag check + { + offset += 10; + } + + for (int i = intBytes.size() - 1; i >= 0; --i) + { + int byte = intBytes[i]; + + for (int inBit = 1; inBit <= 64; inBit *= 2, bit *= 2) + { + if ((byte & inBit) != 0) num |= bit; + } + } + + offset += num; + + seek(0); + } + } + + return ret; +} + +bool AudFile::seek(qint64 pos) +{ + return QFile::seek(pos + offset); +} diff --git a/io/aud_file.h b/io/aud_file.h new file mode 100644 index 0000000..5e69d53 --- /dev/null +++ b/io/aud_file.h @@ -0,0 +1,26 @@ +#ifndef AUD_FILE_H +#define AUD_FILE_H + +#include +#include +#include + +class AudFile : public QFile +{ + Q_OBJECT + +private: + + qint64 offset; + +public: + + AudFile(QObject *parent = 0); + + bool openFile(const QString &path); + bool seek(qint64 pos); + + ~AudFile(); +}; + +#endif // AUD_FILE_H diff --git a/svg/eject.svg b/svg/eject.svg new file mode 100644 index 0000000..55b047a --- /dev/null +++ b/svg/eject.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/list.svg b/svg/list.svg new file mode 100644 index 0000000..f35883c --- /dev/null +++ b/svg/list.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/pause.svg b/svg/pause.svg new file mode 100644 index 0000000..fad0570 --- /dev/null +++ b/svg/pause.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/play-button.svg b/svg/play-button.svg new file mode 100644 index 0000000..32fad65 --- /dev/null +++ b/svg/play-button.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +