File indexing completed on 2025-03-09 03:52:06

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2008-09-14
0007  * Description : a presentation tool.
0008  *
0009  * SPDX-FileCopyrightText: 2008-2009 by Valerio Fuoglio <valerio dot fuoglio at gmail dot com>
0010  * SPDX-FileCopyrightText: 2009      by Andi Clemens <andi dot clemens at googlemail dot com>
0011  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "presentationaudiowidget.h"
0018 
0019 // Qt includes
0020 
0021 #include <QTime>
0022 #include <QUrl>
0023 #include <QIcon>
0024 
0025 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0026 
0027 #   include <QAudioOutput>
0028 
0029 #endif
0030 
0031 // Local includes
0032 
0033 #include "presentationcontainer.h"
0034 #include "digikam_debug.h"
0035 
0036 using namespace Digikam;
0037 
0038 namespace DigikamGenericPresentationPlugin
0039 {
0040 
0041 class Q_DECL_HIDDEN PresentationAudioWidget::Private
0042 {
0043 
0044 public:
0045 
0046     Private() = default;
0047 
0048     PresentationContainer* sharedData  = nullptr;
0049     QList<QUrl>            urlList;
0050     int                    currIndex   = 0;
0051     bool                   canHide     = true;
0052     bool                   isZeroTime  = false;
0053     bool                   playingNext = false;
0054 
0055 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0056 
0057     QMediaPlayer*          mediaObject = nullptr;
0058 
0059 #else
0060 
0061     DAudioPlayer*          mediaObject = nullptr;
0062 
0063 #endif
0064 
0065 };
0066 
0067 PresentationAudioWidget::PresentationAudioWidget(QWidget* const parent, const QList<QUrl>& urls, PresentationContainer* const sharedData)
0068     : QWidget(parent),
0069       d      (new Private)
0070 {
0071     setupUi(this);
0072 
0073     d->sharedData = sharedData;
0074 
0075     m_soundLabel->setPixmap(QIcon::fromTheme(QLatin1String("speaker")).pixmap(64, 64));
0076 
0077     m_prevButton->setText(QString());
0078     m_nextButton->setText(QString());
0079     m_playButton->setText(QString());
0080     m_stopButton->setText(QString());
0081 
0082     m_prevButton->setIcon(QIcon::fromTheme(QLatin1String("media-skip-backward")));
0083     m_nextButton->setIcon(QIcon::fromTheme(QLatin1String("media-skip-forward")));
0084     m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-start")));
0085     m_stopButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-stop")));
0086 
0087     connect(m_prevButton, SIGNAL(clicked()),
0088             this, SLOT(slotPrev()));
0089 
0090     connect(m_nextButton, SIGNAL(clicked()),
0091             this, SLOT(slotNext()));
0092 
0093     connect(m_playButton, SIGNAL(clicked()),
0094             this, SLOT(slotPlay()));
0095 
0096     connect(m_stopButton, SIGNAL(clicked()),
0097             this, SLOT(slotStop()));
0098 
0099     if (urls.isEmpty())
0100     {
0101         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Tracks list is empty...";
0102         setEnabled(false);
0103         return;
0104     }
0105 
0106     // Waiting for files to be enqueued.
0107 
0108     m_playButton->setEnabled(false);
0109     m_prevButton->setEnabled(false);
0110 
0111 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0112 
0113     d->mediaObject = new QMediaPlayer(this);
0114     d->mediaObject->setAudioOutput(new QAudioOutput);
0115 
0116     connect(d->mediaObject, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
0117             this, SLOT(slotMediaStateChanged(QMediaPlayer::MediaStatus)));
0118 
0119     connect(d->mediaObject, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)),
0120             this, SLOT(slotPlayerStateChanged(QMediaPlayer::PlaybackState)));
0121 
0122     connect(d->mediaObject, SIGNAL(errorOccurred(QMediaPlayer::Error,QString)),
0123             this, SLOT(slotPlayerError(QMediaPlayer::Error)));
0124 
0125     connect(d->mediaObject, SIGNAL(positionChanged(qint64)),
0126             this, SLOT(slotTimeUpdaterTimeout(qint64)));
0127 
0128 #else
0129 
0130     d->mediaObject = new DAudioPlayer(this);
0131 
0132     connect(d->mediaObject->player(), SIGNAL(mediaStatusChanged(QAVPlayer::MediaStatus)),
0133             this, SLOT(slotMediaStateChanged(QAVPlayer::MediaStatus)));
0134 
0135     connect(d->mediaObject->player(), SIGNAL(stateChanged(QAVPlayer::State)),
0136             this, SLOT(slotPlayerStateChanged(QAVPlayer::State)));
0137 
0138     connect(d->mediaObject->player(), SIGNAL(errorOccurred(QAVPlayer::Error,QString)),
0139             this, SLOT(slotPlayerError(QAVPlayer::Error,QString)));
0140 
0141     connect(d->mediaObject, SIGNAL(positionChanged(qint64)),
0142             this, SLOT(slotTimeUpdaterTimeout(qint64)));
0143 
0144 #endif
0145 
0146     connect(m_volumeWidget, SIGNAL(valueChanged(int)),
0147             this, SLOT(slotSetVolume(int)));
0148 
0149     enqueue(urls);
0150 
0151     setZeroTime();
0152 }
0153 
0154 PresentationAudioWidget::~PresentationAudioWidget()
0155 {
0156     if (!d->urlList.isEmpty())
0157     {
0158         d->mediaObject->stop();
0159     }
0160 
0161     delete d;
0162 }
0163 
0164 void PresentationAudioWidget::slotSetVolume(int v)
0165 {
0166     if (d->mediaObject->audioOutput())
0167     {
0168 
0169 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0170 
0171         d->mediaObject->audioOutput()->setVolume(v / 100.0);
0172 
0173 #else
0174 
0175         d->mediaObject->setVolume((qreal)v / 100.0);
0176 
0177 #endif
0178 
0179     }
0180 }
0181 
0182 bool PresentationAudioWidget::canHide() const
0183 {
0184     return d->canHide;
0185 }
0186 
0187 bool PresentationAudioWidget::isPaused() const
0188 {
0189 
0190 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0191 
0192     return (d->mediaObject->playbackState() == QMediaPlayer::PausedState);
0193 
0194 #else
0195 
0196     return (d->mediaObject->state() == QAVPlayer::PausedState);
0197 
0198 #endif
0199 
0200 }
0201 
0202 void PresentationAudioWidget::checkSkip()
0203 {
0204     m_prevButton->setEnabled(true);
0205     m_nextButton->setEnabled(true);
0206 
0207     if (!d->sharedData->soundtrackLoop)
0208     {
0209         if (d->currIndex == 0)
0210         {
0211             m_prevButton->setEnabled(false);
0212         }
0213 
0214         if (d->currIndex == d->urlList.count() - 1)
0215         {
0216             m_nextButton->setEnabled(false);
0217         }
0218     }
0219 }
0220 
0221 void PresentationAudioWidget::setZeroTime()
0222 {
0223     QTime zeroTime(0, 0, 0);
0224     m_elapsedTimeLabel->setText(zeroTime.toString(QLatin1String("H:mm:ss")));
0225     m_totalTimeLabel->setText(zeroTime.toString(QLatin1String("H:mm:ss")));
0226     d->isZeroTime = true;
0227 }
0228 
0229 void PresentationAudioWidget::enqueue(const QList<QUrl>& urls)
0230 {
0231     d->urlList   = urls;
0232     d->currIndex = 0;
0233 
0234     qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Tracks : " << d->urlList;
0235 
0236     if (d->urlList.isEmpty())
0237     {
0238         return;
0239     }
0240 
0241     m_playButton->setEnabled(true);
0242 }
0243 
0244 void PresentationAudioWidget::setPaused(bool val)
0245 {
0246     if (val == isPaused())
0247     {
0248         return;
0249     }
0250 
0251     slotPlay();
0252 }
0253 
0254 void PresentationAudioWidget::keyPressEvent(QKeyEvent* event)
0255 {
0256     switch (event->key())
0257     {
0258         case (Qt::Key_Space):
0259         {
0260             m_playButton->animateClick();
0261             break;
0262         }
0263 
0264         case (Qt::Key_A):
0265         {
0266             if (m_prevButton->isEnabled())
0267             {
0268                 m_prevButton->animateClick();
0269             }
0270 
0271             break;
0272         }
0273 
0274         case (Qt::Key_S):
0275         {
0276             if (m_nextButton->isEnabled())
0277             {
0278                 m_nextButton->animateClick();
0279             }
0280 
0281             break;
0282         }
0283 
0284         case (Qt::Key_Escape):
0285         {
0286             if (m_stopButton->isEnabled())
0287             {
0288                 m_stopButton->animateClick();
0289             }
0290 
0291             break;
0292         }
0293 
0294         default:
0295         {
0296             break;
0297         }
0298     }
0299 
0300     event->accept();
0301 }
0302 
0303 void PresentationAudioWidget::slotPlay()
0304 {
0305     if (!d->mediaObject)
0306     {
0307         qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Internal Media Object is null!";
0308 
0309         return;
0310     }
0311 
0312 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0313 
0314     if ((d->mediaObject->playbackState() != QMediaPlayer::PlayingState) ||
0315         (d->mediaObject->playbackState() == QMediaPlayer::PausedState))
0316 
0317 #else
0318 
0319     if ((d->mediaObject->state() != QAVPlayer::PlayingState) ||
0320         (d->mediaObject->state() == QAVPlayer::PausedState))
0321 
0322 #endif
0323 
0324     {
0325 
0326 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0327 
0328         if (d->mediaObject->playbackState() != QMediaPlayer::PlayingState)
0329 
0330 #else
0331 
0332         if (d->mediaObject->state() != QAVPlayer::PlayingState)
0333 
0334 #endif
0335 
0336         {
0337             d->mediaObject->setSource(d->urlList[d->currIndex]);
0338             qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Playing:" << d->urlList[d->currIndex];
0339             d->mediaObject->play();
0340             setZeroTime();
0341         }
0342         else
0343         {
0344             d->mediaObject->pause();
0345         }
0346 
0347         d->canHide = true;
0348 
0349         Q_EMIT signalPlay();
0350     }
0351     else
0352     {
0353         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Pausing:" << d->urlList[d->currIndex];
0354 
0355         d->mediaObject->pause();
0356         d->canHide = false;
0357 
0358         Q_EMIT signalPause();
0359     }
0360 }
0361 
0362 void PresentationAudioWidget::slotStop()
0363 {
0364     if (!d->mediaObject)
0365     {
0366         qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Internal Media Object is null!";
0367 
0368         return;
0369     }
0370 
0371     qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Stoping:" << d->urlList[d->currIndex];
0372     d->playingNext = false;
0373     d->mediaObject->stop();
0374     d->currIndex   = 0;
0375     setZeroTime();
0376     checkSkip();
0377 }
0378 
0379 void PresentationAudioWidget::slotPrev()
0380 {
0381     d->currIndex--;
0382 
0383     if (d->currIndex < 0)
0384     {
0385         if (d->sharedData->soundtrackLoop)
0386         {
0387             d->currIndex = d->urlList.count() - 1;
0388         }
0389         else
0390         {
0391             d->currIndex = 0;
0392             return;
0393         }
0394     }
0395 
0396     d->playingNext = false;
0397     d->mediaObject->stop();
0398     slotPlay();
0399 }
0400 
0401 void PresentationAudioWidget::slotNext()
0402 {
0403     d->currIndex++;
0404 
0405     if (d->currIndex >= d->urlList.count())
0406     {
0407         if (d->sharedData->soundtrackLoop)
0408         {
0409             d->currIndex = 0;
0410         }
0411         else
0412         {
0413             d->currIndex = d->urlList.count() - 1;
0414             return;
0415         }
0416     }
0417 
0418     d->playingNext = false;
0419     d->mediaObject->stop();
0420     slotPlay();
0421 }
0422 
0423 void PresentationAudioWidget::slotTimeUpdaterTimeout(qint64 current)
0424 {
0425 
0426 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0427 
0428     if (d->mediaObject->error() != QMediaPlayer::NoError)
0429 
0430 #else
0431 
0432     if (d->mediaObject->mediaStatus() == QAVPlayer::InvalidMedia)
0433 
0434 #endif
0435 
0436     {
0437         slotError();
0438         return;
0439     }
0440 
0441     int hours      = (int)(current  / (qint64)(60 * 60 * 1000));
0442     int mins       = (int)((current / (qint64)(60 * 1000)) - (qint64)(hours * 60));
0443     int secs       = (int)((current / (qint64)1000) - (qint64)(hours * 60 + mins * 60));
0444     QTime elapsedTime(hours, mins, secs);
0445 
0446     if (d->isZeroTime && (d->mediaObject->duration() > 0))
0447     {
0448         d->isZeroTime = false;
0449         qint64 total  = d->mediaObject->duration();
0450         hours         = (int)(total  / (qint64)(60 * 60 * 1000));
0451         mins          = (int)((total / (qint64)(60 * 1000)) - (qint64)(hours * 60));
0452         secs          = (int)((total / (qint64)1000) - (qint64)(hours * 60 + mins * 60));
0453         QTime totalTime(hours, mins, secs);
0454         m_totalTimeLabel->setText(totalTime.toString(QLatin1String("H:mm:ss")));
0455     }
0456 
0457     m_elapsedTimeLabel->setText(elapsedTime.toString(QLatin1String("H:mm:ss")));
0458 }
0459 
0460 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA
0461 
0462 void PresentationAudioWidget::slotMediaStateChanged(QMediaPlayer::MediaStatus status)
0463 {
0464     if (status == QMediaPlayer::EndOfMedia)
0465     {
0466         slotNext();
0467     }
0468 }
0469 
0470 void PresentationAudioWidget::slotPlayerError(QMediaPlayer::Error err)
0471 {
0472     if (err != QMediaPlayer::NoError)
0473     {
0474         qCDebug(DIGIKAM_GENERAL_LOG) << "An error as occured while playing (" << err << ")";
0475         slotError();
0476     }
0477 }
0478 
0479 void PresentationAudioWidget::slotPlayerStateChanged(QMediaPlayer::PlaybackState state)
0480 {
0481     switch (state)
0482     {
0483         case QMediaPlayer::PausedState:
0484         case QMediaPlayer::StoppedState:
0485         {
0486             m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-start")));
0487             checkSkip();
0488             break;
0489         }
0490 
0491         case QMediaPlayer::PlayingState:
0492         {
0493             m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause")));
0494             d->playingNext = true;
0495             checkSkip();
0496             break;
0497         }
0498 
0499         default:
0500         {
0501             break;
0502         }
0503     }
0504 }
0505 
0506 #else
0507 
0508 void PresentationAudioWidget::slotMediaStateChanged(QAVPlayer::MediaStatus status)
0509 {
0510     if (status == QAVPlayer::EndOfMedia)
0511     {
0512         slotNext();
0513     }
0514 }
0515 
0516 void PresentationAudioWidget::slotPlayerError(QAVPlayer::Error err, const QString& message)
0517 {
0518     if (err != QAVPlayer::NoError)
0519     {
0520         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "An error as occurred while playing (" << message << ")";
0521         slotError();
0522     }
0523 }
0524 
0525 void PresentationAudioWidget::slotPlayerStateChanged(QAVPlayer::State state)
0526 {
0527     switch (state)
0528     {
0529         case QAVPlayer::PausedState:
0530         case QAVPlayer::StoppedState:
0531         {
0532             m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-start")));
0533             checkSkip();
0534             break;
0535         }
0536 
0537         case QAVPlayer::PlayingState:
0538         {
0539             m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause")));
0540             d->playingNext = true;
0541             checkSkip();
0542             break;
0543         }
0544 
0545         default:
0546         {
0547             break;
0548         }
0549     }
0550 }
0551 
0552 #endif
0553 
0554 void PresentationAudioWidget::slotError()
0555 {
0556     qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "An error as occurred!";
0557 
0558     /* TODO:
0559      * Display error on slideshow.
0560      * A QWidget pop-up could help
0561      */
0562 
0563     slotNext();
0564 }
0565 
0566 } // namespace DigikamGenericPresentationPlugin
0567 
0568 #include "moc_presentationaudiowidget.cpp"