2021-05-24 07:25:56 +00:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2007 Pino Toscano <pino@kde.org>
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2007-02-05 00:49:40 +00:00
|
|
|
|
2007-04-19 18:30:20 +00:00
|
|
|
#include "audioplayer.h"
|
|
|
|
|
2007-02-05 00:49:40 +00:00
|
|
|
// qt/kde includes
|
2023-06-21 15:50:58 +00:00
|
|
|
#include <KLocalizedString>
|
2020-07-08 11:54:37 +00:00
|
|
|
#include <QBuffer>
|
2018-08-31 09:23:45 +00:00
|
|
|
#include <QDebug>
|
2007-02-05 00:49:40 +00:00
|
|
|
#include <QDir>
|
2023-05-01 14:53:41 +00:00
|
|
|
#include <QRandomGenerator>
|
2023-06-21 15:50:58 +00:00
|
|
|
|
|
|
|
#include "config-okular.h"
|
|
|
|
|
|
|
|
#if HAVE_PHONON
|
2007-05-01 05:28:28 +00:00
|
|
|
#include <phonon/abstractmediastream.h>
|
|
|
|
#include <phonon/audiooutput.h>
|
2007-02-05 00:49:40 +00:00
|
|
|
#include <phonon/mediaobject.h>
|
2014-08-07 23:35:27 +00:00
|
|
|
#include <phonon/path.h>
|
2023-06-21 15:50:58 +00:00
|
|
|
#endif
|
2007-02-05 00:49:40 +00:00
|
|
|
|
|
|
|
// local includes
|
2007-04-20 11:26:05 +00:00
|
|
|
#include "action.h"
|
2008-01-20 16:39:21 +00:00
|
|
|
#include "debug_p.h"
|
2023-06-21 15:50:58 +00:00
|
|
|
#include "document.h"
|
2007-02-05 00:49:40 +00:00
|
|
|
#include "sound.h"
|
2023-05-01 14:53:41 +00:00
|
|
|
#include <stdlib.h>
|
2007-02-05 00:49:40 +00:00
|
|
|
|
|
|
|
using namespace Okular;
|
|
|
|
|
2023-06-21 15:50:58 +00:00
|
|
|
#if HAVE_PHONON
|
|
|
|
|
2023-08-03 21:57:04 +00:00
|
|
|
class PlayData;
|
|
|
|
class SoundInfo;
|
|
|
|
|
2023-06-21 15:50:58 +00:00
|
|
|
namespace Okular
|
|
|
|
{
|
|
|
|
class AudioPlayerPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit AudioPlayerPrivate(AudioPlayer *qq);
|
|
|
|
|
|
|
|
~AudioPlayerPrivate();
|
|
|
|
|
|
|
|
int newId() const;
|
|
|
|
bool play(const SoundInfo &si);
|
|
|
|
void stopPlayings();
|
|
|
|
|
|
|
|
void finished(int);
|
|
|
|
|
|
|
|
AudioPlayer *q;
|
|
|
|
|
|
|
|
QHash<int, PlayData *> m_playing;
|
|
|
|
QUrl m_currentDocument;
|
|
|
|
AudioPlayer::State m_state;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2007-02-05 00:49:40 +00:00
|
|
|
// helper class used to store info about a sound to be played
|
|
|
|
class SoundInfo
|
|
|
|
{
|
|
|
|
public:
|
2017-09-05 21:27:18 +00:00
|
|
|
explicit SoundInfo(const Sound *s = nullptr, const SoundAction *ls = nullptr)
|
2007-02-05 00:49:40 +00:00
|
|
|
: sound(s)
|
|
|
|
, volume(0.5)
|
|
|
|
, synchronous(false)
|
|
|
|
, repeat(false)
|
|
|
|
, mix(false)
|
|
|
|
{
|
|
|
|
if (ls) {
|
|
|
|
volume = ls->volume();
|
|
|
|
synchronous = ls->synchronous();
|
|
|
|
repeat = ls->repeat();
|
|
|
|
mix = ls->mix();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Sound *sound;
|
|
|
|
double volume;
|
|
|
|
bool synchronous;
|
|
|
|
bool repeat;
|
|
|
|
bool mix;
|
|
|
|
};
|
|
|
|
|
2007-02-05 12:02:53 +00:00
|
|
|
class PlayData
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
public:
|
2007-02-05 12:02:53 +00:00
|
|
|
PlayData()
|
2017-09-05 21:27:18 +00:00
|
|
|
: m_mediaobject(nullptr)
|
|
|
|
, m_output(nullptr)
|
|
|
|
, m_buffer(nullptr)
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-02-05 15:26:05 +00:00
|
|
|
void play()
|
|
|
|
{
|
2007-05-01 11:25:32 +00:00
|
|
|
if (m_buffer) {
|
|
|
|
m_buffer->open(QIODevice::ReadOnly);
|
2007-02-05 15:26:05 +00:00
|
|
|
}
|
2007-05-01 11:25:32 +00:00
|
|
|
m_mediaobject->play();
|
2007-02-05 15:26:05 +00:00
|
|
|
}
|
|
|
|
|
2007-02-05 12:02:53 +00:00
|
|
|
~PlayData()
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
2007-05-01 11:25:32 +00:00
|
|
|
m_mediaobject->stop();
|
|
|
|
delete m_mediaobject;
|
2007-02-05 00:49:40 +00:00
|
|
|
delete m_output;
|
2007-05-01 11:25:32 +00:00
|
|
|
delete m_buffer;
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 23:28:49 +00:00
|
|
|
PlayData(const PlayData &) = delete;
|
|
|
|
PlayData &operator=(const PlayData &) = delete;
|
|
|
|
|
2007-02-05 00:49:40 +00:00
|
|
|
Phonon::MediaObject *m_mediaobject;
|
|
|
|
Phonon::AudioOutput *m_output;
|
2007-05-01 11:25:32 +00:00
|
|
|
QBuffer *m_buffer;
|
2007-02-05 00:49:40 +00:00
|
|
|
SoundInfo m_info;
|
|
|
|
};
|
|
|
|
|
2007-07-10 18:24:18 +00:00
|
|
|
AudioPlayerPrivate::AudioPlayerPrivate(AudioPlayer *qq)
|
2013-11-27 22:26:26 +00:00
|
|
|
: q(qq)
|
|
|
|
, m_state(AudioPlayer::StoppedState)
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
2007-07-10 18:24:18 +00:00
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
|
2007-07-10 18:24:18 +00:00
|
|
|
AudioPlayerPrivate::~AudioPlayerPrivate()
|
|
|
|
{
|
|
|
|
stopPlayings();
|
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
|
2007-07-10 18:24:18 +00:00
|
|
|
int AudioPlayerPrivate::newId() const
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
2023-05-01 14:53:41 +00:00
|
|
|
auto random = QRandomGenerator::global();
|
2007-02-05 00:49:40 +00:00
|
|
|
int newid = 0;
|
2007-02-05 12:02:53 +00:00
|
|
|
QHash<int, PlayData *>::const_iterator it;
|
|
|
|
QHash<int, PlayData *>::const_iterator itEnd = m_playing.constEnd();
|
2007-02-05 00:49:40 +00:00
|
|
|
do {
|
2023-05-01 14:53:41 +00:00
|
|
|
newid = random->bounded(RAND_MAX);
|
2007-02-05 00:49:40 +00:00
|
|
|
it = m_playing.constFind(newid);
|
|
|
|
} while (it != itEnd);
|
|
|
|
return newid;
|
|
|
|
}
|
|
|
|
|
2007-07-10 18:24:18 +00:00
|
|
|
bool AudioPlayerPrivate::play(const SoundInfo &si)
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
2014-09-11 17:36:01 +00:00
|
|
|
qCDebug(OkularCoreDebug);
|
2007-02-05 12:02:53 +00:00
|
|
|
PlayData *data = new PlayData();
|
|
|
|
data->m_output = new Phonon::AudioOutput(Phonon::NotificationCategory);
|
|
|
|
data->m_output->setVolume(si.volume);
|
2007-05-01 11:25:32 +00:00
|
|
|
data->m_mediaobject = new Phonon::MediaObject();
|
2007-08-10 09:43:33 +00:00
|
|
|
Phonon::createPath(data->m_mediaobject, data->m_output);
|
2007-02-05 12:02:53 +00:00
|
|
|
data->m_info = si;
|
2007-02-05 00:49:40 +00:00
|
|
|
bool valid = false;
|
|
|
|
|
|
|
|
switch (si.sound->soundType()) {
|
|
|
|
case Sound::External: {
|
|
|
|
QString url = si.sound->url();
|
2014-09-11 17:36:01 +00:00
|
|
|
qCDebug(OkularCoreDebug) << "External," << url;
|
2007-02-05 00:49:40 +00:00
|
|
|
if (!url.isEmpty()) {
|
2007-08-10 09:43:33 +00:00
|
|
|
int newid = newId();
|
2019-06-10 11:07:05 +00:00
|
|
|
QObject::connect(data->m_mediaobject, &Phonon::MediaObject::finished, q, [this, newid]() { finished(newid); });
|
2023-08-03 22:21:02 +00:00
|
|
|
const QUrl newurl = QUrl::fromUserInput(url, m_currentDocument.adjusted(QUrl::RemoveFilename).toLocalFile());
|
2007-02-05 00:49:40 +00:00
|
|
|
data->m_mediaobject->setCurrentSource(newurl);
|
2007-08-10 09:43:33 +00:00
|
|
|
m_playing.insert(newid, data);
|
|
|
|
valid = true;
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
2020-07-10 22:15:05 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
case Sound::Embedded: {
|
2007-02-05 12:02:53 +00:00
|
|
|
QByteArray filedata = si.sound->data();
|
2014-09-11 17:36:01 +00:00
|
|
|
qCDebug(OkularCoreDebug) << "Embedded," << filedata.length();
|
2007-02-05 12:02:53 +00:00
|
|
|
if (!filedata.isEmpty()) {
|
2014-09-11 17:36:01 +00:00
|
|
|
qCDebug(OkularCoreDebug) << "Mediaobject:" << data->m_mediaobject;
|
2007-08-10 09:43:33 +00:00
|
|
|
int newid = newId();
|
2019-06-10 11:07:05 +00:00
|
|
|
QObject::connect(data->m_mediaobject, &Phonon::MediaObject::finished, q, [this, newid]() { finished(newid); });
|
2007-08-10 09:43:33 +00:00
|
|
|
data->m_buffer = new QBuffer();
|
|
|
|
data->m_buffer->setData(filedata);
|
|
|
|
data->m_mediaobject->setCurrentSource(Phonon::MediaSource(data->m_buffer));
|
|
|
|
m_playing.insert(newid, data);
|
|
|
|
valid = true;
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
2020-07-10 22:15:05 +00:00
|
|
|
break;
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
2020-07-10 22:15:05 +00:00
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
if (!valid) {
|
2007-02-05 12:02:53 +00:00
|
|
|
delete data;
|
2017-09-05 21:27:18 +00:00
|
|
|
data = nullptr;
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
2007-02-05 15:26:05 +00:00
|
|
|
if (data) {
|
2014-09-11 17:36:01 +00:00
|
|
|
qCDebug(OkularCoreDebug) << "PLAY";
|
2007-02-05 15:26:05 +00:00
|
|
|
data->play();
|
2013-11-27 22:26:26 +00:00
|
|
|
m_state = AudioPlayer::PlayingState;
|
2007-02-05 15:26:05 +00:00
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
2007-07-10 18:24:18 +00:00
|
|
|
void AudioPlayerPrivate::stopPlayings()
|
2007-02-05 11:50:07 +00:00
|
|
|
{
|
|
|
|
qDeleteAll(m_playing);
|
|
|
|
m_playing.clear();
|
2013-11-27 22:26:26 +00:00
|
|
|
m_state = AudioPlayer::StoppedState;
|
2007-02-05 11:50:07 +00:00
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
|
2007-07-10 18:24:18 +00:00
|
|
|
void AudioPlayerPrivate::finished(int id)
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
2007-02-05 12:02:53 +00:00
|
|
|
QHash<int, PlayData *>::iterator it = m_playing.find(id);
|
2007-02-05 00:49:40 +00:00
|
|
|
if (it == m_playing.end()) {
|
|
|
|
return;
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
|
2007-02-05 15:26:05 +00:00
|
|
|
SoundInfo si = it.value()->m_info;
|
|
|
|
// if the sound must be repeated indefinitely, then start the playback
|
|
|
|
// again, otherwise destroy the PlayData as it's no more useful
|
|
|
|
if (si.repeat) {
|
|
|
|
it.value()->play();
|
|
|
|
} else {
|
|
|
|
delete it.value();
|
|
|
|
m_playing.erase(it);
|
2013-11-27 22:26:26 +00:00
|
|
|
m_state = AudioPlayer::StoppedState;
|
2007-02-05 15:26:05 +00:00
|
|
|
}
|
2014-09-11 17:36:01 +00:00
|
|
|
qCDebug(OkularCoreDebug) << "finished," << m_playing.count();
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer::AudioPlayer()
|
2007-07-10 18:24:18 +00:00
|
|
|
: QObject()
|
|
|
|
, d(new AudioPlayerPrivate(this))
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer::~AudioPlayer()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer *AudioPlayer::instance()
|
|
|
|
{
|
|
|
|
static AudioPlayer ap;
|
|
|
|
return ≈
|
|
|
|
}
|
|
|
|
|
2007-05-02 22:50:27 +00:00
|
|
|
void AudioPlayer::playSound(const Sound *sound, const SoundAction *linksound)
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
// we can't play null pointers ;)
|
|
|
|
if (!sound) {
|
|
|
|
return;
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
|
2007-07-10 18:24:18 +00:00
|
|
|
// we don't play external sounds for remote documents
|
|
|
|
if (sound->soundType() == Sound::External && !d->m_currentDocument.isLocalFile()) {
|
|
|
|
return;
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2007-07-10 18:24:18 +00:00
|
|
|
|
2014-09-11 17:36:01 +00:00
|
|
|
qCDebug(OkularCoreDebug);
|
2007-02-05 00:49:40 +00:00
|
|
|
SoundInfo si(sound, linksound);
|
|
|
|
|
2007-02-05 11:50:07 +00:00
|
|
|
// if the mix flag of the new sound is false, then the currently playing
|
|
|
|
// sounds must be stopped.
|
|
|
|
if (!si.mix) {
|
|
|
|
d->stopPlayings();
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2007-02-05 11:50:07 +00:00
|
|
|
|
2007-02-05 00:49:40 +00:00
|
|
|
d->play(si);
|
|
|
|
}
|
|
|
|
|
2007-02-05 15:26:05 +00:00
|
|
|
void AudioPlayer::stopPlaybacks()
|
|
|
|
{
|
|
|
|
d->stopPlayings();
|
|
|
|
}
|
|
|
|
|
2013-11-27 22:26:26 +00:00
|
|
|
AudioPlayer::State AudioPlayer::state() const
|
|
|
|
{
|
|
|
|
return d->m_state;
|
|
|
|
}
|
|
|
|
|
2023-06-21 15:50:58 +00:00
|
|
|
void AudioPlayer::resetDocument()
|
|
|
|
{
|
2023-08-03 21:57:04 +00:00
|
|
|
d->m_currentDocument = {};
|
2023-06-21 15:50:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioPlayer::setDocument(const QUrl &url, Okular::Document *document)
|
|
|
|
{
|
|
|
|
Q_UNUSED(document);
|
2023-08-03 21:57:04 +00:00
|
|
|
d->m_currentDocument = url;
|
2023-06-21 15:50:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
namespace Okular
|
|
|
|
{
|
|
|
|
class AudioPlayerPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Document *document;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer::AudioPlayer()
|
|
|
|
: d(new AudioPlayerPrivate())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer *AudioPlayer::instance()
|
|
|
|
{
|
|
|
|
static AudioPlayer ap;
|
|
|
|
return ≈
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioPlayer::playSound(const Sound *sound, const SoundAction *linksound)
|
|
|
|
{
|
|
|
|
Q_UNUSED(sound);
|
|
|
|
Q_UNUSED(linksound);
|
2023-06-22 06:11:40 +00:00
|
|
|
Q_EMIT d->document->warning(i18n("This Okular is built without audio support"), 2000);
|
2023-06-21 15:50:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer::State Okular::AudioPlayer::state() const
|
|
|
|
{
|
|
|
|
return State::StoppedState;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioPlayer::stopPlaybacks()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer::~AudioPlayer() noexcept
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioPlayer::resetDocument()
|
|
|
|
{
|
|
|
|
d->document = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioPlayer::setDocument(const QUrl &url, Okular::Document *document)
|
|
|
|
{
|
|
|
|
Q_UNUSED(url);
|
|
|
|
d->document = document;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2014-08-08 22:00:07 +00:00
|
|
|
#include "moc_audioplayer.cpp"
|