aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2023-11-26 01:10:36 -0500
committerGravatar Chris Xiong <chirs241097@gmail.com> 2023-11-26 01:10:36 -0500
commit60989e52b3f3bc0a95d3e61bd8e59fa4d9b7ab83 (patch)
treeaf08e5b6f7019c6f70bf3800a419ec78db140988
parent382d85b15ce9cc4580a2522b39f5dd4ce43a24b0 (diff)
downloadQMidiPlayer-60989e52b3f3bc0a95d3e61bd8e59fa4d9b7ab83.tar.xz
The 2 year constipation. (mpris plugin)
Probably buggy as hell.
-rw-r--r--CMakeLists.txt1
-rw-r--r--INSTALL.md7
-rw-r--r--include/qmpcorepublic.hpp31
-rw-r--r--mpris-plugin/CMakeLists.txt30
-rw-r--r--mpris-plugin/qmpmpris.cpp29
-rw-r--r--mpris-plugin/qmpmpris.hpp32
-rw-r--r--mpris-plugin/qmpmprisimpl.cpp206
-rw-r--r--mpris-plugin/qmpmprisimpl.hpp72
-rw-r--r--mpris-plugin/qmprisdbusinterface.cpp200
-rw-r--r--mpris-plugin/qmprisdbusinterface.hpp148
-rw-r--r--mpris-plugin/qmpriswrapper.cpp77
-rw-r--r--mpris-plugin/qmpriswrapper.hpp53
-rw-r--r--qmidiplayer-desktop/qmpmainwindow.cpp140
-rw-r--r--qmidiplayer-desktop/qmpmainwindow.hpp6
-rw-r--r--qmidiplayer-desktop/qmpplugin.cpp46
-rw-r--r--qmidiplayer-desktop/qmpplugin.hpp3
-rw-r--r--visualization/qmpvisualization.cpp12
-rw-r--r--visualization/renderer/qmppluginapistub.cpp12
-rw-r--r--visualization/renderer/qmppluginapistub.hpp3
19 files changed, 1050 insertions, 58 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5862f0e..4c84653 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ add_definitions(-DRC_VER_PATCH=${PROJECT_VERSION_PATCH})
add_subdirectory(core)
add_subdirectory(qmidiplayer-desktop)
add_subdirectory(sample-plugin)
+add_subdirectory(mpris-plugin)
add_subdirectory(midifmt-plugin)
add_subdirectory(simple-visualization)
if(BUILD_VISUALIZATION)
diff --git a/INSTALL.md b/INSTALL.md
index 46609ec..85a4171 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -35,7 +35,7 @@ make cmake qt5 glfw3 glew devil zlib freetype fluidsynth
```
Proceed with normal instructions for Linux with tools replaced by those
-provided by mxe. You have to build and install RtMidi yourself.
+provided by mxe. You will have to build and install RtMidi yourself.
At the moment there are some issues with mxe that may cause a
build failure:
@@ -48,3 +48,8 @@ build failure:
FindGLEW.cmake and make sure the section for WIN32 makes sense.
This list may change whenever mxe updates. You are on your own to figure them out.
+
+Another option is to use msys2. The build steps should resemble building on Linux a lot, just
+remember to install dependencies for the correct toolchain. It is however not recommended to
+use msys2 builds as release builds because the libraries provided by msys2 come with a sh*t
+load of unnecessary features enabled for QMidiPlayer.
diff --git a/include/qmpcorepublic.hpp b/include/qmpcorepublic.hpp
index 9e3e9e7..4fc90d8 100644
--- a/include/qmpcorepublic.hpp
+++ b/include/qmpcorepublic.hpp
@@ -77,6 +77,7 @@ public:
struct PlaybackStatus
{
bool paused;
+ bool stopped;
uint64_t curtime_ms;
uint64_t maxtime_ms;
uint64_t curtick;
@@ -153,6 +154,17 @@ public:
return "";
}
};
+enum PlaybackControlCommand
+{
+ Pause,
+ Play,
+ TogglePause,
+ Stop,
+ Seek, //uint32_t *percent
+ SeekAbs, //double *second
+ NextTrack,
+ PrevTrack
+};
#ifdef QMP_MAIN
extern "C" {
#endif
@@ -176,23 +188,34 @@ extern "C" {
virtual PlaybackStatus getPlaybackStatus() = 0;
virtual int getChannelCC(int ch, int cc) = 0;
virtual int getChannelPreset(int ch) = 0;
+ //Deprecated. Use playbackControl(Seek, (double*)&percentage) instead.
+ //Removing in 0.9.x.
virtual void playerSeek(uint32_t percentage) = 0;
virtual double getPitchBend(int ch) = 0;
virtual void getPitchBendRaw(int ch, uint32_t *pb, uint32_t *pbr) = 0;
virtual bool getChannelMask(int ch) = 0;
virtual std::string getTitle() = 0;
virtual std::wstring getWTitle() = 0;
+ virtual std::string getFilePath() = 0;
+ virtual std::wstring getWFilePath() = 0;
virtual std::string getChannelPresetString(int ch) = 0;
virtual bool isDarkTheme() = 0;
+ //Returns the qmpMainWindow instance. Use this as the parent widget
+ //if your plugin wants to create a child window.
+ //Do NOT call any non-virtual public functions through this pointer
+ //in the plugin.
+ //EXCEPTION: If your plugin links against Qt directly, you may call
+ //non-virtual members of the returned object inherited from Qt.
virtual void *getMainWindow() = 0;
+ virtual void playbackControl(PlaybackControlCommand cmd, void* data) = 0;
//WARNING!!: This function should be called from event reader callbacks only and
- //it is somehow dangerous -- other plugins might be unaware of the removal of the
- //event. The design might be modified afterward.
+ //it is somewhat dangerous -- other plugins might be unaware of the removal of the
+ //event. The design might be modified later.
virtual void discardCurrentEvent() = 0;
//WARNING!!: This function should be called from event reader callbacks only and
- //it is somehow dangerous -- other plugins might be unaware of the event change.
- //The design might be modified afterward.
+ //it is somewhat dangerous -- other plugins might be unaware of the event change.
+ //The design might be modified later.
virtual void commitEventChange(SEvent d) = 0;
//This function should be called from a file reader when it has read a new event
virtual void callEventReaderCB(SEvent d) = 0;
diff --git a/mpris-plugin/CMakeLists.txt b/mpris-plugin/CMakeLists.txt
new file mode 100644
index 0000000..c0395c0
--- /dev/null
+++ b/mpris-plugin/CMakeLists.txt
@@ -0,0 +1,30 @@
+set(qmpmpris_SOURCES
+ qmpmpris.hpp
+ qmprisdbusinterface.hpp
+ qmpriswrapper.hpp
+ qmpmprisimpl.hpp
+ qmpmpris.cpp
+ qmprisdbusinterface.cpp
+ qmpriswrapper.cpp
+ qmpmprisimpl.cpp
+)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+find_package(Qt5 REQUIRED COMPONENTS DBus)
+
+include_directories(${PROJECT_SOURCE_DIR}/include/)
+
+add_library(qmpmpris MODULE
+ ${qmpmpris_SOURCES}
+)
+
+target_link_libraries(qmpmpris
+ Qt5::Core
+ Qt5::Widgets
+ Qt5::DBus
+)
+
+install(TARGETS qmpmpris LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/qmidiplayer/)
diff --git a/mpris-plugin/qmpmpris.cpp b/mpris-plugin/qmpmpris.cpp
new file mode 100644
index 0000000..2eb18fd
--- /dev/null
+++ b/mpris-plugin/qmpmpris.cpp
@@ -0,0 +1,29 @@
+#include <cstdio>
+#include "qmpmpris.hpp"
+#include "qmpriswrapper.hpp"
+#include "qmpmprisimpl.hpp"
+
+qmpMPrisPlugin::qmpMPrisPlugin(qmpPluginAPI *_api)
+{
+ api = _api;
+}
+qmpMPrisPlugin::~qmpMPrisPlugin()
+{
+ api = nullptr;
+}
+void qmpMPrisPlugin::init()
+{
+ mw = QMPrisWrapper::create<QMPPlayer, QMPMediaPlayer2, QMPTrackList>("qmidiplayer", api);
+}
+void qmpMPrisPlugin::deinit()
+{
+ delete mw;
+}
+const char *qmpMPrisPlugin::pluginGetName()
+{
+ return "QMidiPlayer MPris Support";
+}
+const char *qmpMPrisPlugin::pluginGetVersion()
+{
+ return "0.8.8";
+}
diff --git a/mpris-plugin/qmpmpris.hpp b/mpris-plugin/qmpmpris.hpp
new file mode 100644
index 0000000..fbc8bfa
--- /dev/null
+++ b/mpris-plugin/qmpmpris.hpp
@@ -0,0 +1,32 @@
+#ifndef QMPMPRIS_HPP
+#define QMPMPRIS_HPP
+
+#include "../include/qmpcorepublic.hpp"
+
+class QMPrisWrapper;
+class qmpMPrisPlugin: public qmpPluginIntf
+{
+private:
+ qmpPluginAPI *api;
+ QMPrisWrapper *mw = nullptr;
+public:
+ qmpMPrisPlugin(qmpPluginAPI *_api);
+ ~qmpMPrisPlugin();
+ void init();
+ void deinit();
+ const char *pluginGetName();
+ const char *pluginGetVersion();
+};
+
+extern "C" {
+ EXPORTSYM qmpPluginIntf *qmpPluginGetInterface(qmpPluginAPI *api)
+ {
+ return new qmpMPrisPlugin(api);
+ }
+ EXPORTSYM const char *qmpPluginGetAPIRev()
+ {
+ return QMP_PLUGIN_API_REV;
+ }
+}
+
+#endif
diff --git a/mpris-plugin/qmpmprisimpl.cpp b/mpris-plugin/qmpmprisimpl.cpp
new file mode 100644
index 0000000..335a638
--- /dev/null
+++ b/mpris-plugin/qmpmprisimpl.cpp
@@ -0,0 +1,206 @@
+#include <QMetaEnum>
+
+#include "../include/qmpcorepublic.hpp"
+#include "../qmidiplayer-desktop/qmpmainwindow.hpp"
+#include "qmpmprisimpl.hpp"
+
+inline QVariantMap get_metadata(qmpPluginAPI *api)
+{
+ ::PlaybackStatus ps = api->getPlaybackStatus();
+ return {
+ {"mpris:trackid", QDBusObjectPath("/org/chrisoft/qmidiplayer/dummylist/0")},
+ {"xesam:url", QString::fromStdWString(api->getWFilePath())},
+ {"xesam:title", QString::fromStdWString(api->getWTitle())},
+ {"mpris:length", qlonglong(ps.maxtime_ms * 1000)}
+ };
+}
+
+QMPPlayer::QMPPlayer(qmpPluginAPI *_api, QObject *parent) :
+ api(_api),
+ QMPrisPlayer(parent)
+{
+ qmw = static_cast<qmpMainWindow*>(api->getMainWindow());
+}
+
+QString QMPPlayer::getPlaybackStatus()
+{
+ ::PlaybackStatus ps = api->getPlaybackStatus();
+ QMPrisPlayer::PlaybackStatus r = QMPrisPlayer::PlaybackStatus::Stopped;
+ if (!ps.stopped)
+ r = ps.paused ? QMPrisPlayer::PlaybackStatus::Paused : QMPrisPlayer::PlaybackStatus::Playing;
+ return QMetaEnum::fromType<QMPrisPlayer::PlaybackStatus>().key(r);
+}
+
+QString QMPPlayer::getLoopStatus()
+{
+ return QMetaEnum::fromType<QMPrisPlayer::LoopStatus>().key(QMPrisPlayer::LoopStatus::None);
+}
+
+double QMPPlayer::getRate()
+{
+ return 1;
+}
+
+bool QMPPlayer::getShuffle()
+{
+ return false;
+}
+
+QVariantMap QMPPlayer::getMetadata()
+{
+ ::PlaybackStatus ps = api->getPlaybackStatus();
+ if (ps.stopped) return {};
+ return get_metadata(api);
+}
+
+qlonglong QMPPlayer::getPosition()
+{
+ ::PlaybackStatus ps = api->getPlaybackStatus();
+ fprintf(stderr, "%lu\n", ps.curtime_ms);
+ return ps.curtime_ms * 1000;
+}
+
+bool QMPPlayer::getCanGoNext()
+{
+ return getCanPlay();
+}
+
+bool QMPPlayer::getCanGoPrevious()
+{
+ return getCanPlay();
+}
+
+bool QMPPlayer::getCanPlay()
+{
+ ::PlaybackStatus ps = api->getPlaybackStatus();
+ return !ps.stopped;
+}
+
+bool QMPPlayer::getCanPause()
+{
+ ::PlaybackStatus ps = api->getPlaybackStatus();
+ return !ps.stopped && !ps.paused;
+}
+
+bool QMPPlayer::getCanSeek()
+{
+ return getCanPlay();
+}
+
+bool QMPPlayer::getCanControl()
+{
+ return true;
+}
+
+void QMPPlayer::Pause()
+{
+ api->playbackControl(PlaybackControlCommand::Pause, nullptr);
+}
+
+void QMPPlayer::PlayPause()
+{
+ api->playbackControl(PlaybackControlCommand::TogglePause, nullptr);
+}
+
+void QMPPlayer::Stop()
+{
+ api->playbackControl(PlaybackControlCommand::Stop, nullptr);
+}
+
+void QMPPlayer::Play()
+{
+ api->playbackControl(PlaybackControlCommand::Play, nullptr);
+}
+
+void QMPPlayer::Next()
+{
+ api->playbackControl(PlaybackControlCommand::NextTrack, nullptr);
+}
+
+void QMPPlayer::Previous()
+{
+ api->playbackControl(PlaybackControlCommand::PrevTrack, nullptr);
+}
+
+void QMPPlayer::Seek(qlonglong t)
+{
+ double td = t / 1e6;
+ api->playbackControl(PlaybackControlCommand::SeekAbs, &td);
+}
+
+void QMPPlayer::SetPosition(QDBusObjectPath o, qlonglong t)
+{
+ if (o.path() == QString("/org/chrisoft/qmidiplayer/dummylist/0"))
+ {
+ double td = t / 1e6;
+ api->playbackControl(PlaybackControlCommand::SeekAbs, &td);
+ }
+}
+
+QMPMediaPlayer2::QMPMediaPlayer2(qmpPluginAPI *_api, QObject *parent) :
+ api(_api),
+ QMPrisMediaPlayer2(parent)
+{
+ qmw = static_cast<qmpMainWindow*>(api->getMainWindow());
+}
+
+void QMPMediaPlayer2::Raise()
+{
+ qmw->raise();
+}
+
+void QMPMediaPlayer2::Quit()
+{
+ qmw->close();
+}
+
+bool QMPMediaPlayer2::getCanQuit()
+{
+ return true;
+}
+
+bool QMPMediaPlayer2::getCanRaise()
+{
+ return true;
+}
+
+QString QMPMediaPlayer2::getIdentity()
+{
+ return QString("QMidiPlayer");
+}
+
+QString QMPMediaPlayer2::getDesktopEntry()
+{
+ return QString("qmidiplayer");
+}
+
+bool QMPMediaPlayer2::getHasTrackList()
+{
+ return true;
+}
+
+QMPTrackList::QMPTrackList(qmpPluginAPI *_api, QObject *parent) :
+ api(_api),
+ QMPrisTrackList(parent)
+{
+}
+
+QList<QVariantMap> QMPTrackList::GetTracksMetaData(QList<QDBusObjectPath> trackIds)
+{
+ QList<QVariantMap> ret;
+ for (auto &i : trackIds)
+ {
+ if (i.path() == QString("/org/chrisoft/qmidiplayer/dummylist/0"))
+ ret.push_back(get_metadata(api));
+ else ret.push_back({});
+ }
+ return ret;
+}
+
+QList<QDBusObjectPath> QMPTrackList::getTracks()
+{
+ ::PlaybackStatus ps = api->getPlaybackStatus();
+ if (ps.stopped)
+ return {};
+ return {QDBusObjectPath("/org/chrisoft/qmidiplayer/dummylist/0")};
+}
diff --git a/mpris-plugin/qmpmprisimpl.hpp b/mpris-plugin/qmpmprisimpl.hpp
new file mode 100644
index 0000000..b9dec93
--- /dev/null
+++ b/mpris-plugin/qmpmprisimpl.hpp
@@ -0,0 +1,72 @@
+#ifndef QMPMPRISIMPL_HPP
+#define QMPMPRISIMPL_HPP
+
+#include "qmprisdbusinterface.hpp"
+
+class qmpPluginAPI;
+class qmpMainWindow;
+
+class QMPPlayer : public QMPrisPlayer
+{
+public:
+ explicit QMPPlayer(qmpPluginAPI *_api, QObject *parent=nullptr);
+
+ QString getPlaybackStatus();
+ QString getLoopStatus();
+ double getRate();
+ bool getShuffle();
+ QVariantMap getMetadata();
+ //double getVolume();
+ qlonglong getPosition();
+ //double getMinimumRate();
+ //double getMaximumRate();
+ bool getCanGoNext();
+ bool getCanGoPrevious();
+ bool getCanPlay();
+ bool getCanPause();
+ bool getCanSeek();
+ bool getCanControl();
+
+ void Pause();
+ void PlayPause();
+ void Stop();
+ void Play();
+ void Next();
+ void Previous();
+ void Seek(qlonglong t);
+ void SetPosition(QDBusObjectPath o, qlonglong t);
+private:
+ qmpPluginAPI *api;
+ qmpMainWindow *qmw;
+};
+
+class QMPTrackList : public QMPrisTrackList
+{
+public:
+ explicit QMPTrackList(qmpPluginAPI *_api, QObject *parent=nullptr);
+
+ QList<QVariantMap> GetTracksMetaData(QList<QDBusObjectPath> trackIds);
+ QList<QDBusObjectPath> getTracks();
+private:
+ qmpPluginAPI *api;
+};
+
+class QMPMediaPlayer2 : public QMPrisMediaPlayer2
+{
+public:
+ explicit QMPMediaPlayer2(qmpPluginAPI *_api, QObject *parent=nullptr);
+
+ void Raise() override;
+ void Quit() override;
+
+ bool getCanQuit() override;
+ bool getCanRaise() override;
+ QString getIdentity() override;
+ QString getDesktopEntry() override;
+ bool getHasTrackList() override;
+private:
+ qmpPluginAPI *api;
+ qmpMainWindow *qmw;
+};
+
+#endif
diff --git a/mpris-plugin/qmprisdbusinterface.cpp b/mpris-plugin/qmprisdbusinterface.cpp
new file mode 100644
index 0000000..822a705
--- /dev/null
+++ b/mpris-plugin/qmprisdbusinterface.cpp
@@ -0,0 +1,200 @@
+#include "qmprisdbusinterface.hpp"
+#include "qmpriswrapper.hpp"
+
+#include <QMetaEnum>
+
+QMPrisPlayer::QMPrisPlayer(QObject* parent) : QDBusAbstractAdaptor(parent)
+{
+}
+
+QString QMPrisPlayer::getPlaybackStatus()
+{
+ /*if (par->pbstgetter)
+ return QMetaEnum::fromType<QMPrisWrapper::PlaybackStatus>().key(par->pbstgetter());*/
+ return QString();
+}
+QString QMPrisPlayer::getLoopStatus()
+{
+ /*if (par->lpstgetter)
+ return QMetaEnum::fromType<QMPrisWrapper::LoopStatus>().key(par->lpstgetter());*/
+ return QString();
+}
+double QMPrisPlayer::getRate()
+{
+ return 1.0;
+}
+bool QMPrisPlayer::getShuffle()
+{
+ return false;
+}
+QVariantMap QMPrisPlayer::getMetadata()
+{
+ return {};
+}
+double QMPrisPlayer::getVolume()
+{
+ return 1.0;
+}
+qlonglong QMPrisPlayer::getPosition()
+{
+ return 0;
+}
+double QMPrisPlayer::getMinimumRate()
+{
+ return 1;
+}
+double QMPrisPlayer::getMaximumRate()
+{
+ return 1;
+}
+bool QMPrisPlayer::getCanGoNext()
+{
+ return false;
+}
+bool QMPrisPlayer::getCanGoPrevious()
+{
+ return false;
+}
+bool QMPrisPlayer::getCanPlay()
+{
+ return false;
+}
+bool QMPrisPlayer::getCanPause()
+{
+ return false;
+}
+bool QMPrisPlayer::getCanSeek()
+{
+ return false;
+}
+bool QMPrisPlayer::getCanControl()
+{
+ return false;
+}
+
+void QMPrisPlayer::setLoopStatus(QString loopStatus)
+{
+}
+void QMPrisPlayer::setRate(double playbackRate)
+{
+}
+bool QMPrisPlayer::setShuffle(bool shuffle)
+{
+ return false;
+}
+void QMPrisPlayer::setVolume(double volume)
+{
+}
+
+void QMPrisPlayer::Next()
+{
+}
+void QMPrisPlayer::Previous()
+{
+}
+void QMPrisPlayer::Pause()
+{
+}
+void QMPrisPlayer::PlayPause()
+{
+}
+void QMPrisPlayer::Stop()
+{
+}
+void QMPrisPlayer::Play()
+{
+}
+void QMPrisPlayer::Seek(qlonglong t)
+{
+}
+
+void QMPrisPlayer::SetPosition(QDBusObjectPath o, qlonglong t)
+{
+}
+void QMPrisPlayer::OpenUri(QString s)
+{
+}
+
+void QMPrisMediaPlayer2::Raise()
+{
+}
+void QMPrisMediaPlayer2::Quit()
+{
+}
+
+QMPrisMediaPlayer2::QMPrisMediaPlayer2(QObject *parent) : QDBusAbstractAdaptor(parent)
+{
+}
+
+bool QMPrisMediaPlayer2::getCanQuit()
+{
+ return false;
+}
+bool QMPrisMediaPlayer2::getCanRaise()
+{
+ return false;
+}
+bool QMPrisMediaPlayer2::getFullscreen()
+{
+ return false;
+}
+bool QMPrisMediaPlayer2::getCanSetFullscreen()
+{
+ return false;
+}
+bool QMPrisMediaPlayer2::getHasTrackList()
+{
+ return false;
+}
+
+QString QMPrisMediaPlayer2::getIdentity()
+{
+ return QString();
+}
+QString QMPrisMediaPlayer2::getDesktopEntry()
+{
+ return QString();
+}
+QStringList QMPrisMediaPlayer2::getSupportedUriSchemes()
+{
+ return {};
+}
+QStringList QMPrisMediaPlayer2::getSupportedMimeTypes()
+{
+ return {};
+}
+
+void QMPrisMediaPlayer2::setFullscreen(bool fullscreen)
+{
+}
+
+QList<QVariantMap> QMPrisTrackList::GetTracksMetaData(QList<QDBusObjectPath> trackIds)
+{
+ return {};
+}
+
+void QMPrisTrackList::AddTrack(QString uri, QDBusObjectPath after, bool setCurrent)
+{
+}
+
+void QMPrisTrackList::RemoveTrack(QDBusObjectPath trackId)
+{
+}
+
+void QMPrisTrackList::GoTo(QDBusObjectPath trackId)
+{
+}
+
+QMPrisTrackList::QMPrisTrackList(QObject *parent) : QDBusAbstractAdaptor(parent)
+{
+}
+
+QList<QDBusObjectPath> QMPrisTrackList::getTracks()
+{
+ return {};
+}
+
+bool QMPrisTrackList::getCanEditTracks()
+{
+ return false;
+}
diff --git a/mpris-plugin/qmprisdbusinterface.hpp b/mpris-plugin/qmprisdbusinterface.hpp
new file mode 100644
index 0000000..071c509
--- /dev/null
+++ b/mpris-plugin/qmprisdbusinterface.hpp
@@ -0,0 +1,148 @@
+/*
+ * DBus adaptor for the MPRIS Interface
+ * Based on MPRIS D-Bus Interface Specification Version 2.2:
+ * https://specifications.freedesktop.org/mpris-spec/2.2/index.html
+ */
+#ifndef QMPRISDBUSINTERFACE_HPP
+#define QMPRISDBUSINTERFACE_HPP
+
+#include <QDBusAbstractAdaptor>
+#include <QDBusObjectPath>
+#include <QVariant>
+
+class QMPrisWrapper;
+
+class QMPrisPlayer : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player")
+
+ Q_PROPERTY(QString PlaybackStatus READ getPlaybackStatus)
+ Q_PROPERTY(QString LoopStatus READ getLoopStatus WRITE setLoopStatus)
+ Q_PROPERTY(double Rate READ getRate WRITE setRate)
+ Q_PROPERTY(bool Shuffle READ getShuffle WRITE setShuffle)
+ Q_PROPERTY(QVariantMap Metadata READ getMetadata)
+ Q_PROPERTY(double Volume READ getVolume WRITE setVolume)
+ Q_PROPERTY(qlonglong Position READ getPosition)
+ Q_PROPERTY(double MinimumRate READ getMinimumRate)
+ Q_PROPERTY(double MaximumRate READ getMaximumRate)
+
+ Q_PROPERTY(bool CanGoNext READ getCanGoNext)
+ Q_PROPERTY(bool CanGoPrevious READ getCanGoPrevious)
+ Q_PROPERTY(bool CanPlay READ getCanPlay)
+ Q_PROPERTY(bool CanPause READ getCanPause)
+ Q_PROPERTY(bool CanSeek READ getCanSeek)
+ Q_PROPERTY(bool CanControl READ getCanControl)
+public:
+ enum PlaybackStatus
+ {
+ Playing,
+ Paused,
+ Stopped
+ };
+ Q_ENUM(PlaybackStatus)
+
+ enum LoopStatus
+ {
+ None,
+ Track,
+ Playlist
+ };
+ Q_ENUM(LoopStatus)
+
+ explicit QMPrisPlayer(QObject *parent=nullptr);
+
+ virtual QString getPlaybackStatus();
+ virtual QString getLoopStatus();
+ virtual double getRate();
+ virtual bool getShuffle();
+ virtual QVariantMap getMetadata();
+ virtual double getVolume();
+ virtual qlonglong getPosition();
+ virtual double getMinimumRate();
+ virtual double getMaximumRate();
+ virtual bool getCanGoNext();
+ virtual bool getCanGoPrevious();
+ virtual bool getCanPlay();
+ virtual bool getCanPause();
+ virtual bool getCanSeek();
+ virtual bool getCanControl();
+
+ virtual void setLoopStatus(QString loopStatus);
+ virtual void setRate(double playbackRate);
+ virtual bool setShuffle(bool shuffle);
+ virtual void setVolume(double volume);
+
+public slots:
+ virtual void Next();
+ virtual void Previous();
+ virtual void Pause();
+ virtual void PlayPause();
+ virtual void Stop();
+ virtual void Play();
+ virtual void Seek(qlonglong t);
+ virtual void SetPosition(QDBusObjectPath o, qlonglong t);
+ virtual void OpenUri(QString s);
+
+signals:
+ void Seeked(qlonglong t);
+};
+
+class QMPrisTrackList : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.TrackList")
+ Q_PROPERTY(QList<QDBusObjectPath> Tracks READ getTracks)
+ Q_PROPERTY(bool CanEditTracks READ getCanEditTracks)
+public slots:
+ virtual QList<QVariantMap> GetTracksMetaData(QList<QDBusObjectPath> trackIds);
+ virtual void AddTrack(QString uri, QDBusObjectPath after, bool setCurrent);
+ virtual void RemoveTrack(QDBusObjectPath trackId);
+ virtual void GoTo(QDBusObjectPath trackId);
+signals:
+ void TrackListReplaced(QList<QDBusObjectPath> tracks, QDBusObjectPath currentTrack);
+ void TrackAdded(QVariantMap metadata, QDBusObjectPath after);
+ void TrackRemoved(QDBusObjectPath track);
+ void TrackMetadataChanged(QDBusObjectPath track, QVariantMap metadata);
+public:
+ explicit QMPrisTrackList(QObject *parent=nullptr);
+ virtual QList<QDBusObjectPath> getTracks();
+ virtual bool getCanEditTracks();
+};
+
+class QMPrisMediaPlayer2 : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2")
+
+ Q_PROPERTY(bool CanQuit READ getCanQuit)
+ Q_PROPERTY(bool CanRaise READ getCanRaise)
+ Q_PROPERTY(bool Fullscreen READ getFullscreen WRITE setFullscreen)
+ Q_PROPERTY(bool CanSetFullscreen READ getCanSetFullscreen)
+ Q_PROPERTY(bool HasTrackList READ getHasTrackList)
+ Q_PROPERTY(QString Identity READ getIdentity)
+ Q_PROPERTY(QString DesktopEntry READ getDesktopEntry)
+ Q_PROPERTY(QStringList SupportedUriSchemes READ getSupportedUriSchemes)
+ Q_PROPERTY(QStringList SupportedMimeTypes READ getSupportedMimeTypes)
+
+public slots:
+ virtual void Raise();
+ virtual void Quit();
+
+public:
+ explicit QMPrisMediaPlayer2(QObject *parent=nullptr);
+
+ virtual bool getCanQuit();
+ virtual bool getCanRaise();
+ virtual bool getFullscreen();
+ virtual bool getCanSetFullscreen();
+ virtual bool getHasTrackList();
+ virtual QString getIdentity();
+ virtual QString getDesktopEntry();
+ virtual QStringList getSupportedUriSchemes();
+ virtual QStringList getSupportedMimeTypes();
+
+ virtual void setFullscreen(bool fullscreen);
+};
+
+#endif // QMPRISDBUSINTERFACE_HPP
diff --git a/mpris-plugin/qmpriswrapper.cpp b/mpris-plugin/qmpriswrapper.cpp
new file mode 100644
index 0000000..8a3f273
--- /dev/null
+++ b/mpris-plugin/qmpriswrapper.cpp
@@ -0,0 +1,77 @@
+#include "qmpriswrapper.hpp"
+#include "qmprisdbusinterface.hpp"
+#include <QDBusMetaType>
+#include <QDBusConnection>
+#include <QDBusMessage>
+
+#include "../include/qmpcorepublic.hpp"
+
+QMPrisWrapper::QMPrisWrapper(QString serviceSuffix, qmpPluginAPI *_api, QObject *parent) :
+ api(_api),
+ QObject(parent),
+ svcsuffix(serviceSuffix)
+{
+ qDBusRegisterMetaType<QStringList>();
+ qDBusRegisterMetaType<QVariantMap>();
+ qDBusRegisterMetaType<QList<QVariantMap>>();
+ qDBusRegisterMetaType<QList<QDBusObjectPath>>();
+}
+
+void QMPrisWrapper::post_creation()
+{
+ QDBusConnection sessbus = QDBusConnection::sessionBus();
+ sessbus.registerService("org.mpris.MediaPlayer2." + svcsuffix);
+ sessbus.registerObject("/org/mpris/MediaPlayer2", this);
+ api->registerUIHook("main.stop", [this](const void* a, void* _) {
+ tracklist->TrackListReplaced({}, QDBusObjectPath("/"));
+ this->notifyPropertyChange(PLAYER_INTERFACE, "Metadata", player->getMetadata());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "PlaybackStatus", player->getPlaybackStatus());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanPause", player->getCanPause());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanPlay", player->getCanPlay());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanSeek", player->getCanSeek());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanGoNext", player->getCanGoNext());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanGoPrevious", player->getCanGoPrevious());
+ }, nullptr);
+ api->registerUIHook("main.start", [this](const void* a, void* _) {
+ tracklist->TrackListReplaced(tracklist->getTracks(), QDBusObjectPath("/org/chrisoft/qmidiplayer/dummylist/0"));
+ this->notifyPropertyChange(PLAYER_INTERFACE, "Metadata", player->getMetadata());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "PlaybackStatus", player->getPlaybackStatus());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanPause", player->getCanPause());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanPlay", player->getCanPlay());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanSeek", player->getCanSeek());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanGoNext", player->getCanGoNext());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanGoPrevious", player->getCanGoPrevious());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "Rate", player->getRate());
+ }, nullptr);
+ api->registerUIHook("main.pause", [this](const void* a, void* _) {
+ this->notifyPropertyChange(PLAYER_INTERFACE, "PlaybackStatus", player->getPlaybackStatus());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanPause", player->getCanPause());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanPlay", player->getCanPlay());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanSeek", player->getCanSeek());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanGoNext", player->getCanGoNext());
+ this->notifyPropertyChange(PLAYER_INTERFACE, "CanGoPrevious", player->getCanGoPrevious());
+ }, nullptr);
+ api->registerUIHook("main.seek", [this](const void* a, void *_) {
+ auto ps = static_cast<const ::PlaybackStatus*>(a);
+ player->Seeked(ps->curtime_ms * 1000);
+ }, nullptr);
+}
+
+QMPrisWrapper::~QMPrisWrapper()
+{
+ QDBusConnection sessbus = QDBusConnection::sessionBus();
+ sessbus.unregisterObject("/org/mpris/MediaPlayer2");
+ sessbus.unregisterService("org.mpris.MediaPlayer2." + svcsuffix);
+}
+
+void QMPrisWrapper::notifyPropertyChange(QString intf, QString prop, QVariant val)
+{
+ QDBusConnection sessbus = QDBusConnection::sessionBus();
+ auto signal = QDBusMessage::createSignal("/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged");
+ signal.setArguments({
+ intf,
+ QVariantMap{{prop, val}},
+ QStringList{}
+ });
+ sessbus.send(signal);
+}
diff --git a/mpris-plugin/qmpriswrapper.hpp b/mpris-plugin/qmpriswrapper.hpp
new file mode 100644
index 0000000..54186d2
--- /dev/null
+++ b/mpris-plugin/qmpriswrapper.hpp
@@ -0,0 +1,53 @@
+#ifndef QMPRISWRAPPER_HPP
+#define QMPRISWRAPPER_HPP
+
+#include <QObject>
+
+class QMPrisMediaPlayer2;
+class QMPrisPlayer;
+class QMPrisTrackList;
+class qmpPluginAPI;
+
+class QMPrisWrapper : public QObject
+{
+ Q_OBJECT
+public:
+ ~QMPrisWrapper();
+
+ template <class TP, class TM, class TT>
+ static QMPrisWrapper *create(QString serviceSuffix, qmpPluginAPI *api, QObject *parent = nullptr)
+ {
+ static_assert(std::is_base_of<QMPrisPlayer, TP>(), "TP must be a subclass of QMPrisPlayer");
+ static_assert(std::is_base_of<QMPrisMediaPlayer2, TM>(), "TM must be a subclass of QMPrisMediaPlayer2");
+ static_assert(std::is_base_of<QMPrisTrackList, TT>(), "TT must be a subclass of QMPrisTrackList");
+
+ auto w = new QMPrisWrapper(serviceSuffix, api, parent);
+ auto p = new TP(api, w);
+ auto t = new TT(api, w);
+ auto mp = new TM(api, w);
+ w->player = p;
+ w->tracklist = t;
+ w->mediaplayer = mp;
+ w->post_creation();
+
+ return w;
+ }
+
+ static void notifyPropertyChange(QString intf, QString prop, QVariant val);
+
+private:
+ explicit QMPrisWrapper(QString serviceSuffix, qmpPluginAPI *_api, QObject *parent = nullptr);
+ void post_creation();
+ QMPrisPlayer *player = nullptr;
+ QMPrisMediaPlayer2 *mediaplayer = nullptr;
+ QMPrisTrackList *tracklist = nullptr;
+ QString svcsuffix;
+ qmpPluginAPI *api;
+
+ const QString PLAYER_INTERFACE = "org.mpris.MediaPlayer2.Player";
+
+ friend class QMPrisPlayer;
+ friend class QMPrisMediaPlayer2;
+};
+
+#endif // QMPRISWRAPPER_HPP
diff --git a/qmidiplayer-desktop/qmpmainwindow.cpp b/qmidiplayer-desktop/qmpmainwindow.cpp
index cc498cb..c02a206 100644
--- a/qmidiplayer-desktop/qmpmainwindow.cpp
+++ b/qmidiplayer-desktop/qmpmainwindow.cpp
@@ -335,10 +335,15 @@ QString qmpMainWindow::getFileName()
{
return ui->lbFileName->text();
}
+
+QUrl qmpMainWindow::getFilePath()
+{
+ return filepath;
+}
void qmpMainWindow::switchTrack(QString s, bool interrupt)
{
- stopped = false;
- playing = true;
+ stopped = true;
+ playing = false;
setFuncEnabled("Render", stopped);
setFuncEnabled("ReloadSynth", stopped);
ui->pbPlayPause->setIcon(QIcon(getThemedIcon(":/img/pause.svg")));
@@ -348,6 +353,8 @@ void qmpMainWindow::switchTrack(QString s, bool interrupt)
player->playerPanic();
}
invokeCallback("main.stop", nullptr);
+ stopped = false;
+ playing = true;
if (playerTh)
{
auto f = std::async([this] {playerTh->join();});
@@ -367,8 +374,9 @@ void qmpMainWindow::switchTrack(QString s, bool interrupt)
chnlw->on_pbUnmute_clicked();
chnlw->on_pbUnsolo_clicked();
QString fns = s;
+ filepath = QUrl::fromLocalFile(fns);
setWindowTitle(QUrl::fromLocalFile(fns).fileName().left(QUrl::fromLocalFile(fns).fileName().lastIndexOf('.')) + " - QMidiPlayer");
- ui->lbFileName->setText(QUrl::fromLocalFile(fns).fileName().left(QUrl::fromLocalFile(fns).fileName().lastIndexOf('.')));
+ ui->lbFileName->setText(filepath.fileName().left(filepath.fileName().lastIndexOf('.')));
if (plistw->getCurrentItem() != fns)
plistw->setCurrentItem(fns);
onfnChanged();
@@ -511,7 +519,6 @@ void qmpMainWindow::registerBehaviorOptions()
void qmpMainWindow::on_pbPlayPause_clicked()
{
- playing = !playing;
if (stopped)
{
QString fns = plistw->getFirstItem();
@@ -526,8 +533,9 @@ void qmpMainWindow::on_pbPlayPause_clicked()
if (!fns.length())
return (void)(playing = false);
}
+ filepath = QUrl::fromLocalFile(fns);
setWindowTitle(QUrl::fromLocalFile(fns).fileName().left(QUrl::fromLocalFile(fns).fileName().lastIndexOf('.')) + " - QMidiPlayer");
- ui->lbFileName->setText(QUrl::fromLocalFile(fns).fileName().left(QUrl::fromLocalFile(fns).fileName().lastIndexOf('.')));
+ ui->lbFileName->setText(filepath.fileName().left(filepath.fileName().lastIndexOf('.')));
onfnChanged();
if (!loadFile(fns))
return;
@@ -535,6 +543,7 @@ void qmpMainWindow::on_pbPlayPause_clicked()
sprintf(ts, "%02d:%02d", (int)player->getFtime() / 60, (int)player->getFtime() % 60);
ui->lbFinTime->setText(ts);
player->playerInit();
+ playing = true;
PlaybackStatus ps = getPlaybackStatus();
invokeCallback("main.start", &ps);
internalfluid->setGain(ui->vsMasterVol->value() / 250.);
@@ -553,24 +562,10 @@ void qmpMainWindow::on_pbPlayPause_clicked()
offset = 0;
timer->start(UPDATE_INTERVAL);
stopped = false;
+ ui->pbPlayPause->setIcon(QIcon(getThemedIcon(":/img/play.svg")));
}
else
- {
- if (!playing)
- {
- player->playerPanic();
- offset = ui->hsTimer->value() / 100.*player->getFtime();
- }
- else
- {
- st = std::chrono::steady_clock::now();
- player->setResumed();
- }
- player->setTCpaused(!playing);
- PlaybackStatus ps = getPlaybackStatus();
- invokeCallback("main.pause", &ps);
- }
- ui->pbPlayPause->setIcon(QIcon(getThemedIcon(playing ? ":/img/pause.svg" : ":/img/play.svg")));
+ setPaused(playing);
}
void qmpMainWindow::on_hsTimer_sliderPressed()
@@ -630,7 +625,7 @@ void qmpMainWindow::playerSeek(uint32_t percentage)
player->playerPanic();
ui->hsTimer->setValue(percentage);
player->setTCeptr(player->getStamp(percentage), percentage);
- offset = percentage / 100.*player->getFtime();
+ offset = percentage / 100. * player->getFtime();
st = std::chrono::steady_clock::now();
}
else
@@ -641,7 +636,7 @@ void qmpMainWindow::playerSeek(uint32_t percentage)
return;
}
player->setTCeptr(player->getStamp(percentage), percentage);
- offset = percentage / 100.*player->getFtime();
+ offset = percentage / 100. * player->getFtime();
ui->hsTimer->setValue(percentage);
char ts[100];
sprintf(ts, "%02d:%02d", (int)(offset) / 60, (int)(offset) % 60);
@@ -655,7 +650,14 @@ PlaybackStatus qmpMainWindow::getPlaybackStatus()
{
std::chrono::duration<double> elapsed =
std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - st);
- return {!playing, uint64_t((elapsed.count() + offset) * 1000), uint64_t(player->getFtime() * 1000), player->getTick(), player->getMaxTick()};
+ return {
+ .paused=!playing,
+ .stopped=stopped,
+ .curtime_ms=stopped ? 0 : uint64_t((elapsed.count() + offset) * 1000),
+ .maxtime_ms=stopped ? 0 : uint64_t(player->getFtime() * 1000),
+ .curtick=stopped ? 0 : player->getTick(),
+ .maxtick=stopped ? 0 : player->getMaxTick()
+ };
}
void qmpMainWindow::on_vsMasterVol_valueChanged()
@@ -667,31 +669,7 @@ void qmpMainWindow::on_vsMasterVol_valueChanged()
void qmpMainWindow::on_pbStop_clicked()
{
- if (!stopped)
- {
- timer->stop();
- stopped = true;
- playing = false;
- invokeCallback("main.stop", nullptr);
- player->playerDeinit();
- setFuncEnabled("Render", stopped);
- setFuncEnabled("ReloadSynth", stopped);
- player->playerPanic();
- player->playerReset();
- if (playerTh)
- {
- playerTh->join();
- delete playerTh;
- playerTh = nullptr;
- }
- chnlw->on_pbUnmute_clicked();
- chnlw->on_pbUnsolo_clicked();
- ui->pbPlayPause->setIcon(QIcon(getThemedIcon(":/img/play.svg")));
- ui->hsTimer->setValue(0);
- ui->lbCurPoly->setText("00000");
- ui->lbMaxPoly->setText("00000");
- ui->lbCurTime->setText("00:00");
- }
+ stop();
}
void qmpMainWindow::dialogClosed()
@@ -702,12 +680,12 @@ void qmpMainWindow::dialogClosed()
void qmpMainWindow::on_pbPrev_clicked()
{
- switchTrack(plistw->getPrevItem());
+ prevTrack();
}
void qmpMainWindow::on_pbNext_clicked()
{
- switchTrack(plistw->getNextItem());
+ nextTrack();
}
void qmpMainWindow::selectionChanged()
@@ -854,6 +832,66 @@ std::map<std::string, qmpFuncPrivate> &qmpMainWindow::getFunc()
return mfunc;
}
+void qmpMainWindow::setPaused(bool paused)
+{
+ if (stopped)
+ return;
+ playing = !paused;
+ if (paused)
+ {
+ player->playerPanic();
+ offset = ui->hsTimer->value() / 100. * player->getFtime();
+ }
+ else
+ {
+ st = std::chrono::steady_clock::now();
+ player->setResumed();
+ }
+ player->setTCpaused(paused);
+ PlaybackStatus ps = getPlaybackStatus();
+ invokeCallback("main.pause", &ps);
+ ui->pbPlayPause->setIcon(QIcon(getThemedIcon(paused ? ":/img/play.svg" : ":/img/pause.svg")));
+}
+
+void qmpMainWindow::nextTrack()
+{
+ switchTrack(plistw->getNextItem());
+}
+
+void qmpMainWindow::prevTrack()
+{
+ switchTrack(plistw->getPrevItem());
+}
+
+void qmpMainWindow::stop()
+{
+ if (!stopped)
+ {
+ timer->stop();
+ stopped = true;
+ playing = false;
+ invokeCallback("main.stop", nullptr);
+ player->playerDeinit();
+ setFuncEnabled("Render", stopped);
+ setFuncEnabled("ReloadSynth", stopped);
+ player->playerPanic();
+ player->playerReset();
+ if (playerTh)
+ {
+ playerTh->join();
+ delete playerTh;
+ playerTh = nullptr;
+ }
+ chnlw->on_pbUnmute_clicked();
+ chnlw->on_pbUnsolo_clicked();
+ ui->pbPlayPause->setIcon(QIcon(getThemedIcon(":/img/play.svg")));
+ ui->hsTimer->setValue(0);
+ ui->lbCurPoly->setText("00000");
+ ui->lbMaxPoly->setText("00000");
+ ui->lbCurTime->setText("00:00");
+ }
+}
+
void qmpMainWindow::setupWidget()
{
for (auto i = mfunc.begin(); i != mfunc.end(); ++i)
diff --git a/qmidiplayer-desktop/qmpmainwindow.hpp b/qmidiplayer-desktop/qmpmainwindow.hpp
index 99886ba..7a34cb6 100644
--- a/qmidiplayer-desktop/qmpmainwindow.hpp
+++ b/qmidiplayer-desktop/qmpmainwindow.hpp
@@ -223,6 +223,7 @@ public:
return fin;
}
QString getFileName();
+ QUrl getFilePath();
void switchTrack(QString s, bool interrupt = true);
std::string getTitle();
std::wstring getWTitle();
@@ -244,6 +245,10 @@ public:
void setupWidget();
void invokeCallback(std::string cat, void *callerdat);
std::map<std::string, qmpFuncPrivate> &getFunc();
+ void setPaused(bool paused);
+ void nextTrack();
+ void prevTrack();
+ void stop();
private slots:
void on_pbPlayPause_clicked();
@@ -273,6 +278,7 @@ private:
std::chrono::steady_clock::time_point st;
double offset;
CMidiPlayer *player;
+ QUrl filepath;
qmpMidiOutFluid *internalfluid;
qmpFileRendererFluid *fluidrenderer;
qmpPluginManager *pmgr;
diff --git a/qmidiplayer-desktop/qmpplugin.cpp b/qmidiplayer-desktop/qmpplugin.cpp
index c52f486..7efd345 100644
--- a/qmidiplayer-desktop/qmpplugin.cpp
+++ b/qmidiplayer-desktop/qmpplugin.cpp
@@ -261,6 +261,16 @@ std::wstring qmpPluginAPIImpl::getWTitle()
{
return qmw ? qmw->getWTitle() : L"";
}
+
+std::string qmpPluginAPIImpl::getFilePath()
+{
+ return qmw ? qmw->getFilePath().toString().toStdString() : "";
+}
+
+std::wstring qmpPluginAPIImpl::getWFilePath()
+{
+ return qmw ? qmw->getFilePath().toString().toStdWString() : L"";
+}
std::string qmpPluginAPIImpl::getChannelPresetString(int ch)
{
uint16_t b;
@@ -290,6 +300,42 @@ void *qmpPluginAPIImpl::getMainWindow()
return (void *)qmw;
}
+void qmpPluginAPIImpl::playbackControl(PlaybackControlCommand cmd, void *data)
+{
+ if (!qmw) return;
+ switch (cmd)
+ {
+ case PlaybackControlCommand::Pause:
+ qmw->setPaused(true);
+ break;
+ case PlaybackControlCommand::Play:
+ qmw->setPaused(false);
+ break;
+ case PlaybackControlCommand::TogglePause:
+ qmw->setPaused(!qmw->getPlaybackStatus().paused);
+ break;
+ case PlaybackControlCommand::Stop:
+ qmw->stop();
+ break;
+ case PlaybackControlCommand::Seek:
+ qmw->playerSeek(*static_cast<uint32_t*>(data));
+ break;
+ case PlaybackControlCommand::SeekAbs:
+ {
+ double t = *static_cast<double*>(data);
+ uint32_t p = 100. * t / (qmw->getPlaybackStatus().maxtime_ms / 1000.);
+ qmw->playerSeek(p);
+ }
+ break;
+ case PlaybackControlCommand::NextTrack:
+ qmw->nextTrack();
+ break;
+ case PlaybackControlCommand::PrevTrack:
+ qmw->prevTrack();
+ break;
+ }
+}
+
void qmpPluginAPIImpl::discardCurrentEvent()
{
if (qmw && qmw->getPlayer())
diff --git a/qmidiplayer-desktop/qmpplugin.hpp b/qmidiplayer-desktop/qmpplugin.hpp
index b7a5e06..95ccbe4 100644
--- a/qmidiplayer-desktop/qmpplugin.hpp
+++ b/qmidiplayer-desktop/qmpplugin.hpp
@@ -46,9 +46,12 @@ public:
bool getChannelMask(int ch);
std::string getTitle();
std::wstring getWTitle();
+ std::string getFilePath();
+ std::wstring getWFilePath();
std::string getChannelPresetString(int ch);
bool isDarkTheme();
void *getMainWindow();
+ void playbackControl(PlaybackControlCommand cmd, void* data);
void discardCurrentEvent();
void commitEventChange(SEvent d);
diff --git a/visualization/qmpvisualization.cpp b/visualization/qmpvisualization.cpp
index db896fd..8212503 100644
--- a/visualization/qmpvisualization.cpp
+++ b/visualization/qmpvisualization.cpp
@@ -904,9 +904,17 @@ bool qmpVisualization::update()
if (!rendermode)
{
if (sm->smGetKeyState(SMK_RIGHT) == SMKST_HIT)
- api->playerSeek(api->getCurrentPlaybackPercentage() + (sm->smGetKeyState(SMK_SHIFT) ? 5 : 1));
+ {
+ auto p = api->getCurrentPlaybackPercentage();
+ p += (sm->smGetKeyState(SMK_SHIFT) ? 5 : 1);
+ api->playbackControl(PlaybackControlCommand::Seek, &p);
+ }
if (sm->smGetKeyState(SMK_LEFT) == SMKST_HIT)
- api->playerSeek(api->getCurrentPlaybackPercentage() - (sm->smGetKeyState(SMK_SHIFT) ? 5 : 1));
+ {
+ auto p = api->getCurrentPlaybackPercentage();
+ p -= (sm->smGetKeyState(SMK_SHIFT) ? 5 : 1);
+ api->playbackControl(PlaybackControlCommand::Seek, &p);
+ }
if (sm->smGetKeyState(SMK_B) == SMKST_HIT)
debug ^= 1;
}
diff --git a/visualization/renderer/qmppluginapistub.cpp b/visualization/renderer/qmppluginapistub.cpp
index 6c6424c..7804cc6 100644
--- a/visualization/renderer/qmppluginapistub.cpp
+++ b/visualization/renderer/qmppluginapistub.cpp
@@ -98,6 +98,16 @@ std::wstring qmpPluginAPIStub::getWTitle()
core->settings()->getOptionEnumIntOptName("Midi/TextEncoding").c_str())->
toUnicode(core->player->getTitle()).toStdWString();
}
+
+std::string qmpPluginAPIStub::getFilePath()
+{
+ return "";
+}
+
+std::wstring qmpPluginAPIStub::getWFilePath()
+{
+ return L"";
+}
std::string qmpPluginAPIStub::getChannelPresetString(int ch)
{
return std::string();
@@ -110,6 +120,8 @@ void *qmpPluginAPIStub::getMainWindow()
{
return nullptr;
}
+
+void qmpPluginAPIStub::playbackControl(PlaybackControlCommand cmd, void *data) {}
void qmpPluginAPIStub::discardCurrentEvent() {}
void qmpPluginAPIStub::commitEventChange(SEvent d) {}
void qmpPluginAPIStub::callEventReaderCB(SEvent d) {}
diff --git a/visualization/renderer/qmppluginapistub.hpp b/visualization/renderer/qmppluginapistub.hpp
index ede08d3..675a306 100644
--- a/visualization/renderer/qmppluginapistub.hpp
+++ b/visualization/renderer/qmppluginapistub.hpp
@@ -29,9 +29,12 @@ public:
bool getChannelMask(int ch);
std::string getTitle();
std::wstring getWTitle();
+ std::string getFilePath();
+ std::wstring getWFilePath();
std::string getChannelPresetString(int ch);
bool isDarkTheme();
void *getMainWindow();
+ void playbackControl(PlaybackControlCommand cmd, void* data);
void discardCurrentEvent();
void commitEventChange(SEvent d);