Initial commit for Waybar JACK monitoring module

-DSP load
  -xruns
  -connected/disconnected state
  -only tested with Pipewire so far but should work with JACK2 as well

 On branch dsp
 Changes to be committed:
	modified:   include/factory.hpp
	new file:   include/modules/jack.hpp
	modified:   meson.build
	modified:   meson_options.txt
	modified:   src/factory.cpp
	new file:   src/modules/jack.cpp
This commit is contained in:
Kenny Phelps-McKeown 2022-02-09 02:53:52 -05:00
parent 12caae8fd2
commit d5c400c0cc
6 changed files with 175 additions and 0 deletions

View File

@ -62,6 +62,9 @@
# include "modules/bluetooth.hpp" # include "modules/bluetooth.hpp"
# endif # endif
#endif #endif
#ifdef HAVE_LIBJACK
#include "modules/jack.hpp"
#endif
namespace waybar { namespace waybar {

29
include/modules/jack.hpp Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <fmt/format.h>
#include <fstream>
#include <jack/jack.h>
#include <proc/readproc.h>
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
class JACK : public ALabel {
public:
JACK(const std::string&, const Json::Value&);
~JACK() = default;
auto update() -> void;
jack_nframes_t bufsize_;
jack_client_t* client_;
unsigned int xruns_;
std::string state_;
private:
std::string JACKState();
jack_nframes_t samplerate_;
util::SleeperThread thread_;
};
} // namespace waybar::modules

View File

@ -97,6 +97,8 @@ libudev = dependency('libudev', required: get_option('libudev'))
libevdev = dependency('libevdev', required: get_option('libevdev')) libevdev = dependency('libevdev', required: get_option('libevdev'))
libmpdclient = dependency('libmpdclient', required: get_option('mpd')) libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
xkbregistry = dependency('xkbregistry') xkbregistry = dependency('xkbregistry')
libjack = dependency('jack', required: get_option('jack'))
libprocps = dependency('libprocps', required: get_option('jack'))
libsndio = compiler.find_library('sndio', required: get_option('sndio')) libsndio = compiler.find_library('sndio', required: get_option('sndio'))
if libsndio.found() if libsndio.found()
@ -207,6 +209,11 @@ if libpulse.found()
src_files += 'src/modules/pulseaudio.cpp' src_files += 'src/modules/pulseaudio.cpp'
endif endif
if libjack.found() and libprocps.found()
add_project_arguments('-DHAVE_LIBJACK', language: 'cpp')
src_files += 'src/modules/jack.cpp'
endif
if dbusmenu_gtk.found() if dbusmenu_gtk.found()
add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp') add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp')
src_files += files( src_files += files(
@ -288,6 +295,8 @@ executable(
libnl, libnl,
libnlgen, libnlgen,
libpulse, libpulse,
libjack,
libprocps,
libudev, libudev,
libepoll, libepoll,
libmpdclient, libmpdclient,

View File

@ -13,3 +13,5 @@ option('sndio', type: 'feature', value: 'auto', description: 'Enable support for
option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind') option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind')
option('tests', type: 'feature', value: 'auto', description: 'Enable tests') option('tests', type: 'feature', value: 'auto', description: 'Enable tests')
option('experimental', type : 'boolean', value : false, description: 'Enable experimental features') option('experimental', type : 'boolean', value : false, description: 'Enable experimental features')
option('jack', type: 'feature', value: 'auto', description: 'Enable support for JACK DSP load monitoring')

View File

@ -85,6 +85,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
return new waybar::modules::Pulseaudio(id, config_[name]); return new waybar::modules::Pulseaudio(id, config_[name]);
} }
#endif #endif
#ifdef HAVE_LIBJACK
if (ref == "jack") {
return new waybar::modules::JACK(id, config_[name]);
}
#endif
#ifdef HAVE_LIBMPDCLIENT #ifdef HAVE_LIBMPDCLIENT
if (ref == "mpd") { if (ref == "mpd") {
return new waybar::modules::MPD(id, config_[name]); return new waybar::modules::MPD(id, config_[name]);

127
src/modules/jack.cpp Normal file
View File

@ -0,0 +1,127 @@
#include "modules/jack.hpp"
extern "C" {
int bufSizeCallback(unsigned int size, void *obj) {
waybar::modules::JACK* x = (waybar::modules::JACK*)obj;
x->bufsize_ = size;
return size;
}
int xrunCallback(void *obj) {
waybar::modules::JACK* x = (waybar::modules::JACK*)obj;
x->xruns_ += 1;
x->state_ = "xrun";
return 0;
}
void shutdownCallback(void *obj) {
waybar::modules::JACK* x = (waybar::modules::JACK*)obj;
x->client_ = NULL;
x->state_ = "disconnected";
x->xruns_ = 0;
}
}
waybar::modules::JACK::JACK(const std::string& id, const Json::Value& config)
: ALabel(config, "jack", id, "{load}%", 1) {
xruns_ = 0;
state_ = "disconnected";
client_ = NULL;
state_ = JACKState();
thread_ = [this] {
dp.emit();
thread_.sleep_for(interval_);
};
}
std::string waybar::modules::JACK::JACKState() {
if(state_.compare("xrun") == 0)
return "xrun";
if(state_.compare("connected") == 0)
return "connected";
std::string procname;
bool foundJACK = false;
proc_t** proctab = readproctab(PROC_FILLSTAT);
for(int i=0; proctab[i]; i++) {
procname = proctab[i]->cmd;
if(!procname.compare("jackd") || !procname.compare("pipewire"))
foundJACK = true;
freeproc(proctab[i]);
}
free(proctab);
if(!foundJACK)
return "disconnected";
client_ = jack_client_open("waybar", JackNoStartServer, NULL);
if (client_) {
bufsize_ = jack_get_buffer_size(client_);
samplerate_ = jack_get_sample_rate(client_);
jack_set_buffer_size_callback(client_, bufSizeCallback, this);
jack_set_xrun_callback(client_, xrunCallback, this);
jack_on_shutdown(client_, shutdownCallback, this);
if (!jack_activate(client_))
return "connected";
}
return "disconnected";
}
auto waybar::modules::JACK::update() -> void {
std::string format;
float latency = 1000 * (float)bufsize_ / (float)samplerate_;
auto state = JACKState();
float load;
if(label_.get_style_context()->has_class("xrun")) {
label_.get_style_context()->remove_class("xrun");
state = "connected";
}
if(state.compare("disconnected") != 0)
load = jack_cpu_load(client_);
else {
load = 0;
bufsize_ = 0;
samplerate_ = 0;
latency = 0;
}
if(label_.get_style_context()->has_class(state_))
label_.get_style_context()->remove_class(state_);
if (config_["format-" + state].isString()) {
format = config_["format-" + state].asString();
} else if (config_["format"].isString()) {
format = config_["format"].asString();
} else format = "DSP {load}%";
if(!label_.get_style_context()->has_class(state))
label_.get_style_context()->add_class(state);
state_ = state;
label_.set_markup(fmt::format(format, fmt::arg("load", std::round(load)),
fmt::arg("bufsize", bufsize_),
fmt::arg("samplerate", samplerate_),
fmt::arg("latency", fmt::format("{:.2f}", latency)),
fmt::arg("xruns", xruns_)));
if (tooltipEnabled()) {
std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms";
if (config_["tooltip-format"].isString())
tooltip_format = config_["tooltip-format"].asString();
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("load", std::round(load)),
fmt::arg("bufsize", bufsize_),
fmt::arg("samplerate", samplerate_),
fmt::arg("latency", fmt::format("{:.2f}", latency)),
fmt::arg("xruns", xruns_)));
}
// Call parent update
ALabel::update();
}