diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | INSTALL.md | 7 | ||||
-rw-r--r-- | include/qmpcorepublic.hpp | 31 | ||||
-rw-r--r-- | mpris-plugin/CMakeLists.txt | 30 | ||||
-rw-r--r-- | mpris-plugin/qmpmpris.cpp | 29 | ||||
-rw-r--r-- | mpris-plugin/qmpmpris.hpp | 32 | ||||
-rw-r--r-- | mpris-plugin/qmpmprisimpl.cpp | 206 | ||||
-rw-r--r-- | mpris-plugin/qmpmprisimpl.hpp | 72 | ||||
-rw-r--r-- | mpris-plugin/qmprisdbusinterface.cpp | 200 | ||||
-rw-r--r-- | mpris-plugin/qmprisdbusinterface.hpp | 148 | ||||
-rw-r--r-- | mpris-plugin/qmpriswrapper.cpp | 77 | ||||
-rw-r--r-- | mpris-plugin/qmpriswrapper.hpp | 53 | ||||
-rw-r--r-- | qmidiplayer-desktop/qmpmainwindow.cpp | 140 | ||||
-rw-r--r-- | qmidiplayer-desktop/qmpmainwindow.hpp | 6 | ||||
-rw-r--r-- | qmidiplayer-desktop/qmpplugin.cpp | 46 | ||||
-rw-r--r-- | qmidiplayer-desktop/qmpplugin.hpp | 3 | ||||
-rw-r--r-- | visualization/qmpvisualization.cpp | 12 | ||||
-rw-r--r-- | visualization/renderer/qmppluginapistub.cpp | 12 | ||||
-rw-r--r-- | visualization/renderer/qmppluginapistub.hpp | 3 |
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) @@ -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); |