2021-04-11 17:22:13 +02:00
|
|
|
/**
|
|
|
|
* SPDX-FileCopyrightText: 2017 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
|
2022-12-15 21:59:19 +01:00
|
|
|
* SPDX-FileCopyrightText: 2021-2023 Bart De Vries <bart@mogwai.be>
|
2021-04-11 17:22:13 +02:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
2021-04-11 15:26:28 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2022-12-15 21:59:19 +01:00
|
|
|
#include <kmediasession/kmediasession.h>
|
|
|
|
#include <memory>
|
|
|
|
|
2021-05-01 21:35:37 +02:00
|
|
|
#include <QObject>
|
2021-04-11 15:26:28 +02:00
|
|
|
#include <QString>
|
2021-05-01 21:35:37 +02:00
|
|
|
#include <QUrl>
|
2021-04-11 15:26:28 +02:00
|
|
|
|
2021-06-03 16:23:06 +02:00
|
|
|
#include <KFormat>
|
|
|
|
|
2021-04-11 15:26:28 +02:00
|
|
|
#include "entry.h"
|
2021-07-04 14:53:42 +02:00
|
|
|
#include "error.h"
|
2021-04-11 15:26:28 +02:00
|
|
|
|
|
|
|
class AudioManagerPrivate;
|
|
|
|
|
|
|
|
class AudioManager : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
2022-12-15 21:59:19 +01:00
|
|
|
Q_PROPERTY(KMediaSession::MediaBackends currentBackend READ currentBackend WRITE setCurrentBackend NOTIFY currentBackendChanged)
|
|
|
|
Q_PROPERTY(QList<KMediaSession::MediaBackends> availableBackends READ availableBackends CONSTANT)
|
|
|
|
|
2021-05-01 21:35:37 +02:00
|
|
|
Q_PROPERTY(Entry *entry READ entry WRITE setEntry NOTIFY entryChanged)
|
|
|
|
Q_PROPERTY(bool muted READ muted WRITE setMuted NOTIFY mutedChanged)
|
|
|
|
Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged)
|
2022-12-15 21:59:19 +01:00
|
|
|
Q_PROPERTY(KMediaSession::MediaStatus status READ status NOTIFY statusChanged)
|
|
|
|
Q_PROPERTY(KMediaSession::PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged)
|
2021-05-01 21:35:37 +02:00
|
|
|
Q_PROPERTY(qreal playbackRate READ playbackRate WRITE setPlaybackRate NOTIFY playbackRateChanged)
|
2022-12-15 21:59:19 +01:00
|
|
|
Q_PROPERTY(KMediaSession::Error error READ error NOTIFY errorChanged)
|
2021-05-01 21:35:37 +02:00
|
|
|
Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged)
|
|
|
|
Q_PROPERTY(qint64 position READ position WRITE setPosition NOTIFY positionChanged)
|
|
|
|
Q_PROPERTY(bool seekable READ seekable NOTIFY seekableChanged)
|
|
|
|
Q_PROPERTY(bool canPlay READ canPlay NOTIFY canPlayChanged)
|
|
|
|
Q_PROPERTY(bool canSkipForward READ canSkipForward NOTIFY canSkipForwardChanged)
|
|
|
|
Q_PROPERTY(bool canSkipBackward READ canSkipBackward NOTIFY canSkipBackwardChanged)
|
|
|
|
Q_PROPERTY(bool canGoNext READ canGoNext NOTIFY canGoNextChanged)
|
2021-06-03 16:23:06 +02:00
|
|
|
Q_PROPERTY(QString formattedLeftDuration READ formattedLeftDuration NOTIFY positionChanged)
|
|
|
|
Q_PROPERTY(QString formattedDuration READ formattedDuration NOTIFY durationChanged)
|
|
|
|
Q_PROPERTY(QString formattedPosition READ formattedPosition NOTIFY positionChanged)
|
2022-06-30 10:21:09 +02:00
|
|
|
Q_PROPERTY(qint64 sleepTime READ sleepTime WRITE setSleepTimer RESET stopSleepTimer NOTIFY sleepTimerChanged)
|
|
|
|
Q_PROPERTY(qint64 remainingSleepTime READ remainingSleepTime NOTIFY remainingSleepTimeChanged)
|
|
|
|
Q_PROPERTY(QString formattedRemainingSleepTime READ formattedRemainingSleepTime NOTIFY remainingSleepTimeChanged)
|
2022-09-22 16:59:30 +02:00
|
|
|
Q_PROPERTY(bool isStreaming READ isStreaming NOTIFY isStreamingChanged)
|
2021-04-13 20:51:00 +02:00
|
|
|
|
2021-04-11 15:26:28 +02:00
|
|
|
public:
|
2021-05-20 21:42:13 +02:00
|
|
|
static AudioManager &instance()
|
|
|
|
{
|
|
|
|
static AudioManager _instance;
|
|
|
|
return _instance;
|
|
|
|
}
|
2021-04-11 16:19:58 +02:00
|
|
|
|
2021-04-11 15:26:28 +02:00
|
|
|
~AudioManager() override;
|
|
|
|
|
2022-12-15 21:59:19 +01:00
|
|
|
[[nodiscard]] Q_INVOKABLE QString backendName(KMediaSession::MediaBackends backend) const;
|
|
|
|
[[nodiscard]] KMediaSession::MediaBackends currentBackend() const;
|
|
|
|
[[nodiscard]] QList<KMediaSession::MediaBackends> availableBackends() const;
|
|
|
|
|
2021-05-01 21:35:37 +02:00
|
|
|
[[nodiscard]] Entry *entry() const;
|
2021-04-11 15:26:28 +02:00
|
|
|
[[nodiscard]] bool muted() const;
|
|
|
|
[[nodiscard]] qreal volume() const;
|
2021-04-11 23:07:21 +02:00
|
|
|
[[nodiscard]] QUrl source() const;
|
2022-12-15 21:59:19 +01:00
|
|
|
[[nodiscard]] KMediaSession::MediaStatus status() const;
|
|
|
|
[[nodiscard]] KMediaSession::PlaybackState playbackState() const;
|
2021-04-11 17:13:44 +02:00
|
|
|
[[nodiscard]] qreal playbackRate() const;
|
2021-04-12 22:18:04 +02:00
|
|
|
[[nodiscard]] qreal minimumPlaybackRate() const;
|
|
|
|
[[nodiscard]] qreal maximumPlaybackRate() const;
|
2022-12-15 21:59:19 +01:00
|
|
|
[[nodiscard]] KMediaSession::Error error() const;
|
2021-04-11 15:26:28 +02:00
|
|
|
[[nodiscard]] qint64 duration() const;
|
|
|
|
[[nodiscard]] qint64 position() const;
|
|
|
|
[[nodiscard]] bool seekable() const;
|
2021-04-11 23:07:21 +02:00
|
|
|
[[nodiscard]] bool canPlay() const;
|
2021-04-12 22:18:04 +02:00
|
|
|
[[nodiscard]] bool canPause() const;
|
2021-04-12 22:44:05 +02:00
|
|
|
[[nodiscard]] bool canSkipForward() const;
|
|
|
|
[[nodiscard]] bool canSkipBackward() const;
|
2021-04-13 20:51:00 +02:00
|
|
|
[[nodiscard]] bool canGoNext() const;
|
|
|
|
|
2021-06-03 16:23:06 +02:00
|
|
|
QString formattedDuration() const;
|
|
|
|
QString formattedLeftDuration() const;
|
|
|
|
QString formattedPosition() const;
|
|
|
|
|
2022-06-30 10:21:09 +02:00
|
|
|
qint64 sleepTime() const; // returns originally set sleep time
|
|
|
|
qint64 remainingSleepTime() const; // returns remaining sleep time
|
|
|
|
QString formattedRemainingSleepTime() const;
|
|
|
|
|
2022-09-22 16:59:30 +02:00
|
|
|
bool isStreaming() const;
|
|
|
|
|
2021-04-11 15:26:28 +02:00
|
|
|
Q_SIGNALS:
|
2022-12-15 21:59:19 +01:00
|
|
|
void currentBackendChanged(KMediaSession::MediaBackends backend);
|
2021-04-11 15:26:28 +02:00
|
|
|
|
2021-05-01 21:35:37 +02:00
|
|
|
void entryChanged(Entry *entry);
|
2021-04-11 15:26:28 +02:00
|
|
|
void mutedChanged(bool muted);
|
|
|
|
void volumeChanged();
|
2021-04-11 23:07:21 +02:00
|
|
|
void sourceChanged();
|
2022-12-15 21:59:19 +01:00
|
|
|
void statusChanged(KMediaSession::MediaStatus status);
|
|
|
|
void playbackStateChanged(KMediaSession::PlaybackState state);
|
2021-04-11 17:13:44 +02:00
|
|
|
void playbackRateChanged(qreal rate);
|
2022-12-15 21:59:19 +01:00
|
|
|
void errorChanged(KMediaSession::Error error);
|
2021-04-11 15:26:28 +02:00
|
|
|
void durationChanged(qint64 duration);
|
|
|
|
void positionChanged(qint64 position);
|
|
|
|
void seekableChanged(bool seekable);
|
2021-04-12 22:18:04 +02:00
|
|
|
void canPlayChanged();
|
|
|
|
void canPauseChanged();
|
2021-04-12 22:44:05 +02:00
|
|
|
void canSkipForwardChanged();
|
|
|
|
void canSkipBackwardChanged();
|
2021-04-13 20:51:00 +02:00
|
|
|
void canGoNextChanged();
|
|
|
|
|
2022-06-30 10:21:09 +02:00
|
|
|
void sleepTimerChanged(qint64 duration);
|
|
|
|
void remainingSleepTimeChanged(qint64 duration);
|
|
|
|
|
2022-09-22 16:59:30 +02:00
|
|
|
void isStreamingChanged();
|
|
|
|
|
2021-07-14 22:27:52 +02:00
|
|
|
void logError(Error::Type type, const QString &url, const QString &id, const int errorId, const QString &errorString, const QString &title);
|
2021-07-04 14:53:42 +02:00
|
|
|
|
2022-12-15 21:59:19 +01:00
|
|
|
// mpris2 signals
|
|
|
|
void raiseWindowRequested();
|
|
|
|
void quitRequested();
|
|
|
|
|
2021-04-11 15:26:28 +02:00
|
|
|
public Q_SLOTS:
|
2022-12-15 21:59:19 +01:00
|
|
|
void setCurrentBackend(KMediaSession::MediaBackends backend);
|
2021-04-11 15:26:28 +02:00
|
|
|
|
2021-05-01 21:35:37 +02:00
|
|
|
void setEntry(Entry *entry);
|
2021-04-11 15:26:28 +02:00
|
|
|
void setMuted(bool muted);
|
|
|
|
void setVolume(qreal volume);
|
|
|
|
void setPosition(qint64 position);
|
2021-04-11 17:13:44 +02:00
|
|
|
void setPlaybackRate(qreal rate);
|
2021-04-11 15:26:28 +02:00
|
|
|
void play();
|
|
|
|
void pause();
|
2021-04-12 22:18:04 +02:00
|
|
|
void playPause();
|
2021-04-11 15:26:28 +02:00
|
|
|
void stop();
|
|
|
|
void seek(qint64 position);
|
2021-04-12 22:44:05 +02:00
|
|
|
void skipBackward();
|
|
|
|
void skipForward();
|
2021-04-13 20:51:00 +02:00
|
|
|
void next();
|
|
|
|
|
2022-06-30 10:21:09 +02:00
|
|
|
void setSleepTimer(qint64 duration);
|
|
|
|
void stopSleepTimer();
|
|
|
|
|
2021-04-11 15:26:28 +02:00
|
|
|
private Q_SLOTS:
|
|
|
|
|
|
|
|
void mediaStatusChanged();
|
Replace Audio prepare hack by nicer, asynchronous solution
The main bits of this implementation are:
- Start a new track in paused state. We don't care about the actual
media state or player state that QMediaPlayer is reporting. We will
deal with that when the audio actually starts playing.
- If a player position needs to be restored, we set d->m_pendingSeek to
the position that needs to be seeked. We don't actually seek because
we have no idea what state the player is in yet.
- On the positionChanged signal of QMP, and if the media is buffered, we
check if there is pendingSeek value set which is set to a different
value than the current player position. If so, we call
d->m_player.setPosition(). If we have arrived at the correct
position, then we reset d->m_pendingSeek to -1.
- In the position(), duration() and seek() methods, we return sensible
values, even QMP is not. So, we report the duration from the
enclosure, the position from d->m_pendingSeek, and let seek() change
the value of d->m_PendingSeek (if it's not -1) to the new seek
position.
- When there's a pending seek, we set the notifyInterval to shorter
interval to reduce the startup audio glitch as much as possible. We
then reset it to the default of 1000 msec.
This was tested on linux and android.
2021-06-14 16:31:25 +02:00
|
|
|
void playerDurationChanged(qint64 duration);
|
2021-04-11 15:26:28 +02:00
|
|
|
void playerMutedChanged();
|
|
|
|
void playerVolumeChanged();
|
Replace Audio prepare hack by nicer, asynchronous solution
The main bits of this implementation are:
- Start a new track in paused state. We don't care about the actual
media state or player state that QMediaPlayer is reporting. We will
deal with that when the audio actually starts playing.
- If a player position needs to be restored, we set d->m_pendingSeek to
the position that needs to be seeked. We don't actually seek because
we have no idea what state the player is in yet.
- On the positionChanged signal of QMP, and if the media is buffered, we
check if there is pendingSeek value set which is set to a different
value than the current player position. If so, we call
d->m_player.setPosition(). If we have arrived at the correct
position, then we reset d->m_pendingSeek to -1.
- In the position(), duration() and seek() methods, we return sensible
values, even QMP is not. So, we report the duration from the
enclosure, the position from d->m_pendingSeek, and let seek() change
the value of d->m_PendingSeek (if it's not -1) to the new seek
position.
- When there's a pending seek, we set the notifyInterval to shorter
interval to reduce the startup audio glitch as much as possible. We
then reset it to the default of 1000 msec.
This was tested on linux and android.
2021-06-14 16:31:25 +02:00
|
|
|
void savePlayPosition();
|
2023-02-15 16:43:57 +01:00
|
|
|
void setEntryInfo(Entry *entry);
|
|
|
|
void prepareAudio(const QUrl &loadUrl);
|
Replace Audio prepare hack by nicer, asynchronous solution
The main bits of this implementation are:
- Start a new track in paused state. We don't care about the actual
media state or player state that QMediaPlayer is reporting. We will
deal with that when the audio actually starts playing.
- If a player position needs to be restored, we set d->m_pendingSeek to
the position that needs to be seeked. We don't actually seek because
we have no idea what state the player is in yet.
- On the positionChanged signal of QMP, and if the media is buffered, we
check if there is pendingSeek value set which is set to a different
value than the current player position. If so, we call
d->m_player.setPosition(). If we have arrived at the correct
position, then we reset d->m_pendingSeek to -1.
- In the position(), duration() and seek() methods, we return sensible
values, even QMP is not. So, we report the duration from the
enclosure, the position from d->m_pendingSeek, and let seek() change
the value of d->m_PendingSeek (if it's not -1) to the new seek
position.
- When there's a pending seek, we set the notifyInterval to shorter
interval to reduce the startup audio glitch as much as possible. We
then reset it to the default of 1000 msec.
This was tested on linux and android.
2021-06-14 16:31:25 +02:00
|
|
|
void checkForPendingSeek();
|
2022-12-15 21:59:19 +01:00
|
|
|
void updateMetaData();
|
2021-04-11 20:30:12 +02:00
|
|
|
|
2021-04-11 15:26:28 +02:00
|
|
|
private:
|
2021-05-20 21:42:13 +02:00
|
|
|
explicit AudioManager(QObject *parent = nullptr);
|
|
|
|
|
2021-04-11 15:26:28 +02:00
|
|
|
friend class AudioManagerPrivate;
|
|
|
|
|
|
|
|
std::unique_ptr<AudioManagerPrivate> d;
|
2021-06-03 16:23:06 +02:00
|
|
|
KFormat m_kformat;
|
2021-04-11 15:26:28 +02:00
|
|
|
};
|