diff --git a/mainwindow.cpp b/mainwindow.cpp index 9393c0b..c572ac9 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,539 +1,540 @@ -#include "mainwindow.h" -#include "./ui_mainwindow.h" - -#include "playlistmodel.h" -#include "qt/qmediaplaylist.h" - -#include "ID3v2Pic.h" -#include "FlacPic.h" - -// taglib -#ifndef NO_TAGLIB -#include -#endif // NO_TAGLIB - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) - , m_mediaPlayer(new QMediaPlayer(this)) - , m_audioOutput(new QAudioOutput(this)) - , m_playlistModel(new PlaylistModel(this)) -{ - ui->setupUi(this); - - m_mediaPlayer->setAudioOutput(m_audioOutput); - - this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint); - this->setAttribute(Qt::WA_TranslucentBackground, true); - - initConnections(); - initUiAndAnimation(); - - centerWindow(); -} - -MainWindow::~MainWindow() -{ - delete ui; -} - -void MainWindow::commandlinePlayAudioFiles(QStringList audioFiles) -{ - QList audioFileUrls = strlst2urllst(audioFiles); - - if (!audioFileUrls.isEmpty()) { - if (audioFileUrls.count() == 1) { - loadPlaylistBySingleLocalFile(audioFileUrls.first().toLocalFile()); - } else { - createPlaylist(audioFileUrls); - } - m_mediaPlayer->play(); - } -} - -void MainWindow::loadPlaylistBySingleLocalFile(const QString &path) -{ - QFileInfo info(path); - QDir dir(info.path()); - QString currentFileName = info.fileName(); - QStringList entryList = dir.entryList({"*.mp3", "*.wav", "*.aiff", "*.ape", "*.flac", "*.ogg", "*.oga", "*.mpga"}, - QDir::Files | QDir::NoSymLinks, QDir::NoSort); - - QCollator collator; - collator.setNumericMode(true); - - std::sort(entryList.begin(), entryList.end(), collator); - - QList urlList; - int currentFileIndex = -1; - for (int i = 0; i < entryList.count(); i++) { - const QString & oneEntry = entryList.at(i); - urlList.append(QUrl::fromLocalFile(dir.absoluteFilePath(oneEntry))); - if (oneEntry == currentFileName) { - currentFileIndex = i; - } - } - - if (currentFileIndex == -1) { - // not in the list probably because of the suffix is not a common one, add it to the first one anyway. - urlList.prepend(QUrl::fromLocalFile(path)); - currentFileIndex = 0; - } - - createPlaylist(urlList); -} - -void MainWindow::setAudioPropertyInfoForDisplay(int sampleRate, int bitrate, int channelCount, QString audioExt) -{ - QStringList uiStrs; - QStringList tooltipStrs; - - auto channelStr = [](int channelCnt) { - switch (channelCnt) { - case 1: - return tr("Mono"); - case 2: - return tr("Stereo"); - default: - return tr("%1 Channels").arg(channelCnt); - } - }; - - if (sampleRate >= 0) { - uiStrs << QString("%1 Hz").arg(sampleRate); - tooltipStrs << tr("Sample Rate: %1 Hz").arg(sampleRate); - } - - if (bitrate >= 0) { - uiStrs << QString("%1 Kbps").arg(bitrate); - tooltipStrs << tr("Bitrate: %1 Kbps").arg(bitrate); - } - - if (channelCount >= 0) { - uiStrs << channelStr(channelCount); - tooltipStrs << tr("Channel Count: %1").arg(channelCount); - } - - uiStrs << audioExt; - - ui->propLabel->setText(uiStrs.join(" | ")); - ui->propLabel->setToolTip(tooltipStrs.join('\n')); -} - -void MainWindow::setAudioMetadataForDisplay(QString title, QString artist, QString album) -{ - Q_UNUSED(album); - - if (!title.isEmpty()) { - if (!artist.isEmpty()) { - ui->titleLabel->setText(QString("%1 - %2").arg(artist, title)); - } else if (!album.isEmpty()) { - ui->titleLabel->setText(QString("%1 - %2").arg(album, title)); - } else { - ui->titleLabel->setText(QString("%1").arg(title)); - } - } -} - -void MainWindow::localSocketPlayAudioFiles(QVariant audioFilesVariant) -{ - QStringList urlStrList = audioFilesVariant.toStringList(); - qDebug() << urlStrList << "MainWindow::localSocketPlayAudioFiles"; - commandlinePlayAudioFiles(urlStrList); - - showNormal(); - activateWindow(); - raise(); -} - -void MainWindow::closeEvent(QCloseEvent *) -{ - qApp->exit(); -} - -void MainWindow::paintEvent(QPaintEvent * e) -{ - QPainter painter(this); - - painter.setPen(Qt::NoPen); - - // Temp bg - painter.setBrush(QColor(20, 32, 83)); - painter.drawRect(0, 0, width(), height()); - - painter.setBrush(QBrush(m_bgLinearGradient)); - painter.drawRect(0, 0, width(), height()); - - return QMainWindow::paintEvent(e); -} - -void MainWindow::mousePressEvent(QMouseEvent *event) -{ - if (event->buttons() & Qt::LeftButton && !isMaximized()) { - m_clickedOnWindow = true; - event->accept(); - } - - return QMainWindow::mousePressEvent(event); -} - -void MainWindow::mouseMoveEvent(QMouseEvent *event) -{ - if (event->buttons() & Qt::LeftButton && m_clickedOnWindow) { - window()->windowHandle()->startSystemMove(); - event->accept(); - } - - return QMainWindow::mouseMoveEvent(event); -} - -void MainWindow::mouseReleaseEvent(QMouseEvent *event) -{ - m_clickedOnWindow = false; - - return QMainWindow::mouseReleaseEvent(event); -} - -void MainWindow::dragEnterEvent(QDragEnterEvent *e) -{ - // TODO: file/format filter? - - e->acceptProposedAction(); -} - -void MainWindow::dropEvent(QDropEvent *e) -{ - QList urls = e->mimeData()->urls(); - if (urls.isEmpty()) { - return; - } - - QString fileName = urls.first().toLocalFile(); - if (fileName.isEmpty()) { - return; - } - - // TODO: file/format filter? - - createPlaylist(urls); - m_mediaPlayer->play(); -} - -void MainWindow::loadFile() -{ - QStringList files = QFileDialog::getOpenFileNames(this, - tr("Select songs to play"), - QDir::homePath(), - tr("Audio Files") + " (*.mp3 *.wav *.aiff *.ape *.flac *.ogg *.oga)"); - QList urlList; - for (const QString & fileName : files) { - urlList.append(QUrl::fromLocalFile(fileName)); - } - - createPlaylist(urlList); -} - -/* - * The returned QMediaPlaylist* ownership belongs to the internal QMediaPlayer instance. - */ -void MainWindow::createPlaylist(QList urlList) -{ - QMediaPlaylist* playlist = m_playlistModel->playlist(); - playlist->clear(); - playlist->addMedia(urlList); - - connect(playlist, &QMediaPlaylist::playbackModeChanged, this, [=](QMediaPlaylist::PlaybackMode mode) { - switch (mode) { - case QMediaPlaylist::CurrentItemInLoop: - ui->playbackModeBtn->setIcon(QIcon(":/icons/icons/media-playlist-repeat-song.png")); - break; - case QMediaPlaylist::Loop: - ui->playbackModeBtn->setIcon(QIcon(":/icons/icons/media-playlist-repeat.png")); - break; - case QMediaPlaylist::Sequential: - ui->playbackModeBtn->setIcon(QIcon(":/icons/icons/media-playlist-normal.png")); - break; -// case QMediaPlaylist::Random: -// ui->playbackModeBtn->setIcon(QIcon(":/icons/icons/media-playlist-shuffle.png")); -// break; - default: - break; - } - }); - - playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); - playlist->setCurrentIndex(0); -} - -void MainWindow::centerWindow() -{ - this->setGeometry( - QStyle::alignedRect( - Qt::LeftToRight, - Qt::AlignCenter, - this->size(), - qApp->screenAt(QCursor::pos())->geometry() - ) - ); -} - -void MainWindow::on_closeWindowBtn_clicked() -{ - m_fadeOutAnimation->start(); -} - -void MainWindow::on_playBtn_clicked() -{ - if (m_mediaPlayer->mediaStatus() == QMediaPlayer::NoMedia) { - loadFile(); - m_mediaPlayer->play(); - } else if (m_mediaPlayer->mediaStatus() == QMediaPlayer::InvalidMedia) { - ui->propLabel->setText("Error: InvalidMedia" + m_mediaPlayer->errorString()); - } else { - if (QList {QMediaPlayer::PausedState, QMediaPlayer::StoppedState} - .contains(m_mediaPlayer->playbackState())) { - m_mediaPlayer->play(); - } else { - m_mediaPlayer->pause(); - } - } -} - -QString MainWindow::ms2str(qint64 ms) -{ - QTime duaTime(QTime::fromMSecsSinceStartOfDay(ms)); - if (duaTime.hour() > 0) { - return duaTime.toString("h:mm:ss"); - } else { - return duaTime.toString("m:ss"); - } -} - -QList MainWindow::strlst2urllst(QStringList strlst) -{ - QList urlList; - for (const QString & str : strlst) { - QUrl url = QUrl::fromLocalFile(str); - if (url.isValid()) { - urlList.append(url); - } - } - - return urlList; -} - -void MainWindow::on_volumeSlider_valueChanged(int value) -{ - if (m_audioOutput->isMuted()) { - m_audioOutput->setMuted(false); - } - m_audioOutput->setVolume(value / 100.0); -} - -void MainWindow::on_stopBtn_clicked() -{ - m_mediaPlayer->stop(); -} - -void MainWindow::on_playbackSlider_valueChanged(int value) -{ - qint64 currPos = m_mediaPlayer->duration() == 0 ? value : m_mediaPlayer->position() * ui->playbackSlider->maximum() / m_mediaPlayer->duration(); - if (qAbs(currPos - value) > 2) { - m_mediaPlayer->setPosition(ui->playbackSlider->value() * 1.0 / ui->playbackSlider->maximum() * m_mediaPlayer->duration()); - } -} - -void MainWindow::on_prevBtn_clicked() -{ - // QMediaPlaylist::previous() won't work when in CurrentItemInLoop playmode, - // and also works not as intended when in other playmode, so do it manually... - QMediaPlaylist * playlist = m_playlistModel->playlist(); - if (playlist) { - int index = playlist->currentIndex(); - int count = playlist->mediaCount(); - - playlist->setCurrentIndex(index == 0 ? count - 1 : index - 1); - } -} - -void MainWindow::on_nextBtn_clicked() -{ - // see also: MainWindow::on_prevBtn_clicked() - QMediaPlaylist * playlist = m_playlistModel->playlist(); - if (playlist) { - int index = playlist->currentIndex(); - int count = playlist->mediaCount(); - - playlist->setCurrentIndex(index == (count - 1) ? 0 : index + 1); - } -} - -void MainWindow::on_volumeBtn_clicked() -{ - m_audioOutput->setMuted(!m_audioOutput->isMuted()); -} - -void MainWindow::on_minimumWindowBtn_clicked() -{ - this->showMinimized(); -} - -void MainWindow::initUiAndAnimation() -{ - m_bgLinearGradient.setColorAt(0, QColor(255, 255, 255, 25)); // a:0 - m_bgLinearGradient.setColorAt(1, QColor(255, 255, 255, 75)); // a:200 - m_bgLinearGradient.setStart(0, 0); - m_bgLinearGradient.setFinalStop(0, height()); - - m_fadeOutAnimation = new QPropertyAnimation(this, "windowOpacity", this); - m_fadeOutAnimation->setDuration(400); - m_fadeOutAnimation->setStartValue(1); - m_fadeOutAnimation->setEndValue(0); - connect(m_fadeOutAnimation, &QPropertyAnimation::finished, this, &QMainWindow::close); - - // temp: a playlist for debug... - QListView * tmp_listview = new QListView(ui->pluginWidget); - tmp_listview->setModel(m_playlistModel); - tmp_listview->setGeometry({0,0,490,250}); - this->setGeometry({0,0,490,160}); // temp size, hide the playlist thing. -} - -void MainWindow::initConnections() -{ - connect(m_playlistModel->playlist(), &QMediaPlaylist::currentIndexChanged, this, [=](int currentItem) { - bool isPlaying = m_mediaPlayer->playbackState() == QMediaPlayer::PlayingState; - m_mediaPlayer->setSource(m_playlistModel->playlist()->currentMedia()); - if (isPlaying) m_mediaPlayer->play(); - }); - connect(m_playlistModel->playlist(), &QMediaPlaylist::currentMediaChanged, this, [=](const QUrl &fileUrl) { - ui->titleLabel->setText(fileUrl.fileName()); - ui->titleLabel->setToolTip(fileUrl.fileName()); - - if (fileUrl.isLocalFile()) { - QString filePath(fileUrl.toLocalFile()); - QString suffix(filePath.mid(filePath.lastIndexOf('.') + 1)); - suffix = suffix.toUpper(); - -#ifndef NO_TAGLIB - TagLib::FileRef fileRef(filePath.toLocal8Bit().data()); - - if (!fileRef.isNull() && fileRef.audioProperties()) { - TagLib::AudioProperties *prop = fileRef.audioProperties(); - setAudioPropertyInfoForDisplay(prop->sampleRate(), prop->bitrate(), prop->channels(), suffix); - } - - if (!fileRef.isNull() && fileRef.tag()) { - TagLib::Tag * tag = fileRef.tag(); - setAudioMetadataForDisplay(QString::fromStdString(tag->title().to8Bit(true)), - QString::fromStdString(tag->artist().to8Bit(true)), - QString::fromStdString(tag->album().to8Bit(true))); - } -#endif // NO_TAGLIB - - using namespace spID3; - using namespace spFLAC; - - bool coverLoaded = false; - - if (suffix == "MP3") { - if (spID3::loadPictureData(filePath.toLocal8Bit().data())) { - coverLoaded = true; - QByteArray picData((const char*)spID3::getPictureDataPtr(), spID3::getPictureLength()); - ui->coverLabel->setPixmap(QPixmap::fromImage(QImage::fromData(picData))); - spID3::freePictureData(); - } - } else if (suffix == "FLAC") { - if (spFLAC::loadPictureData(filePath.toLocal8Bit().data())) { - coverLoaded = true; - QByteArray picData((const char*)spFLAC::getPictureDataPtr(), spFLAC::getPictureLength()); - ui->coverLabel->setPixmap(QPixmap::fromImage(QImage::fromData(picData))); - spFLAC::freePictureData(); - } - } - - if (!coverLoaded) { - ui->coverLabel->setPixmap(QPixmap(":/icons/icons/media-album-cover.svg")); - } - } - }); - - connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, [=](qint64 pos) { - ui->nowTimeLabel->setText(ms2str(pos)); - if (m_mediaPlayer->duration() != 0) { - ui->playbackSlider->setSliderPosition(ui->playbackSlider->maximum() * pos / m_mediaPlayer->duration()); - } - }); - - connect(m_audioOutput, &QAudioOutput::mutedChanged, this, [=](bool muted) { - if (muted) { - ui->volumeBtn->setIcon(QIcon(":/icons/icons/audio-volume-muted.png")); - } else { - ui->volumeBtn->setIcon(QIcon(":/icons/icons/audio-volume-high.png")); - } - }); - - connect(m_mediaPlayer, &QMediaPlayer::durationChanged, this, [=](qint64 dua) { - ui->totalTimeLabel->setText(ms2str(dua)); - }); - - connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged, this, [=](QMediaPlayer::PlaybackState newState) { - switch (newState) { - case QMediaPlayer::PlayingState: - ui->playBtn->setIcon(QIcon(":/icons/icons/media-playback-pause.png")); - break; - case QMediaPlayer::StoppedState: - case QMediaPlayer::PausedState: - ui->playBtn->setIcon(QIcon(":/icons/icons/media-playback-start.png")); - break; - } - }); - - connect(m_audioOutput, &QAudioOutput::volumeChanged, this, [=](float vol) { - ui->volumeSlider->setValue(vol * 100); - }); - -// connect(m_mediaPlayer, static_cast(&QMediaPlayer::error), -// this, [=](QMediaPlayer::Error error) { -// switch (error) { -// default: -// break; -// } -// qDebug("%s aaaaaaaaaaaaa", m_mediaPlayer->errorString().toUtf8().data()); -// }); -} - -void MainWindow::on_playbackModeBtn_clicked() -{ - QMediaPlaylist * playlist = m_playlistModel->playlist(); - if (!playlist) return; - - switch (playlist->playbackMode()) { - case QMediaPlaylist::CurrentItemInLoop: - playlist->setPlaybackMode(QMediaPlaylist::Loop); - break; - case QMediaPlaylist::Loop: - playlist->setPlaybackMode(QMediaPlaylist::Sequential); - break; - case QMediaPlaylist::Sequential: - playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); - break; -// case QMediaPlaylist::Random: -// playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); -// break; - default: - break; - } -} +#include "mainwindow.h" +#include "./ui_mainwindow.h" + +#include "playlistmodel.h" +#include "qt/qmediaplaylist.h" + +#include "ID3v2Pic.h" +#include "FlacPic.h" + +// taglib +#ifndef NO_TAGLIB +#include +#endif // NO_TAGLIB + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , m_mediaPlayer(new QMediaPlayer(this)) + , m_audioOutput(new QAudioOutput(this)) + , m_playlistModel(new PlaylistModel(this)) +{ + ui->setupUi(this); + + m_mediaPlayer->setAudioOutput(m_audioOutput); + + this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint); + this->setAttribute(Qt::WA_TranslucentBackground, true); + + initConnections(); + initUiAndAnimation(); + + centerWindow(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::commandlinePlayAudioFiles(QStringList audioFiles) +{ + QList audioFileUrls = strlst2urllst(audioFiles); + + if (!audioFileUrls.isEmpty()) { + if (audioFileUrls.count() == 1) { + loadPlaylistBySingleLocalFile(audioFileUrls.first().toLocalFile()); + } else { + createPlaylist(audioFileUrls); + } + m_mediaPlayer->play(); + } +} + +void MainWindow::loadPlaylistBySingleLocalFile(const QString &path) +{ + QFileInfo info(path); + QDir dir(info.path()); + QString currentFileName = info.fileName(); + QStringList entryList = dir.entryList({"*.mp3", "*.wav", "*.aiff", "*.ape", "*.flac", "*.ogg", "*.oga", "*.mpga"}, + QDir::Files | QDir::NoSymLinks, QDir::NoSort); + + QCollator collator; + collator.setNumericMode(true); + + std::sort(entryList.begin(), entryList.end(), collator); + + QList urlList; + int currentFileIndex = -1; + for (int i = 0; i < entryList.count(); i++) { + const QString & oneEntry = entryList.at(i); + urlList.append(QUrl::fromLocalFile(dir.absoluteFilePath(oneEntry))); + if (oneEntry == currentFileName) { + currentFileIndex = i; + } + } + + if (currentFileIndex == -1) { + // not in the list probably because of the suffix is not a common one, add it to the first one anyway. + urlList.prepend(QUrl::fromLocalFile(path)); + currentFileIndex = 0; + } + + createPlaylist(urlList, currentFileIndex); +} + +void MainWindow::setAudioPropertyInfoForDisplay(int sampleRate, int bitrate, int channelCount, QString audioExt) +{ + QStringList uiStrs; + QStringList tooltipStrs; + + auto channelStr = [](int channelCnt) { + switch (channelCnt) { + case 1: + return tr("Mono"); + case 2: + return tr("Stereo"); + default: + return tr("%1 Channels").arg(channelCnt); + } + }; + + if (sampleRate >= 0) { + uiStrs << QString("%1 Hz").arg(sampleRate); + tooltipStrs << tr("Sample Rate: %1 Hz").arg(sampleRate); + } + + if (bitrate >= 0) { + uiStrs << QString("%1 Kbps").arg(bitrate); + tooltipStrs << tr("Bitrate: %1 Kbps").arg(bitrate); + } + + if (channelCount >= 0) { + uiStrs << channelStr(channelCount); + tooltipStrs << tr("Channel Count: %1").arg(channelCount); + } + + uiStrs << audioExt; + + ui->propLabel->setText(uiStrs.join(" | ")); + ui->propLabel->setToolTip(tooltipStrs.join('\n')); +} + +void MainWindow::setAudioMetadataForDisplay(QString title, QString artist, QString album) +{ + Q_UNUSED(album); + + if (!title.isEmpty()) { + if (!artist.isEmpty()) { + ui->titleLabel->setText(QString("%1 - %2").arg(artist, title)); + } else if (!album.isEmpty()) { + ui->titleLabel->setText(QString("%1 - %2").arg(album, title)); + } else { + ui->titleLabel->setText(QString("%1").arg(title)); + } + } +} + +void MainWindow::localSocketPlayAudioFiles(QVariant audioFilesVariant) +{ + QStringList urlStrList = audioFilesVariant.toStringList(); + qDebug() << urlStrList << "MainWindow::localSocketPlayAudioFiles"; + commandlinePlayAudioFiles(urlStrList); + + showNormal(); + activateWindow(); + raise(); +} + +void MainWindow::closeEvent(QCloseEvent *) +{ + qApp->exit(); +} + +void MainWindow::paintEvent(QPaintEvent * e) +{ + QPainter painter(this); + + painter.setPen(Qt::NoPen); + + // Temp bg + painter.setBrush(QColor(20, 32, 83)); + painter.drawRect(0, 0, width(), height()); + + painter.setBrush(QBrush(m_bgLinearGradient)); + painter.drawRect(0, 0, width(), height()); + + return QMainWindow::paintEvent(e); +} + +void MainWindow::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton && !isMaximized()) { + m_clickedOnWindow = true; + event->accept(); + } + + return QMainWindow::mousePressEvent(event); +} + +void MainWindow::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton && m_clickedOnWindow) { + qDebug() << "??" << event << event->flags() << event->isBeginEvent() << event->isEndEvent(); + window()->windowHandle()->startSystemMove(); + event->accept(); + } + + return QMainWindow::mouseMoveEvent(event); +} + +void MainWindow::mouseReleaseEvent(QMouseEvent *event) +{ + m_clickedOnWindow = false; + qDebug() << "?"; + return QMainWindow::mouseReleaseEvent(event); +} + +void MainWindow::dragEnterEvent(QDragEnterEvent *e) +{ + // TODO: file/format filter? + + e->acceptProposedAction(); +} + +void MainWindow::dropEvent(QDropEvent *e) +{ + QList urls = e->mimeData()->urls(); + if (urls.isEmpty()) { + return; + } + + QString fileName = urls.first().toLocalFile(); + if (fileName.isEmpty()) { + return; + } + + // TODO: file/format filter? + + createPlaylist(urls); + m_mediaPlayer->play(); +} + +void MainWindow::loadFile() +{ + QStringList files = QFileDialog::getOpenFileNames(this, + tr("Select songs to play"), + QDir::homePath(), + tr("Audio Files") + " (*.mp3 *.wav *.aiff *.ape *.flac *.ogg *.oga)"); + QList urlList; + for (const QString & fileName : files) { + urlList.append(QUrl::fromLocalFile(fileName)); + } + + createPlaylist(urlList); +} + +/* + * The returned QMediaPlaylist* ownership belongs to the internal QMediaPlayer instance. + */ +void MainWindow::createPlaylist(QList urlList, int index) +{ + QMediaPlaylist* playlist = m_playlistModel->playlist(); + playlist->clear(); + playlist->addMedia(urlList); + + connect(playlist, &QMediaPlaylist::playbackModeChanged, this, [=](QMediaPlaylist::PlaybackMode mode) { + switch (mode) { + case QMediaPlaylist::CurrentItemInLoop: + ui->playbackModeBtn->setIcon(QIcon(":/icons/icons/media-playlist-repeat-song.png")); + break; + case QMediaPlaylist::Loop: + ui->playbackModeBtn->setIcon(QIcon(":/icons/icons/media-playlist-repeat.png")); + break; + case QMediaPlaylist::Sequential: + ui->playbackModeBtn->setIcon(QIcon(":/icons/icons/media-playlist-normal.png")); + break; +// case QMediaPlaylist::Random: +// ui->playbackModeBtn->setIcon(QIcon(":/icons/icons/media-playlist-shuffle.png")); +// break; + default: + break; + } + }); + + playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); + playlist->setCurrentIndex(index < 0 ? 0 : index); +} + +void MainWindow::centerWindow() +{ + this->setGeometry( + QStyle::alignedRect( + Qt::LeftToRight, + Qt::AlignCenter, + this->size(), + qApp->screenAt(QCursor::pos())->geometry() + ) + ); +} + +void MainWindow::on_closeWindowBtn_clicked() +{ + m_fadeOutAnimation->start(); +} + +void MainWindow::on_playBtn_clicked() +{ + if (m_mediaPlayer->mediaStatus() == QMediaPlayer::NoMedia) { + loadFile(); + m_mediaPlayer->play(); + } else if (m_mediaPlayer->mediaStatus() == QMediaPlayer::InvalidMedia) { + ui->propLabel->setText("Error: InvalidMedia" + m_mediaPlayer->errorString()); + } else { + if (QList {QMediaPlayer::PausedState, QMediaPlayer::StoppedState} + .contains(m_mediaPlayer->playbackState())) { + m_mediaPlayer->play(); + } else { + m_mediaPlayer->pause(); + } + } +} + +QString MainWindow::ms2str(qint64 ms) +{ + QTime duaTime(QTime::fromMSecsSinceStartOfDay(ms)); + if (duaTime.hour() > 0) { + return duaTime.toString("h:mm:ss"); + } else { + return duaTime.toString("m:ss"); + } +} + +QList MainWindow::strlst2urllst(QStringList strlst) +{ + QList urlList; + for (const QString & str : strlst) { + QUrl url = QUrl::fromLocalFile(str); + if (url.isValid()) { + urlList.append(url); + } + } + + return urlList; +} + +void MainWindow::on_volumeSlider_valueChanged(int value) +{ + if (m_audioOutput->isMuted()) { + m_audioOutput->setMuted(false); + } + m_audioOutput->setVolume(value / 100.0); +} + +void MainWindow::on_stopBtn_clicked() +{ + m_mediaPlayer->stop(); +} + +void MainWindow::on_playbackSlider_valueChanged(int value) +{ + qint64 currPos = m_mediaPlayer->duration() == 0 ? value : m_mediaPlayer->position() * ui->playbackSlider->maximum() / m_mediaPlayer->duration(); + if (qAbs(currPos - value) > 2) { + m_mediaPlayer->setPosition(ui->playbackSlider->value() * 1.0 / ui->playbackSlider->maximum() * m_mediaPlayer->duration()); + } +} + +void MainWindow::on_prevBtn_clicked() +{ + // QMediaPlaylist::previous() won't work when in CurrentItemInLoop playmode, + // and also works not as intended when in other playmode, so do it manually... + QMediaPlaylist * playlist = m_playlistModel->playlist(); + if (playlist) { + int index = playlist->currentIndex(); + int count = playlist->mediaCount(); + + playlist->setCurrentIndex(index == 0 ? count - 1 : index - 1); + } +} + +void MainWindow::on_nextBtn_clicked() +{ + // see also: MainWindow::on_prevBtn_clicked() + QMediaPlaylist * playlist = m_playlistModel->playlist(); + if (playlist) { + int index = playlist->currentIndex(); + int count = playlist->mediaCount(); + + playlist->setCurrentIndex(index == (count - 1) ? 0 : index + 1); + } +} + +void MainWindow::on_volumeBtn_clicked() +{ + m_audioOutput->setMuted(!m_audioOutput->isMuted()); +} + +void MainWindow::on_minimumWindowBtn_clicked() +{ + this->showMinimized(); +} + +void MainWindow::initUiAndAnimation() +{ + m_bgLinearGradient.setColorAt(0, QColor(255, 255, 255, 25)); // a:0 + m_bgLinearGradient.setColorAt(1, QColor(255, 255, 255, 75)); // a:200 + m_bgLinearGradient.setStart(0, 0); + m_bgLinearGradient.setFinalStop(0, height()); + + m_fadeOutAnimation = new QPropertyAnimation(this, "windowOpacity", this); + m_fadeOutAnimation->setDuration(400); + m_fadeOutAnimation->setStartValue(1); + m_fadeOutAnimation->setEndValue(0); + connect(m_fadeOutAnimation, &QPropertyAnimation::finished, this, &QMainWindow::close); + + // temp: a playlist for debug... + QListView * tmp_listview = new QListView(ui->pluginWidget); + tmp_listview->setModel(m_playlistModel); + tmp_listview->setGeometry({0,0,490,250}); + this->setGeometry({0,0,490,160}); // temp size, hide the playlist thing. +} + +void MainWindow::initConnections() +{ + connect(m_playlistModel->playlist(), &QMediaPlaylist::currentIndexChanged, this, [=](int currentItem) { + bool isPlaying = m_mediaPlayer->playbackState() == QMediaPlayer::PlayingState; + m_mediaPlayer->setSource(m_playlistModel->playlist()->currentMedia()); + if (isPlaying) m_mediaPlayer->play(); + }); + connect(m_playlistModel->playlist(), &QMediaPlaylist::currentMediaChanged, this, [=](const QUrl &fileUrl) { + ui->titleLabel->setText(fileUrl.fileName()); + ui->titleLabel->setToolTip(fileUrl.fileName()); + + if (fileUrl.isLocalFile()) { + QString filePath(fileUrl.toLocalFile()); + QString suffix(filePath.mid(filePath.lastIndexOf('.') + 1)); + suffix = suffix.toUpper(); + +#ifndef NO_TAGLIB + TagLib::FileRef fileRef(filePath.toLocal8Bit().data()); + + if (!fileRef.isNull() && fileRef.audioProperties()) { + TagLib::AudioProperties *prop = fileRef.audioProperties(); + setAudioPropertyInfoForDisplay(prop->sampleRate(), prop->bitrate(), prop->channels(), suffix); + } + + if (!fileRef.isNull() && fileRef.tag()) { + TagLib::Tag * tag = fileRef.tag(); + setAudioMetadataForDisplay(QString::fromStdString(tag->title().to8Bit(true)), + QString::fromStdString(tag->artist().to8Bit(true)), + QString::fromStdString(tag->album().to8Bit(true))); + } +#endif // NO_TAGLIB + + using namespace spID3; + using namespace spFLAC; + + bool coverLoaded = false; + + if (suffix == "MP3") { + if (spID3::loadPictureData(filePath.toLocal8Bit().data())) { + coverLoaded = true; + QByteArray picData((const char*)spID3::getPictureDataPtr(), spID3::getPictureLength()); + ui->coverLabel->setPixmap(QPixmap::fromImage(QImage::fromData(picData))); + spID3::freePictureData(); + } + } else if (suffix == "FLAC") { + if (spFLAC::loadPictureData(filePath.toLocal8Bit().data())) { + coverLoaded = true; + QByteArray picData((const char*)spFLAC::getPictureDataPtr(), spFLAC::getPictureLength()); + ui->coverLabel->setPixmap(QPixmap::fromImage(QImage::fromData(picData))); + spFLAC::freePictureData(); + } + } + + if (!coverLoaded) { + ui->coverLabel->setPixmap(QPixmap(":/icons/icons/media-album-cover.svg")); + } + } + }); + + connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, [=](qint64 pos) { + ui->nowTimeLabel->setText(ms2str(pos)); + if (m_mediaPlayer->duration() != 0) { + ui->playbackSlider->setSliderPosition(ui->playbackSlider->maximum() * pos / m_mediaPlayer->duration()); + } + }); + + connect(m_audioOutput, &QAudioOutput::mutedChanged, this, [=](bool muted) { + if (muted) { + ui->volumeBtn->setIcon(QIcon(":/icons/icons/audio-volume-muted.png")); + } else { + ui->volumeBtn->setIcon(QIcon(":/icons/icons/audio-volume-high.png")); + } + }); + + connect(m_mediaPlayer, &QMediaPlayer::durationChanged, this, [=](qint64 dua) { + ui->totalTimeLabel->setText(ms2str(dua)); + }); + + connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged, this, [=](QMediaPlayer::PlaybackState newState) { + switch (newState) { + case QMediaPlayer::PlayingState: + ui->playBtn->setIcon(QIcon(":/icons/icons/media-playback-pause.png")); + break; + case QMediaPlayer::StoppedState: + case QMediaPlayer::PausedState: + ui->playBtn->setIcon(QIcon(":/icons/icons/media-playback-start.png")); + break; + } + }); + + connect(m_audioOutput, &QAudioOutput::volumeChanged, this, [=](float vol) { + ui->volumeSlider->setValue(vol * 100); + }); + +// connect(m_mediaPlayer, static_cast(&QMediaPlayer::error), +// this, [=](QMediaPlayer::Error error) { +// switch (error) { +// default: +// break; +// } +// qDebug("%s aaaaaaaaaaaaa", m_mediaPlayer->errorString().toUtf8().data()); +// }); +} + +void MainWindow::on_playbackModeBtn_clicked() +{ + QMediaPlaylist * playlist = m_playlistModel->playlist(); + if (!playlist) return; + + switch (playlist->playbackMode()) { + case QMediaPlaylist::CurrentItemInLoop: + playlist->setPlaybackMode(QMediaPlaylist::Loop); + break; + case QMediaPlaylist::Loop: + playlist->setPlaybackMode(QMediaPlaylist::Sequential); + break; + case QMediaPlaylist::Sequential: + playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); + break; +// case QMediaPlaylist::Random: +// playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); +// break; + default: + break; + } +} diff --git a/mainwindow.h b/mainwindow.h index 04e9d97..ce83ad6 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -1,75 +1,75 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class MainWindow; } - -class QMediaPlayer; -class QAudioOutput; -class QPropertyAnimation; -QT_END_NAMESPACE - -class PlaylistModel; -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow() override; - - void commandlinePlayAudioFiles(QStringList audioFiles); - void loadPlaylistBySingleLocalFile(const QString &path); - void setAudioPropertyInfoForDisplay(int sampleRate, int bitrate, int channelCount, QString audioExt); - void setAudioMetadataForDisplay(QString title, QString artist, QString album); - -public slots: - void localSocketPlayAudioFiles(QVariant audioFilesVariant); - -protected: - void closeEvent(QCloseEvent *) override; - void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void dragEnterEvent(QDragEnterEvent *e) override; - void dropEvent(QDropEvent *e) override; - - void loadFile(); - void centerWindow(); - void createPlaylist(QList urlList); - -private slots: - void on_playbackModeBtn_clicked(); - void on_closeWindowBtn_clicked(); - void on_playBtn_clicked(); - void on_volumeSlider_valueChanged(int value); - void on_stopBtn_clicked(); - void on_playbackSlider_valueChanged(int value); - void on_prevBtn_clicked(); - void on_nextBtn_clicked(); - void on_volumeBtn_clicked(); - void on_minimumWindowBtn_clicked(); - -private: - bool m_clickedOnWindow = false; - bool m_playbackSliderPressed = false; - QLinearGradient m_bgLinearGradient; - - Ui::MainWindow *ui; - - QMediaPlayer *m_mediaPlayer; - QAudioOutput *m_audioOutput; - QPropertyAnimation *m_fadeOutAnimation; - PlaylistModel *m_playlistModel = nullptr; // TODO: move playback logic to player.cpp - - void initUiAndAnimation(); - void initConnections(); - - static QString ms2str(qint64 ms); - static QList strlst2urllst(QStringList strlst); -}; -#endif // MAINWINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } + +class QMediaPlayer; +class QAudioOutput; +class QPropertyAnimation; +QT_END_NAMESPACE + +class PlaylistModel; +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow() override; + + void commandlinePlayAudioFiles(QStringList audioFiles); + void loadPlaylistBySingleLocalFile(const QString &path); + void setAudioPropertyInfoForDisplay(int sampleRate, int bitrate, int channelCount, QString audioExt); + void setAudioMetadataForDisplay(QString title, QString artist, QString album); + +public slots: + void localSocketPlayAudioFiles(QVariant audioFilesVariant); + +protected: + void closeEvent(QCloseEvent *) override; + void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void dragEnterEvent(QDragEnterEvent *e) override; + void dropEvent(QDropEvent *e) override; + + void loadFile(); + void centerWindow(); + void createPlaylist(QList urlList, int index = -1); + +private slots: + void on_playbackModeBtn_clicked(); + void on_closeWindowBtn_clicked(); + void on_playBtn_clicked(); + void on_volumeSlider_valueChanged(int value); + void on_stopBtn_clicked(); + void on_playbackSlider_valueChanged(int value); + void on_prevBtn_clicked(); + void on_nextBtn_clicked(); + void on_volumeBtn_clicked(); + void on_minimumWindowBtn_clicked(); + +private: + bool m_clickedOnWindow = false; + bool m_playbackSliderPressed = false; + QLinearGradient m_bgLinearGradient; + + Ui::MainWindow *ui; + + QMediaPlayer *m_mediaPlayer; + QAudioOutput *m_audioOutput; + QPropertyAnimation *m_fadeOutAnimation; + PlaylistModel *m_playlistModel = nullptr; // TODO: move playback logic to player.cpp + + void initUiAndAnimation(); + void initConnections(); + + static QString ms2str(qint64 ms); + static QList strlst2urllst(QStringList strlst); +}; +#endif // MAINWINDOW_H