1 /*
2    SPDX-FileCopyrightText: 2017 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
3 
4    SPDX-License-Identifier: LGPL-3.0-or-later
5  */
6 
7 #include "audiowrapper.h"
8 #include "powermanagementinterface.h"
9 
10 #include "qtMultimediaLogging.h"
11 
12 #include <QTimer>
13 #include <QAudio>
14 
15 #include "config-upnp-qt.h"
16 
17 class AudioWrapperPrivate
18 {
19 
20 public:
21 
22     PowerManagementInterface mPowerInterface;
23 
24     QMediaPlayer mPlayer;
25 
26     qint64 mSavedPosition = 0.0;
27 
28     qint64 mUndoSavedPosition = 0.0;
29 
30     bool mHasSavedPosition = false;
31 
32 };
33 
AudioWrapper(QObject * parent)34 AudioWrapper::AudioWrapper(QObject *parent) : QObject(parent), d(std::make_unique<AudioWrapperPrivate>())
35 {
36     connect(&d->mPlayer, &QMediaPlayer::mutedChanged, this, &AudioWrapper::playerMutedChanged);
37     connect(&d->mPlayer, &QMediaPlayer::volumeChanged, this, &AudioWrapper::playerVolumeChanged);
38     connect(&d->mPlayer, &QMediaPlayer::mediaChanged, this, &AudioWrapper::sourceChanged);
39     connect(&d->mPlayer, &QMediaPlayer::mediaStatusChanged, this, &AudioWrapper::statusChanged);
40     connect(&d->mPlayer, &QMediaPlayer::mediaStatusChanged, this, &AudioWrapper::mediaStatusChanged);
41     connect(&d->mPlayer, &QMediaPlayer::stateChanged, this, &AudioWrapper::playbackStateChanged);
42     connect(&d->mPlayer, &QMediaPlayer::stateChanged, this, &AudioWrapper::playerStateChanged);
43     connect(&d->mPlayer, QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error), this, &AudioWrapper::errorChanged);
44     connect(&d->mPlayer, &QMediaPlayer::durationChanged, this, &AudioWrapper::durationChanged);
45     connect(&d->mPlayer, &QMediaPlayer::positionChanged, this, &AudioWrapper::positionChanged);
46     connect(&d->mPlayer, &QMediaPlayer::seekableChanged, this, &AudioWrapper::seekableChanged);
47 }
48 
~AudioWrapper()49 AudioWrapper::~AudioWrapper()
50 {
51     d->mPowerInterface.setPreventSleep(false);
52 }
53 
muted() const54 bool AudioWrapper::muted() const
55 {
56     return d->mPlayer.isMuted();
57 }
58 
volume() const59 qreal AudioWrapper::volume() const
60 {
61     auto realVolume = static_cast<qreal>(d->mPlayer.volume() / 100.0);
62     auto userVolume = static_cast<qreal>(QAudio::convertVolume(realVolume, QAudio::LinearVolumeScale, QAudio::LogarithmicVolumeScale));
63 
64     return userVolume * 100.0;
65 }
66 
source() const67 QUrl AudioWrapper::source() const
68 {
69 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
70     return d->mPlayer.media().request().url();
71 #else
72     return d->mPlayer.media().canonicalUrl();
73 #endif
74 }
75 
error() const76 QMediaPlayer::Error AudioWrapper::error() const
77 {
78     if (d->mPlayer.error() != QMediaPlayer::NoError) {
79         qDebug() << "AudioWrapper::error" << d->mPlayer.errorString();
80     }
81 
82     return d->mPlayer.error();
83 }
84 
duration() const85 qint64 AudioWrapper::duration() const
86 {
87     return d->mPlayer.duration();
88 }
89 
position() const90 qint64 AudioWrapper::position() const
91 {
92     return d->mPlayer.position();
93 }
94 
seekable() const95 bool AudioWrapper::seekable() const
96 {
97     return d->mPlayer.isSeekable();
98 }
99 
playbackState() const100 QMediaPlayer::State AudioWrapper::playbackState() const
101 {
102     return d->mPlayer.state();
103 }
104 
status() const105 QMediaPlayer::MediaStatus AudioWrapper::status() const
106 {
107     return d->mPlayer.mediaStatus();
108 }
109 
setMuted(bool muted)110 void AudioWrapper::setMuted(bool muted)
111 {
112     d->mPlayer.setMuted(muted);
113 }
114 
setVolume(qreal volume)115 void AudioWrapper::setVolume(qreal volume)
116 {
117     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::setVolume" << volume;
118 
119     auto realVolume = static_cast<qreal>(QAudio::convertVolume(volume / 100.0, QAudio::LogarithmicVolumeScale, QAudio::LinearVolumeScale));
120     d->mPlayer.setVolume(qRound(realVolume * 100));
121 }
122 
setSource(const QUrl & source)123 void AudioWrapper::setSource(const QUrl &source)
124 {
125     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::setSource" << source;
126 
127     d->mPlayer.setMedia({source});
128 }
129 
setPosition(qint64 position)130 void AudioWrapper::setPosition(qint64 position)
131 {
132     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::setPosition" << position;
133 
134     if (d->mPlayer.duration() <= 0) {
135         savePosition(position);
136         return;
137     }
138 
139     d->mPlayer.setPosition(position);
140 }
141 
play()142 void AudioWrapper::play()
143 {
144     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::play";
145 
146     d->mPlayer.play();
147 
148     if (d->mHasSavedPosition) {
149         qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::playerDurationSignalChanges" << "restore old position" << d->mSavedPosition;
150 
151         setPosition(d->mSavedPosition);
152         d->mHasSavedPosition = false;
153     }
154 }
155 
pause()156 void AudioWrapper::pause()
157 {
158     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::pause";
159 
160     d->mPlayer.pause();
161 }
162 
stop()163 void AudioWrapper::stop()
164 {
165     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::stop";
166 
167     d->mPlayer.stop();
168 }
169 
seek(qint64 position)170 void AudioWrapper::seek(qint64 position)
171 {
172     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::seek" << position;
173 
174     d->mPlayer.setPosition(position);
175 }
176 
mediaStatusChanged()177 void AudioWrapper::mediaStatusChanged()
178 {
179     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::mediaStatusChanged" << d->mPlayer.mediaStatus();
180 }
181 
playerStateChanged()182 void AudioWrapper::playerStateChanged()
183 {
184     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::playerStateChanged" << d->mPlayer.state();
185 
186     switch(d->mPlayer.state())
187     {
188     case QMediaPlayer::State::StoppedState:
189         Q_EMIT stopped();
190         d->mPowerInterface.setPreventSleep(false);
191         break;
192     case QMediaPlayer::State::PlayingState:
193         Q_EMIT playing();
194         d->mPowerInterface.setPreventSleep(true);
195         break;
196     case QMediaPlayer::State::PausedState:
197         Q_EMIT paused();
198         d->mPowerInterface.setPreventSleep(false);
199         break;
200     }
201 }
202 
playerVolumeChanged()203 void AudioWrapper::playerVolumeChanged()
204 {
205     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::playerVolumeChanged" << d->mPlayer.volume();
206 
207     QTimer::singleShot(0, [this]() {Q_EMIT volumeChanged();});
208 }
209 
playerMutedChanged()210 void AudioWrapper::playerMutedChanged()
211 {
212     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::playerMutedChanged";
213 
214     QTimer::singleShot(0, [this]() {Q_EMIT mutedChanged(muted());});
215 }
216 
playerStateSignalChanges(QMediaPlayer::State newState)217 void AudioWrapper::playerStateSignalChanges(QMediaPlayer::State newState)
218 {
219     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::playerStateSignalChanges" << newState;
220 
221     QMetaObject::invokeMethod(this, [this, newState]() {Q_EMIT playbackStateChanged(newState);}, Qt::QueuedConnection);
222 }
223 
mediaStatusSignalChanges(QMediaPlayer::MediaStatus newStatus)224 void AudioWrapper::mediaStatusSignalChanges(QMediaPlayer::MediaStatus newStatus)
225 {
226     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::mediaStatusSignalChanges" << newStatus;
227 
228     QMetaObject::invokeMethod(this, [this, newStatus]() {Q_EMIT statusChanged(newStatus);}, Qt::QueuedConnection);
229 }
230 
playerDurationSignalChanges(qint64 newDuration)231 void AudioWrapper::playerDurationSignalChanges(qint64 newDuration)
232 {
233     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::playerDurationSignalChanges" << newDuration;
234 
235     QMetaObject::invokeMethod(this, [this, newDuration]() {Q_EMIT durationChanged(newDuration);}, Qt::QueuedConnection);
236 }
237 
playerPositionSignalChanges(qint64 newPosition)238 void AudioWrapper::playerPositionSignalChanges(qint64 newPosition)
239 {
240     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::playerPositionSignalChanges" << newPosition;
241 
242     QMetaObject::invokeMethod(this, [this, newPosition]() {Q_EMIT positionChanged(newPosition);}, Qt::QueuedConnection);
243 }
244 
playerVolumeSignalChanges()245 void AudioWrapper::playerVolumeSignalChanges()
246 {
247     QMetaObject::invokeMethod(this, [this]() {Q_EMIT volumeChanged();}, Qt::QueuedConnection);
248 }
249 
playerMutedSignalChanges(bool isMuted)250 void AudioWrapper::playerMutedSignalChanges(bool isMuted)
251 {
252     QMetaObject::invokeMethod(this, [this, isMuted]() {Q_EMIT mutedChanged(isMuted);}, Qt::QueuedConnection);
253 }
254 
playerSeekableSignalChanges(bool isSeekable)255 void AudioWrapper::playerSeekableSignalChanges(bool isSeekable)
256 {
257     QMetaObject::invokeMethod(this, [this, isSeekable]() {Q_EMIT seekableChanged(isSeekable);}, Qt::QueuedConnection);
258 }
259 
saveUndoPosition(qint64 position)260 void AudioWrapper::saveUndoPosition(qint64 position)
261 {
262     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::saveUndoPosition" << position;
263 
264     d->mUndoSavedPosition = position;
265 }
266 
restoreUndoPosition()267 void AudioWrapper::restoreUndoPosition()
268 {
269     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::restoreUndoPosition";
270 
271     d->mHasSavedPosition = true;
272     d->mSavedPosition = d->mUndoSavedPosition;
273 }
274 
savePosition(qint64 position)275 void AudioWrapper::savePosition(qint64 position)
276 {
277     qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::savePosition" << position;
278 
279     if (!d->mHasSavedPosition) {
280         d->mHasSavedPosition = true;
281         d->mSavedPosition = position;
282         qCDebug(orgKdeElisaPlayerQtMultimedia) << "AudioWrapper::savePosition" << "restore old position" << d->mSavedPosition;
283     }
284 }
285 
286 
287 #include "moc_audiowrapper.cpp"
288