Skip to content

Commit

Permalink
The 2 year constipation. (mpris plugin)
Browse files Browse the repository at this point in the history
Probably buggy as hell.
  • Loading branch information
chirs241097 committed Nov 26, 2023
1 parent 382d85b commit 60989e5
Show file tree
Hide file tree
Showing 19 changed files with 1,050 additions and 58 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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.
31 changes: 27 additions & 4 deletions include/qmpcorepublic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class CMidiFile
struct PlaybackStatus
{
bool paused;
bool stopped;
uint64_t curtime_ms;
uint64_t maxtime_ms;
uint64_t curtick;
Expand Down Expand Up @@ -153,6 +154,17 @@ class qmpPluginIntf
return "";
}
};
enum PlaybackControlCommand
{
Pause,
Play,
TogglePause,
Stop,
Seek, //uint32_t *percent
SeekAbs, //double *second
NextTrack,
PrevTrack
};
#ifdef QMP_MAIN
extern "C" {
#endif
Expand All @@ -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;
Expand Down
30 changes: 30 additions & 0 deletions mpris-plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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/)
29 changes: 29 additions & 0 deletions mpris-plugin/qmpmpris.cpp
Original file line number Diff line number Diff line change
@@ -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";
}
32 changes: 32 additions & 0 deletions mpris-plugin/qmpmpris.hpp
Original file line number Diff line number Diff line change
@@ -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
206 changes: 206 additions & 0 deletions mpris-plugin/qmpmprisimpl.cpp
Original file line number Diff line number Diff line change
@@ -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")};
}
Loading

0 comments on commit 60989e5

Please sign in to comment.