From 13519ca5bfa6d24e638ace7b82fe0fe7bad9bcb8 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Wed, 26 Nov 2025 13:26:32 +0300 Subject: [PATCH 1/4] cava. nonsafe thread.= & cava bump --- include/modules/cava/cava.hpp | 2 +- include/modules/cava/cava_backend.hpp | 6 +- meson.build | 6 +- src/modules/cava/cava.cpp | 6 +- src/modules/cava/cava_backend.cpp | 239 ++++++++++++++------------ subprojects/cava.wrap | 11 -- subprojects/libcava.wrap | 12 ++ 7 files changed, 157 insertions(+), 125 deletions(-) delete mode 100644 subprojects/cava.wrap create mode 100644 subprojects/libcava.wrap diff --git a/include/modules/cava/cava.hpp b/include/modules/cava/cava.hpp index 6b13c4bd..7344f15d 100644 --- a/include/modules/cava/cava.hpp +++ b/include/modules/cava/cava.hpp @@ -16,7 +16,7 @@ class Cava final : public ALabel, public sigc::trackable { private: std::shared_ptr backend_; // Text to display - std::string label_text_{""}; + Glib::ustring label_text_{""}; bool hide_on_silence_{false}; std::string format_silent_{""}; int ascii_range_{0}; diff --git a/include/modules/cava/cava_backend.hpp b/include/modules/cava/cava_backend.hpp index c88182b3..17952c4a 100644 --- a/include/modules/cava/cava_backend.hpp +++ b/include/modules/cava/cava_backend.hpp @@ -40,8 +40,8 @@ class CavaBackend final { private: CavaBackend(const Json::Value& config); - util::SleeperThread thread_; util::SleeperThread read_thread_; + sigc::connection out_thread_; // Cava API to read audio source ::cava::ptr input_source_{NULL}; @@ -55,6 +55,7 @@ class CavaBackend final { // Delay to handle audio source std::chrono::milliseconds frame_time_milsec_{1s}; + const Json::Value& config_; int re_paint_{0}; bool silence_{false}; bool silence_prev_{false}; @@ -66,6 +67,9 @@ class CavaBackend final { void execute(); bool isSilence(); void doUpdate(bool force = false); + void loadConfig(); + void freeBackend(); + void doOutReadConnect(); // Signal type_signal_update m_signal_update_; diff --git a/meson.build b/meson.build index 0675a963..6b9805e1 100644 --- a/meson.build +++ b/meson.build @@ -497,10 +497,10 @@ else man_files += files('man/waybar-clock.5.scd') endif -cava = dependency('cava', - version : '>=0.10.6', +cava = dependency('libcava', + version : '>=0.10.7', required: get_option('cava'), - fallback : ['cava', 'cava_dep'], + fallback : ['libcava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') if cava.found() diff --git a/src/modules/cava/cava.cpp b/src/modules/cava/cava.cpp index a2a74606..906b7021 100644 --- a/src/modules/cava/cava.cpp +++ b/src/modules/cava/cava.cpp @@ -26,7 +26,8 @@ void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); } auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void { if (silence_) { label_.get_style_context()->remove_class("silent"); - label_.get_style_context()->add_class("updated"); + if (!label_.get_style_context()->has_class("updated")) + label_.get_style_context()->add_class("updated"); } label_text_.clear(); for (auto& ch : input) @@ -39,7 +40,8 @@ auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void { } auto waybar::modules::cava::Cava::onSilence() -> void { if (!silence_) { - label_.get_style_context()->remove_class("updated"); + if (label_.get_style_context()->has_class("updated")) + label_.get_style_context()->remove_class("updated"); if (hide_on_silence_) label_.hide(); diff --git a/src/modules/cava/cava_backend.cpp b/src/modules/cava/cava_backend.cpp index c9dba67f..1ff7c086 100644 --- a/src/modules/cava/cava_backend.cpp +++ b/src/modules/cava/cava_backend.cpp @@ -9,134 +9,42 @@ std::shared_ptr waybar::modules::cava::CavaB return backend_ptr; } -waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) { +waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) : config_(config) { // Load waybar module config - char cfgPath[PATH_MAX]; - cfgPath[0] = '\0'; - - if (config["cava_config"].isString()) strcpy(cfgPath, config["cava_config"].asString().data()); - // Load cava config - error_.length = 0; - - if (!load_config(cfgPath, &prm_, false, &error_, 0)) { - spdlog::error("cava backend. Error loading config. {0}", error_.message); - exit(EXIT_FAILURE); - } - - // Override cava parameters by the user config - prm_.inAtty = 0; - prm_.output = ::cava::output_method::OUTPUT_RAW; - if (prm_.data_format) free(prm_.data_format); - prm_.data_format = strdup("ascii"); - if (prm_.raw_target) free(prm_.raw_target); - prm_.raw_target = strdup("/dev/stdout"); - prm_.ascii_range = config["format-icons"].size() - 1; - - prm_.bar_width = 2; - prm_.bar_spacing = 0; - prm_.bar_height = 32; - prm_.bar_width = 1; - prm_.orientation = ::cava::ORIENT_TOP; - prm_.xaxis = ::cava::xaxis_scale::NONE; - prm_.mono_opt = ::cava::AVERAGE; - prm_.autobars = 0; - prm_.gravity = 0; - prm_.integral = 1; - - if (config["framerate"].isInt()) prm_.framerate = config["framerate"].asInt(); - // Calculate delay for Update() thread - frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate)); - if (config["autosens"].isInt()) prm_.autosens = config["autosens"].asInt(); - if (config["sensitivity"].isInt()) prm_.sens = config["sensitivity"].asInt(); - if (config["bars"].isInt()) prm_.fixedbars = config["bars"].asInt(); - if (config["lower_cutoff_freq"].isNumeric()) - prm_.lower_cut_off = config["lower_cutoff_freq"].asLargestInt(); - if (config["higher_cutoff_freq"].isNumeric()) - prm_.upper_cut_off = config["higher_cutoff_freq"].asLargestInt(); - if (config["sleep_timer"].isInt()) prm_.sleep_timer = config["sleep_timer"].asInt(); - if (config["method"].isString()) - prm_.input = ::cava::input_method_by_name(config["method"].asString().c_str()); - if (config["source"].isString()) { - if (prm_.audio_source) free(prm_.audio_source); - prm_.audio_source = config["source"].asString().data(); - } - if (config["sample_rate"].isNumeric()) prm_.samplerate = config["sample_rate"].asLargestInt(); - if (config["sample_bits"].isInt()) prm_.samplebits = config["sample_bits"].asInt(); - if (config["stereo"].isBool()) prm_.stereo = config["stereo"].asBool(); - if (config["reverse"].isBool()) prm_.reverse = config["reverse"].asBool(); - if (config["bar_delimiter"].isInt()) prm_.bar_delim = config["bar_delimiter"].asInt(); - if (config["monstercat"].isBool()) prm_.monstercat = config["monstercat"].asBool(); - if (config["waves"].isBool()) prm_.waves = config["waves"].asBool(); - if (config["noise_reduction"].isDouble()) - prm_.noise_reduction = config["noise_reduction"].asDouble(); - if (config["input_delay"].isInt()) - fetch_input_delay_ = std::chrono::seconds(config["input_delay"].asInt()); - - audio_raw_.height = prm_.ascii_range; - audio_data_.format = -1; - audio_data_.rate = 0; - audio_data_.samples_counter = 0; - audio_data_.channels = 2; - audio_data_.IEEE_FLOAT = 0; - audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels; - audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8; - audio_data_.terminate = 0; - audio_data_.suspendFlag = false; - input_source_ = get_input(&audio_data_, &prm_); - - if (!input_source_) { - spdlog::error("cava backend API didn't provide input audio source method"); - exit(EXIT_FAILURE); - } - - // Make cava parameters configuration - // Init cava plan, audio_raw structure - audio_raw_init(&audio_data_, &audio_raw_, &prm_, &plan_); - if (!plan_) spdlog::error("cava backend plan is not provided"); - audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message + loadConfig(); // Read audio source trough cava API. Cava orginizes this process via infinity loop read_thread_ = [this] { + // Thread safe reading incoming source and do callbacks + doOutReadConnect(); + try { input_source_(&audio_data_); } catch (const std::runtime_error& e) { spdlog::warn("Cava backend. Read source error: {0}", e.what()); } read_thread_.sleep_for(fetch_input_delay_); - }; - - thread_ = [this] { - doUpdate(); - thread_.sleep_for(frame_time_milsec_); + loadConfig(); }; } -waybar::modules::cava::CavaBackend::~CavaBackend() { - thread_.stop(); - read_thread_.stop(); - cava_destroy(plan_); - delete plan_; - plan_ = nullptr; - audio_raw_clean(&audio_raw_); - pthread_mutex_lock(&audio_data_.lock); - audio_data_.terminate = 1; - pthread_mutex_unlock(&audio_data_.lock); - config_clean(&prm_); - free(audio_data_.source); - free(audio_data_.cava_in); -} +waybar::modules::cava::CavaBackend::~CavaBackend() { freeBackend(); } -static void upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) { +static bool upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) { if (delta == std::chrono::seconds{0}) { delta += std::chrono::seconds{1}; delay += delta; + return true; } + return false; } -static void downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) { +static bool downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) { if (delta > std::chrono::seconds{0}) { delay -= delta; delta -= std::chrono::seconds{1}; + return true; } + return false; } bool waybar::modules::cava::CavaBackend::isSilence() { @@ -186,6 +94,7 @@ void waybar::modules::cava::CavaBackend::doPauseResume() { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); } pthread_mutex_unlock(&audio_data_.lock); + doOutReadConnect(); } waybar::modules::cava::CavaBackend::type_signal_update @@ -215,12 +124,128 @@ void waybar::modules::cava::CavaBackend::doUpdate(bool force) { } if (!silence_ || prm_.sleep_timer == 0) { - downThreadDelay(frame_time_milsec_, suspend_silence_delay_); + if (downThreadDelay(frame_time_milsec_, suspend_silence_delay_)) doOutReadConnect(); execute(); if (re_paint_ == 1 || force) m_signal_update_.emit(output_); } else { - upThreadDelay(frame_time_milsec_, suspend_silence_delay_); + if (upThreadDelay(frame_time_milsec_, suspend_silence_delay_)) doOutReadConnect(); if (silence_ != silence_prev_ || force) m_signal_silence_.emit(); } silence_prev_ = silence_; } + +void waybar::modules::cava::CavaBackend::freeBackend() { + out_thread_.disconnect(); + + if (plan_ != NULL) { + cava_destroy(plan_); + plan_ = NULL; + } + + audio_raw_clean(&audio_raw_); + pthread_mutex_lock(&audio_data_.lock); + audio_data_.terminate = 1; + pthread_mutex_unlock(&audio_data_.lock); + free_config(&prm_); + free(audio_data_.source); + free(audio_data_.cava_in); +} + +void waybar::modules::cava::CavaBackend::loadConfig() { + freeBackend(); + // Load waybar module config + char cfgPath[PATH_MAX]; + cfgPath[0] = '\0'; + + if (config_["cava_config"].isString()) strcpy(cfgPath, config_["cava_config"].asString().data()); + // Load cava config + error_.length = 0; + + if (!load_config(cfgPath, &prm_, &error_)) { + spdlog::error("cava backend. Error loading config. {0}", error_.message); + exit(EXIT_FAILURE); + } + + // Override cava parameters by the user config + prm_.inAtty = 0; + prm_.output = ::cava::output_method::OUTPUT_RAW; + if (prm_.data_format) free(prm_.data_format); + prm_.data_format = strdup("ascii"); + if (prm_.raw_target) free(prm_.raw_target); + prm_.raw_target = strdup("/dev/stdout"); + prm_.ascii_range = config_["format-icons"].size() - 1; + + prm_.bar_width = 2; + prm_.bar_spacing = 0; + prm_.bar_height = 32; + prm_.bar_width = 1; + prm_.orientation = ::cava::ORIENT_TOP; + prm_.xaxis = ::cava::xaxis_scale::NONE; + prm_.mono_opt = ::cava::AVERAGE; + prm_.autobars = 0; + prm_.gravity = 0; + prm_.integral = 1; + + if (config_["framerate"].isInt()) prm_.framerate = config_["framerate"].asInt(); + // Calculate delay for Update() thread + frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate)); + if (config_["autosens"].isInt()) prm_.autosens = config_["autosens"].asInt(); + if (config_["sensitivity"].isInt()) prm_.sens = config_["sensitivity"].asInt(); + if (config_["bars"].isInt()) prm_.fixedbars = config_["bars"].asInt(); + if (config_["lower_cutoff_freq"].isNumeric()) + prm_.lower_cut_off = config_["lower_cutoff_freq"].asLargestInt(); + if (config_["higher_cutoff_freq"].isNumeric()) + prm_.upper_cut_off = config_["higher_cutoff_freq"].asLargestInt(); + if (config_["sleep_timer"].isInt()) prm_.sleep_timer = config_["sleep_timer"].asInt(); + if (config_["method"].isString()) + prm_.input = ::cava::input_method_by_name(config_["method"].asString().c_str()); + if (config_["source"].isString()) { + if (prm_.audio_source) free(prm_.audio_source); + prm_.audio_source = config_["source"].asString().data(); + } + if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt(); + if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt(); + if (config_["stereo"].isBool()) prm_.stereo = config_["stereo"].asBool(); + if (config_["reverse"].isBool()) prm_.reverse = config_["reverse"].asBool(); + if (config_["bar_delimiter"].isInt()) prm_.bar_delim = config_["bar_delimiter"].asInt(); + if (config_["monstercat"].isBool()) prm_.monstercat = config_["monstercat"].asBool(); + if (config_["waves"].isBool()) prm_.waves = config_["waves"].asBool(); + if (config_["noise_reduction"].isDouble()) + prm_.noise_reduction = config_["noise_reduction"].asDouble(); + if (config_["input_delay"].isInt()) + fetch_input_delay_ = std::chrono::seconds(config_["input_delay"].asInt()); + + audio_raw_.height = prm_.ascii_range; + audio_data_.format = -1; + audio_data_.rate = 0; + audio_data_.samples_counter = 0; + audio_data_.channels = 2; + audio_data_.IEEE_FLOAT = 0; + audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels; + audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8; + audio_data_.terminate = 0; + audio_data_.suspendFlag = false; + input_source_ = get_input(&audio_data_, &prm_); + + if (!input_source_) { + spdlog::error("cava backend API didn't provide input audio source method"); + exit(EXIT_FAILURE); + } + + // Make cava parameters configuration + // Init cava plan, audio_raw structure + audio_raw_init(&audio_data_, &audio_raw_, &prm_, &plan_); + if (!plan_) spdlog::error("cava backend plan is not provided"); + audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message +} + +void waybar::modules::cava::CavaBackend::doOutReadConnect() { + out_thread_.disconnect(); + // Thread safe reading incoming source and do callbacks + out_thread_ = Glib::signal_timeout().connect( + [&]() { + Update(); + return true; + }, + frame_time_milsec_.count()); +} diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap deleted file mode 100644 index e32bb0da..00000000 --- a/subprojects/cava.wrap +++ /dev/null @@ -1,11 +0,0 @@ -[wrap-git] -url = https://github.com/LukashonakV/cava.git -revision = 23efcced43b5a395747b18a2e5f2171fc0925d18 -depth = 1 - -#directory = cava-0.10.6 -#source_url = https://github.com/LukashonakV/cava/archive/0.10.6.tar.gz -#source_filename = cava-0.10.6.tar.gz -#source_hash = e715c4c6a625b8dc063e57e8e81c80e4d1015ec1b98db69a283b2c6770f839f4 -[provide] -cava = cava_dep diff --git a/subprojects/libcava.wrap b/subprojects/libcava.wrap new file mode 100644 index 00000000..466e2262 --- /dev/null +++ b/subprojects/libcava.wrap @@ -0,0 +1,12 @@ +#[wrap-git] +#url = https://github.com/LukashonakV/cava.git +#revision = 866cfec40b7b9d38e97148d004d3134c1385b52f +#depth = 1 + +[wrap-file] +directory = cava-0.10.7-beta +source_url = https://github.com/LukashonakV/cava/archive/v0.10.7-beta.tar.gz +source_filename = cava-0.10.7.tar.gz +source_hash = 8915d7214f2046554c158fe6f2ae518881dfb573e421ea848727be11a5dfa8c4 +[provide] +libcava = cava_dep From 52f4db115466c2848c24822e2b8fe54508a57f26 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 5 Dec 2025 10:04:52 +0300 Subject: [PATCH 2/4] Moving GTK dispatcher into frontend --- include/modules/cava/cava_backend.hpp | 4 +-- src/modules/cava/cava.cpp | 49 +++++++++++++++------------ src/modules/cava/cava_backend.cpp | 34 ++++++++----------- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/include/modules/cava/cava_backend.hpp b/include/modules/cava/cava_backend.hpp index 17952c4a..c9d6b92b 100644 --- a/include/modules/cava/cava_backend.hpp +++ b/include/modules/cava/cava_backend.hpp @@ -41,7 +41,8 @@ class CavaBackend final { private: CavaBackend(const Json::Value& config); util::SleeperThread read_thread_; - sigc::connection out_thread_; + util::SleeperThread out_thread_; + // Cava API to read audio source ::cava::ptr input_source_{NULL}; @@ -69,7 +70,6 @@ class CavaBackend final { void doUpdate(bool force = false); void loadConfig(); void freeBackend(); - void doOutReadConnect(); // Signal type_signal_update m_signal_update_; diff --git a/src/modules/cava/cava.cpp b/src/modules/cava/cava.cpp index 906b7021..2ea4e694 100644 --- a/src/modules/cava/cava.cpp +++ b/src/modules/cava/cava.cpp @@ -24,30 +24,35 @@ auto waybar::modules::cava::Cava::doAction(const std::string& name) -> void { // Cava actions void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); } auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void { - if (silence_) { - label_.get_style_context()->remove_class("silent"); - if (!label_.get_style_context()->has_class("updated")) - label_.get_style_context()->add_class("updated"); - } - label_text_.clear(); - for (auto& ch : input) - label_text_.append(getIcon((ch > ascii_range_) ? ascii_range_ : ch, "", ascii_range_ + 1)); + Glib::signal_idle().connect_once([this, input]() { + if (silence_) { + label_.get_style_context()->remove_class("silent"); + if (!label_.get_style_context()->has_class("updated")) + label_.get_style_context()->add_class("updated"); + } + label_text_.clear(); + for (auto& ch : input) + label_text_.append(getIcon((ch > ascii_range_) ? ascii_range_ : ch, "", ascii_range_ + 1)); - label_.set_markup(label_text_); - label_.show(); - ALabel::update(); + label_.set_markup(label_text_); + label_.show(); + ALabel::update(); + }); silence_ = false; } -auto waybar::modules::cava::Cava::onSilence() -> void { - if (!silence_) { - if (label_.get_style_context()->has_class("updated")) - label_.get_style_context()->remove_class("updated"); - if (hide_on_silence_) - label_.hide(); - else if (config_["format_silent"].isString()) - label_.set_markup(format_silent_); - silence_ = true; - label_.get_style_context()->add_class("silent"); - } +auto waybar::modules::cava::Cava::onSilence() -> void { + Glib::signal_idle().connect_once([this]() { + if (!silence_) { + if (label_.get_style_context()->has_class("updated")) + label_.get_style_context()->remove_class("updated"); + + if (hide_on_silence_) + label_.hide(); + else if (config_["format_silent"].isString()) + label_.set_markup(format_silent_); + silence_ = true; + label_.get_style_context()->add_class("silent"); + } + }); } diff --git a/src/modules/cava/cava_backend.cpp b/src/modules/cava/cava_backend.cpp index 1ff7c086..aec945dc 100644 --- a/src/modules/cava/cava_backend.cpp +++ b/src/modules/cava/cava_backend.cpp @@ -14,9 +14,6 @@ waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) : con loadConfig(); // Read audio source trough cava API. Cava orginizes this process via infinity loop read_thread_ = [this] { - // Thread safe reading incoming source and do callbacks - doOutReadConnect(); - try { input_source_(&audio_data_); } catch (const std::runtime_error& e) { @@ -25,9 +22,19 @@ waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) : con read_thread_.sleep_for(fetch_input_delay_); loadConfig(); }; + // Write outcoming data. Emit signals + out_thread_ = [this] { + Update(); + out_thread_.sleep_for(frame_time_milsec_); + }; } -waybar::modules::cava::CavaBackend::~CavaBackend() { freeBackend(); } +waybar::modules::cava::CavaBackend::~CavaBackend() { + out_thread_.stop(); + read_thread_.stop(); + + freeBackend(); +} static bool upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) { if (delta == std::chrono::seconds{0}) { @@ -94,7 +101,7 @@ void waybar::modules::cava::CavaBackend::doPauseResume() { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); } pthread_mutex_unlock(&audio_data_.lock); - doOutReadConnect(); + Update(); } waybar::modules::cava::CavaBackend::type_signal_update @@ -124,19 +131,17 @@ void waybar::modules::cava::CavaBackend::doUpdate(bool force) { } if (!silence_ || prm_.sleep_timer == 0) { - if (downThreadDelay(frame_time_milsec_, suspend_silence_delay_)) doOutReadConnect(); + if (downThreadDelay(frame_time_milsec_, suspend_silence_delay_)) Update(); execute(); if (re_paint_ == 1 || force) m_signal_update_.emit(output_); } else { - if (upThreadDelay(frame_time_milsec_, suspend_silence_delay_)) doOutReadConnect(); + if (upThreadDelay(frame_time_milsec_, suspend_silence_delay_)) Update(); if (silence_ != silence_prev_ || force) m_signal_silence_.emit(); } silence_prev_ = silence_; } void waybar::modules::cava::CavaBackend::freeBackend() { - out_thread_.disconnect(); - if (plan_ != NULL) { cava_destroy(plan_); plan_ = NULL; @@ -238,14 +243,3 @@ void waybar::modules::cava::CavaBackend::loadConfig() { if (!plan_) spdlog::error("cava backend plan is not provided"); audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message } - -void waybar::modules::cava::CavaBackend::doOutReadConnect() { - out_thread_.disconnect(); - // Thread safe reading incoming source and do callbacks - out_thread_ = Glib::signal_timeout().connect( - [&]() { - Update(); - return true; - }, - frame_time_milsec_.count()); -} From 3773021546d359b516a9a8d30e20c4821ef61eca Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sun, 7 Dec 2025 23:08:55 +0300 Subject: [PATCH 3/4] cavaGLSL --- include/modules/cava/cava.hpp | 6 +- include/modules/cava/cavaGLSL.hpp | 43 ++++ include/modules/cava/cava_backend.hpp | 6 + meson.build | 11 +- src/factory.cpp | 7 + src/modules/cava/cavaGLSL.cpp | 271 ++++++++++++++++++++++++++ src/modules/cava/cava_backend.cpp | 35 +++- 7 files changed, 367 insertions(+), 12 deletions(-) create mode 100644 include/modules/cava/cavaGLSL.hpp create mode 100644 src/modules/cava/cavaGLSL.cpp diff --git a/include/modules/cava/cava.hpp b/include/modules/cava/cava.hpp index 7344f15d..f4ceb603 100644 --- a/include/modules/cava/cava.hpp +++ b/include/modules/cava/cava.hpp @@ -9,20 +9,20 @@ class Cava final : public ALabel, public sigc::trackable { public: Cava(const std::string&, const Json::Value&); ~Cava() = default; - auto onUpdate(const std::string& input) -> void; - auto onSilence() -> void; auto doAction(const std::string& name) -> void override; private: std::shared_ptr backend_; // Text to display Glib::ustring label_text_{""}; + bool silence_{false}; bool hide_on_silence_{false}; std::string format_silent_{""}; int ascii_range_{0}; - bool silence_{false}; // Cava method void pause_resume(); + auto onUpdate(const std::string& input) -> void; + auto onSilence() -> void; // ModuleActionMap static inline std::map actionMap_{{"mode", &waybar::modules::cava::Cava::pause_resume}}; diff --git a/include/modules/cava/cavaGLSL.hpp b/include/modules/cava/cavaGLSL.hpp new file mode 100644 index 00000000..d2632aff --- /dev/null +++ b/include/modules/cava/cavaGLSL.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include "AModule.hpp" +#include "cava_backend.hpp" + +namespace waybar::modules::cava { + +class CavaGLSL final : public AModule, public Gtk::GLArea { + public: + CavaGLSL(const std::string&, const Json::Value&); + ~CavaGLSL() = default; + + private: + std::shared_ptr backend_; + struct ::cava::config_params prm_; + int frame_counter{0}; + bool silence_{false}; + bool hide_on_silence_{false}; + // Cava method + auto onUpdate(const ::cava::audio_raw& input) -> void; + auto onSilence() -> void; + // Member variable to store the shared pointer + std::shared_ptr<::cava::audio_raw> m_data_; + GLuint shaderProgram_; + // OpenGL variables + GLuint fbo_; + GLuint texture_; + GLint uniform_bars_; + GLint uniform_previous_bars_; + GLint uniform_bars_count_; + GLint uniform_time_; + // Methods + void onRealize(); + bool onRender(const Glib::RefPtr& context); + + void initShaders(); + void initSurface(); + void initGLSL(); + GLuint loadShader(const std::string& fileName, GLenum type); +}; +} // namespace waybar::modules::cava diff --git a/include/modules/cava/cava_backend.hpp b/include/modules/cava/cava_backend.hpp index c9d6b92b..4022a2ff 100644 --- a/include/modules/cava/cava_backend.hpp +++ b/include/modules/cava/cava_backend.hpp @@ -32,9 +32,14 @@ class CavaBackend final { int getAsciiRange(); void doPauseResume(); void Update(); + const struct ::cava::config_params* getPrm(); + std::chrono::milliseconds getFrameTimeMilsec(); + // Signal accessor using type_signal_update = sigc::signal; type_signal_update signal_update(); + using type_signal_audio_raw_update = sigc::signal; + type_signal_audio_raw_update signal_audio_raw_update(); using type_signal_silence = sigc::signal; type_signal_silence signal_silence(); @@ -73,6 +78,7 @@ class CavaBackend final { // Signal type_signal_update m_signal_update_; + type_signal_audio_raw_update m_signal_audio_raw_; type_signal_silence m_signal_silence_; }; } // namespace waybar::modules::cava diff --git a/meson.build b/meson.build index 6b9805e1..17818a09 100644 --- a/meson.build +++ b/meson.build @@ -503,10 +503,18 @@ cava = dependency('libcava', fallback : ['libcava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') +eproxy = dependency('epoxy', required: false) + if cava.found() add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp') - src_files += files('src/modules/cava/cava.cpp', 'src/modules/cava/cava_backend.cpp') + src_files += files('src/modules/cava/cava.cpp', + 'src/modules/cava/cava_backend.cpp') man_files += files('man/waybar-cava.5.scd') + + if eproxy.found() + add_project_arguments('-DHAVE_LIBCAVAGLSL', language: 'cpp') + src_files += files('src/modules/cava/cavaGLSL.cpp') + endif endif if libgps.found() @@ -554,6 +562,7 @@ executable( tz_dep, xkbregistry, cava, + eproxy, libgps ], include_directories: inc_dirs, diff --git a/src/factory.cpp b/src/factory.cpp index 7828ce75..94ca8e61 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -110,6 +110,9 @@ #endif #ifdef HAVE_LIBCAVA #include "modules/cava/cava.hpp" +#ifdef HAVE_LIBCAVAGLSL +#include "modules/cava/cavaGLSL.hpp" +#endif #endif #ifdef HAVE_SYSTEMD_MONITOR #include "modules/systemd_failed_units.hpp" @@ -344,6 +347,10 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, #ifdef HAVE_LIBCAVA if (ref == "cava") { return new waybar::modules::cava::Cava(id, config_[name]); +#ifdef HAVE_LIBCAVAGLSL + } else if (ref == "cavaGLSL") { + return new waybar::modules::cava::CavaGLSL(id, config_[name]); +#endif } #endif #ifdef HAVE_SYSTEMD_MONITOR diff --git a/src/modules/cava/cavaGLSL.cpp b/src/modules/cava/cavaGLSL.cpp new file mode 100644 index 00000000..4ab03bcd --- /dev/null +++ b/src/modules/cava/cavaGLSL.cpp @@ -0,0 +1,271 @@ +#include "modules/cava/cavaGLSL.hpp" + +#include + +#include + +waybar::modules::cava::CavaGLSL::CavaGLSL(const std::string& id, const Json::Value& config) + : AModule(config, "cavaGLSL", id, false, false), + backend_{waybar::modules::cava::CavaBackend::inst(config)} { + set_name(name_); + if (config_["hide_on_silence"].isBool()) hide_on_silence_ = config_["hide_on_silence"].asBool(); + if (!id.empty()) { + get_style_context()->add_class(id); + } + get_style_context()->add_class(MODULE_CLASS); + + set_use_es(true); + // set_auto_render(true); + signal_realize().connect(sigc::mem_fun(*this, &CavaGLSL::onRealize)); + signal_render().connect(sigc::mem_fun(*this, &CavaGLSL::onRender), false); + + // Get parameters_config struct from the backend + prm_ = *backend_->getPrm(); + + // Set widget length + int length{0}; + if (config_["min-length"].isUInt()) + length = config_["min-length"].asUInt(); + else if (config_["max-length"].isUInt()) + length = config_["max-length"].asUInt(); + else + length = prm_.sdl_width; + + set_size_request(length, prm_.sdl_height); + + // Subscribe for changes + backend_->signal_audio_raw_update().connect(sigc::mem_fun(*this, &CavaGLSL::onUpdate)); + // Subscribe for silence + backend_->signal_silence().connect(sigc::mem_fun(*this, &CavaGLSL::onSilence)); + event_box_.add(*this); +} + +auto waybar::modules::cava::CavaGLSL::onUpdate(const ::cava::audio_raw& input) -> void { + Glib::signal_idle().connect_once([this, input]() { + m_data_ = std::make_shared<::cava::audio_raw>(input); + if (silence_) { + get_style_context()->remove_class("silent"); + if (!get_style_context()->has_class("updated")) get_style_context()->add_class("updated"); + show(); + silence_ = false; + } + + queue_render(); + }); +} + +auto waybar::modules::cava::CavaGLSL::onSilence() -> void { + Glib::signal_idle().connect_once([this]() { + if (!silence_) { + if (get_style_context()->has_class("updated")) get_style_context()->remove_class("updated"); + + if (hide_on_silence_) hide(); + silence_ = true; + get_style_context()->add_class("silent"); + // Set clear color to black + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + queue_render(); + } + }); +} + +bool waybar::modules::cava::CavaGLSL::onRender(const Glib::RefPtr& context) { + if (!m_data_) return true; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_); + glUniform1i(glGetUniformLocation(shaderProgram_, "inputTexture"), 0); + + glUniform1fv(uniform_bars_, m_data_->number_of_bars, m_data_->bars_raw); + glUniform1fv(uniform_previous_bars_, m_data_->number_of_bars, m_data_->previous_bars_raw); + glUniform1i(uniform_bars_count_, m_data_->number_of_bars); + ++frame_counter; + glUniform1f(uniform_time_, (frame_counter / backend_->getFrameTimeMilsec().count()) / 1e3); + + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, nullptr); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, nullptr); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return true; +} + +void waybar::modules::cava::CavaGLSL::onRealize() { + make_current(); + initShaders(); + initGLSL(); + initSurface(); +} + +struct colors { + uint16_t R; + uint16_t G; + uint16_t B; +}; + +static void parse_color(char* color_string, struct colors* color) { + if (color_string[0] == '#') { + sscanf(++color_string, "%02hx%02hx%02hx", &color->R, &color->G, &color->B); + } +} + +void waybar::modules::cava::CavaGLSL::initGLSL() { + GLint gVertexPos2DLocation{glGetAttribLocation(shaderProgram_, "vertexPosition_modelspace")}; + if (gVertexPos2DLocation == -1) { + spdlog::error("{0}. Could not find vertex position shader variable", name_); + } + + glClearColor(0.f, 0.f, 0.f, 1.f); + + GLfloat vertexData[]{-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f}; + GLint indexData[]{0, 1, 2, 3}; + + GLuint gVBO{0}; + glGenBuffers(1, &gVBO); + glBindBuffer(GL_ARRAY_BUFFER, gVBO); + glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), vertexData, GL_STATIC_DRAW); + + GLuint gIBO{0}; + glGenBuffers(1, &gIBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gIBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 4 * sizeof(GLuint), indexData, GL_STATIC_DRAW); + + GLuint gVAO{0}; + glGenVertexArrays(1, &gVAO); + glBindVertexArray(gVAO); + glEnableVertexAttribArray(gVertexPos2DLocation); + + glBindBuffer(GL_ARRAY_BUFFER, gVBO); + glVertexAttribPointer(gVertexPos2DLocation, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gIBO); + + glGenFramebuffers(1, &fbo_); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + + // Create a texture to attach the framebuffer + glGenTextures(1, &texture_); + glBindTexture(GL_TEXTURE_2D, texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, prm_.sdl_width, prm_.sdl_height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0); + + // Check is framebuffer is complete + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + spdlog::error("{0}. Framebuffer not complete", name_); + } + + // Unbind the framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); + uniform_bars_ = glGetUniformLocation(shaderProgram_, "bars"); + uniform_previous_bars_ = glGetUniformLocation(shaderProgram_, "previous_bars"); + uniform_bars_count_ = glGetUniformLocation(shaderProgram_, "bars_count"); + uniform_time_ = glGetUniformLocation(shaderProgram_, "shader_time"); + + GLuint err{glGetError()}; + if (err != 0) { + spdlog::error("{0}. Error on initGLSL: {1}", name_, err); + } +} + +void waybar::modules::cava::CavaGLSL::initSurface() { + colors color = {0}; + GLint uniform_bg_col{glGetUniformLocation(shaderProgram_, "bg_color")}; + parse_color(prm_.bcolor, &color); + glUniform3f(uniform_bg_col, (float)color.R / 255.0, (float)color.G / 255.0, + (float)color.B / 255.0); + GLint uniform_fg_col{glGetUniformLocation(shaderProgram_, "fg_color")}; + parse_color(prm_.color, &color); + glUniform3f(uniform_fg_col, (float)color.R / 255.0, (float)color.G / 255.0, + (float)color.B / 255.0); + GLint uniform_res{glGetUniformLocation(shaderProgram_, "u_resolution")}; + glUniform3f(uniform_res, (float)prm_.sdl_width, (float)prm_.sdl_height, 0.0f); + GLint uniform_bar_width{glGetUniformLocation(shaderProgram_, "bar_width")}; + glUniform1i(uniform_bar_width, prm_.bar_width); + GLint uniform_bar_spacing{glGetUniformLocation(shaderProgram_, "bar_spacing")}; + glUniform1i(uniform_bar_spacing, prm_.bar_spacing); + GLint uniform_gradient_count{glGetUniformLocation(shaderProgram_, "gradient_count")}; + glUniform1i(uniform_gradient_count, prm_.gradient_count); + GLint uniform_gradient_colors{glGetUniformLocation(shaderProgram_, "gradient_colors")}; + GLfloat gradient_colors[8][3]; + for (int i{0}; i < prm_.gradient_count; ++i) { + parse_color(prm_.gradient_colors[i], &color); + gradient_colors[i][0] = (float)color.R / 255.0; + gradient_colors[i][1] = (float)color.G / 255.0; + gradient_colors[i][2] = (float)color.B / 255.0; + } + glUniform3fv(uniform_gradient_colors, 8, (const GLfloat*)gradient_colors); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, nullptr); +} + +void waybar::modules::cava::CavaGLSL::initShaders() { + shaderProgram_ = glCreateProgram(); + + GLuint vertexShader{loadShader(prm_.vertex_shader, GL_VERTEX_SHADER)}; + GLuint fragmentShader{loadShader(prm_.fragment_shader, GL_FRAGMENT_SHADER)}; + + glAttachShader(shaderProgram_, vertexShader); + glAttachShader(shaderProgram_, fragmentShader); + + glLinkProgram(shaderProgram_); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + // Check for linking errors + GLint success, len; + glGetProgramiv(shaderProgram_, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramiv(shaderProgram_, GL_INFO_LOG_LENGTH, &len); + GLchar* infoLog{(char*)'\0'}; + glGetProgramInfoLog(shaderProgram_, len, &len, infoLog); + spdlog::error("{0}. Shader linking error: {1}", name_, infoLog); + } + + glReleaseShaderCompiler(); + glUseProgram(shaderProgram_); +} + +GLuint waybar::modules::cava::CavaGLSL::loadShader(const std::string& fileName, GLenum type) { + spdlog::debug("{0}. loadShader: {1}", name_, fileName); + + // Read shader source code from the file + std::ifstream shaderFile{fileName}; + + if (!shaderFile.is_open()) { + spdlog::error("{0}. Could not open shader file: {1}", name_, fileName); + } + + std::ostringstream buffer; + buffer << shaderFile.rdbuf(); // read file content into stringstream + std::string str{buffer.str()}; + const char* shaderSource = str.c_str(); + shaderFile.close(); + + GLuint shaderID{glCreateShader(type)}; + if (shaderID == 0) spdlog::error("{0}. Error creating shader type: {0}", type); + glShaderSource(shaderID, 1, &shaderSource, nullptr); + glCompileShader(shaderID); + + // Check for compilation errors + GLint success, len; + + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); + + if (!success) { + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &len); + + GLchar* infoLog{(char*)'\0'}; + glGetShaderInfoLog(shaderID, len, nullptr, infoLog); + spdlog::error("{0}. Shader compilation error in {1}: {2}", name_, fileName, infoLog); + } + + return shaderID; +} diff --git a/src/modules/cava/cava_backend.cpp b/src/modules/cava/cava_backend.cpp index aec945dc..c2563272 100644 --- a/src/modules/cava/cava_backend.cpp +++ b/src/modules/cava/cava_backend.cpp @@ -24,7 +24,7 @@ waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) : con }; // Write outcoming data. Emit signals out_thread_ = [this] { - Update(); + doUpdate(false); out_thread_.sleep_for(frame_time_milsec_); }; } @@ -109,6 +109,11 @@ waybar::modules::cava::CavaBackend::signal_update() { return m_signal_update_; } +waybar::modules::cava::CavaBackend::type_signal_audio_raw_update +waybar::modules::cava::CavaBackend::signal_audio_raw_update() { + return m_signal_audio_raw_; +} + waybar::modules::cava::CavaBackend::type_signal_silence waybar::modules::cava::CavaBackend::signal_silence() { return m_signal_silence_; @@ -133,7 +138,10 @@ void waybar::modules::cava::CavaBackend::doUpdate(bool force) { if (!silence_ || prm_.sleep_timer == 0) { if (downThreadDelay(frame_time_milsec_, suspend_silence_delay_)) Update(); execute(); - if (re_paint_ == 1 || force) m_signal_update_.emit(output_); + if (re_paint_ == 1 || force || prm_.continuous_rendering) { + m_signal_update_.emit(output_); + m_signal_audio_raw_.emit(audio_raw_); + } } else { if (upThreadDelay(frame_time_milsec_, suspend_silence_delay_)) Update(); if (silence_ != silence_prev_ || force) m_signal_silence_.emit(); @@ -180,16 +188,15 @@ void waybar::modules::cava::CavaBackend::loadConfig() { prm_.raw_target = strdup("/dev/stdout"); prm_.ascii_range = config_["format-icons"].size() - 1; - prm_.bar_width = 2; - prm_.bar_spacing = 0; - prm_.bar_height = 32; - prm_.bar_width = 1; + if (config_["bar_spacing"].isInt()) prm_.bar_spacing = config_["bar_spacing"].asInt(); + if (config_["bar_width"].isInt()) prm_.bar_width = config_["bar_width"].asInt(); + if (config_["bar_height"].isInt()) prm_.bar_height = config_["bar_height"].asInt(); prm_.orientation = ::cava::ORIENT_TOP; prm_.xaxis = ::cava::xaxis_scale::NONE; prm_.mono_opt = ::cava::AVERAGE; prm_.autobars = 0; - prm_.gravity = 0; - prm_.integral = 1; + if (config_["gravity"].isInt()) prm_.gravity = config_["gravity"].asInt(); + if (config_["integral"].isInt()) prm_.integral = config_["integral"].asInt(); if (config_["framerate"].isInt()) prm_.framerate = config_["framerate"].asInt(); // Calculate delay for Update() thread @@ -219,6 +226,13 @@ void waybar::modules::cava::CavaBackend::loadConfig() { prm_.noise_reduction = config_["noise_reduction"].asDouble(); if (config_["input_delay"].isInt()) fetch_input_delay_ = std::chrono::seconds(config_["input_delay"].asInt()); + if (config_["gradient"].isInt()) prm_.gradient = config_["gradient"].asInt(); + if (prm_.gradient == 0) + prm_.gradient_count = 0; + else if (config_["gradient_count"].isInt()) + prm_.gradient_count = config_["gradient_count"].asInt(); + if (config_["sdl_width"].isInt()) prm_.sdl_width = config_["sdl_width"].asInt(); + if (config_["sdl_height"].isInt()) prm_.sdl_height = config_["sdl_height"].asInt(); audio_raw_.height = prm_.ascii_range; audio_data_.format = -1; @@ -243,3 +257,8 @@ void waybar::modules::cava::CavaBackend::loadConfig() { if (!plan_) spdlog::error("cava backend plan is not provided"); audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message } + +const struct ::cava::config_params* waybar::modules::cava::CavaBackend::getPrm() { return &prm_; } +std::chrono::milliseconds waybar::modules::cava::CavaBackend::getFrameTimeMilsec() { + return frame_time_milsec_; +}; From e03119fe9414b8a83a4a87cadbbf6d0429b878c9 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 9 Dec 2025 18:13:45 +0300 Subject: [PATCH 4/4] Factory. cava_frontend --- .../modules/cava/{cava.hpp => cavaRaw.hpp} | 0 include/modules/cava/cava_frontend.hpp | 30 +++++++++++++++++++ meson.build | 2 +- src/factory.cpp | 15 ++-------- src/modules/cava/{cava.cpp => cavaRaw.cpp} | 2 +- src/modules/cava/cava_backend.cpp | 19 ++++++++---- 6 files changed, 48 insertions(+), 20 deletions(-) rename include/modules/cava/{cava.hpp => cavaRaw.hpp} (100%) create mode 100644 include/modules/cava/cava_frontend.hpp rename src/modules/cava/{cava.cpp => cavaRaw.cpp} (98%) diff --git a/include/modules/cava/cava.hpp b/include/modules/cava/cavaRaw.hpp similarity index 100% rename from include/modules/cava/cava.hpp rename to include/modules/cava/cavaRaw.hpp diff --git a/include/modules/cava/cava_frontend.hpp b/include/modules/cava/cava_frontend.hpp new file mode 100644 index 00000000..5a73f62f --- /dev/null +++ b/include/modules/cava/cava_frontend.hpp @@ -0,0 +1,30 @@ +#pragma once + +#ifdef HAVE_LIBCAVA +#include "cavaRaw.hpp" +#include "cava_backend.hpp" +#ifdef HAVE_LIBCAVAGLSL +#include "cavaGLSL.hpp" +#endif +#endif + +namespace waybar::modules::cava { +AModule* getModule(const std::string& id, const Json::Value& config) { +#ifdef HAVE_LIBCAVA + const std::shared_ptr backend_{waybar::modules::cava::CavaBackend::inst(config)}; + switch (backend_->getPrm()->output) { + case ::cava::output_method::OUTPUT_RAW: + return new waybar::modules::cava::Cava(id, config); + break; +#ifdef HAVE_LIBCAVAGLSL + case ::cava::output_method::OUTPUT_SDL_GLSL: + return new waybar::modules::cava::CavaGLSL(id, config); + break; +#endif + default: + break; + } +#endif + throw std::runtime_error("Unknown module"); +}; +} // namespace waybar::modules::cava diff --git a/meson.build b/meson.build index 17818a09..c6b7f90a 100644 --- a/meson.build +++ b/meson.build @@ -507,7 +507,7 @@ eproxy = dependency('epoxy', required: false) if cava.found() add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp') - src_files += files('src/modules/cava/cava.cpp', + src_files += files('src/modules/cava/cavaRaw.cpp', 'src/modules/cava/cava_backend.cpp') man_files += files('man/waybar-cava.5.scd') diff --git a/src/factory.cpp b/src/factory.cpp index 94ca8e61..2fd3e3b8 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -108,18 +108,13 @@ #ifdef HAVE_LIBWIREPLUMBER #include "modules/wireplumber.hpp" #endif -#ifdef HAVE_LIBCAVA -#include "modules/cava/cava.hpp" -#ifdef HAVE_LIBCAVAGLSL -#include "modules/cava/cavaGLSL.hpp" -#endif -#endif #ifdef HAVE_SYSTEMD_MONITOR #include "modules/systemd_failed_units.hpp" #endif #ifdef HAVE_LIBGPS #include "modules/gps.hpp" #endif +#include "modules/cava/cava_frontend.hpp" #include "modules/cffi.hpp" #include "modules/custom.hpp" #include "modules/image.hpp" @@ -344,15 +339,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, return new waybar::modules::Wireplumber(id, config_[name]); } #endif -#ifdef HAVE_LIBCAVA if (ref == "cava") { - return new waybar::modules::cava::Cava(id, config_[name]); -#ifdef HAVE_LIBCAVAGLSL - } else if (ref == "cavaGLSL") { - return new waybar::modules::cava::CavaGLSL(id, config_[name]); -#endif + return waybar::modules::cava::getModule(id, config_[name]); } -#endif #ifdef HAVE_SYSTEMD_MONITOR if (ref == "systemd-failed-units") { return new waybar::modules::SystemdFailedUnits(id, config_[name]); diff --git a/src/modules/cava/cava.cpp b/src/modules/cava/cavaRaw.cpp similarity index 98% rename from src/modules/cava/cava.cpp rename to src/modules/cava/cavaRaw.cpp index 2ea4e694..edec902b 100644 --- a/src/modules/cava/cava.cpp +++ b/src/modules/cava/cavaRaw.cpp @@ -1,4 +1,4 @@ -#include "modules/cava/cava.hpp" +#include "modules/cava/cavaRaw.hpp" #include diff --git a/src/modules/cava/cava_backend.cpp b/src/modules/cava/cava_backend.cpp index c2563272..917e165f 100644 --- a/src/modules/cava/cava_backend.cpp +++ b/src/modules/cava/cava_backend.cpp @@ -181,11 +181,16 @@ void waybar::modules::cava::CavaBackend::loadConfig() { // Override cava parameters by the user config prm_.inAtty = 0; - prm_.output = ::cava::output_method::OUTPUT_RAW; - if (prm_.data_format) free(prm_.data_format); - prm_.data_format = strdup("ascii"); - if (prm_.raw_target) free(prm_.raw_target); - prm_.raw_target = strdup("/dev/stdout"); + auto const output{prm_.output}; + // prm_.output = ::cava::output_method::OUTPUT_RAW; + if (config_["data_format"].isString()) { + if (prm_.data_format) free(prm_.data_format); + prm_.data_format = strdup(config_["data_format"].asString().c_str()); + } + if (config_["raw_target"].isString()) { + if (prm_.raw_target) free(prm_.raw_target); + prm_.raw_target = strdup(config_["raw_target"].asString().c_str()); + } prm_.ascii_range = config_["format-icons"].size() - 1; if (config_["bar_spacing"].isInt()) prm_.bar_spacing = config_["bar_spacing"].asInt(); @@ -251,11 +256,15 @@ void waybar::modules::cava::CavaBackend::loadConfig() { exit(EXIT_FAILURE); } + prm_.output = ::cava::output_method::OUTPUT_RAW; + // Make cava parameters configuration // Init cava plan, audio_raw structure audio_raw_init(&audio_data_, &audio_raw_, &prm_, &plan_); if (!plan_) spdlog::error("cava backend plan is not provided"); audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message + + prm_.output = output; } const struct ::cava::config_params* waybar::modules::cava::CavaBackend::getPrm() { return &prm_; }