Skip to content

Commit d72f38b

Browse files
committed
cava. nonsafe threads
1 parent 161367d commit d72f38b

File tree

5 files changed

+144
-112
lines changed

5 files changed

+144
-112
lines changed

include/modules/cava/cava.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Cava final : public ALabel, public sigc::trackable {
1616
private:
1717
std::shared_ptr<CavaBackend> backend_;
1818
// Text to display
19-
std::string label_text_{""};
19+
Glib::ustring label_text_{""};
2020
bool hide_on_silence_{false};
2121
std::string format_silent_{""};
2222
int ascii_range_{0};

include/modules/cava/cava_backend.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ class CavaBackend final {
4040

4141
private:
4242
CavaBackend(const Json::Value& config);
43-
util::SleeperThread thread_;
4443
util::SleeperThread read_thread_;
44+
sigc::connection out_thread_;
4545
// Cava API to read audio source
4646
::cava::ptr input_source_{NULL};
4747

@@ -55,6 +55,7 @@ class CavaBackend final {
5555
// Delay to handle audio source
5656
std::chrono::milliseconds frame_time_milsec_{1s};
5757

58+
const Json::Value& config_;
5859
int re_paint_{0};
5960
bool silence_{false};
6061
bool silence_prev_{false};
@@ -66,6 +67,9 @@ class CavaBackend final {
6667
void execute();
6768
bool isSilence();
6869
void doUpdate(bool force = false);
70+
void loadConfig();
71+
void freeBackend();
72+
void doOutReadConnect();
6973

7074
// Signal
7175
type_signal_update m_signal_update_;

src/modules/cava/cava.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); }
2626
auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
2727
if (silence_) {
2828
label_.get_style_context()->remove_class("silent");
29-
label_.get_style_context()->add_class("updated");
29+
if (!label_.get_style_context()->has_class("updated"))
30+
label_.get_style_context()->add_class("updated");
3031
}
3132
label_text_.clear();
3233
for (auto& ch : input)
@@ -39,7 +40,8 @@ auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
3940
}
4041
auto waybar::modules::cava::Cava::onSilence() -> void {
4142
if (!silence_) {
42-
label_.get_style_context()->remove_class("updated");
43+
if (label_.get_style_context()->has_class("updated"))
44+
label_.get_style_context()->remove_class("updated");
4345

4446
if (hide_on_silence_)
4547
label_.hide();

src/modules/cava/cava_backend.cpp

Lines changed: 133 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -9,134 +9,42 @@ std::shared_ptr<waybar::modules::cava::CavaBackend> waybar::modules::cava::CavaB
99
return backend_ptr;
1010
}
1111

12-
waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) {
12+
waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) : config_(config) {
1313
// Load waybar module config
14-
char cfgPath[PATH_MAX];
15-
cfgPath[0] = '\0';
16-
17-
if (config["cava_config"].isString()) strcpy(cfgPath, config["cava_config"].asString().data());
18-
// Load cava config
19-
error_.length = 0;
20-
21-
if (!load_config(cfgPath, &prm_, false, &error_, 0)) {
22-
spdlog::error("cava backend. Error loading config. {0}", error_.message);
23-
exit(EXIT_FAILURE);
24-
}
25-
26-
// Override cava parameters by the user config
27-
prm_.inAtty = 0;
28-
prm_.output = ::cava::output_method::OUTPUT_RAW;
29-
if (prm_.data_format) free(prm_.data_format);
30-
prm_.data_format = strdup("ascii");
31-
if (prm_.raw_target) free(prm_.raw_target);
32-
prm_.raw_target = strdup("/dev/stdout");
33-
prm_.ascii_range = config["format-icons"].size() - 1;
34-
35-
prm_.bar_width = 2;
36-
prm_.bar_spacing = 0;
37-
prm_.bar_height = 32;
38-
prm_.bar_width = 1;
39-
prm_.orientation = ::cava::ORIENT_TOP;
40-
prm_.xaxis = ::cava::xaxis_scale::NONE;
41-
prm_.mono_opt = ::cava::AVERAGE;
42-
prm_.autobars = 0;
43-
prm_.gravity = 0;
44-
prm_.integral = 1;
45-
46-
if (config["framerate"].isInt()) prm_.framerate = config["framerate"].asInt();
47-
// Calculate delay for Update() thread
48-
frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate));
49-
if (config["autosens"].isInt()) prm_.autosens = config["autosens"].asInt();
50-
if (config["sensitivity"].isInt()) prm_.sens = config["sensitivity"].asInt();
51-
if (config["bars"].isInt()) prm_.fixedbars = config["bars"].asInt();
52-
if (config["lower_cutoff_freq"].isNumeric())
53-
prm_.lower_cut_off = config["lower_cutoff_freq"].asLargestInt();
54-
if (config["higher_cutoff_freq"].isNumeric())
55-
prm_.upper_cut_off = config["higher_cutoff_freq"].asLargestInt();
56-
if (config["sleep_timer"].isInt()) prm_.sleep_timer = config["sleep_timer"].asInt();
57-
if (config["method"].isString())
58-
prm_.input = ::cava::input_method_by_name(config["method"].asString().c_str());
59-
if (config["source"].isString()) {
60-
if (prm_.audio_source) free(prm_.audio_source);
61-
prm_.audio_source = config["source"].asString().data();
62-
}
63-
if (config["sample_rate"].isNumeric()) prm_.samplerate = config["sample_rate"].asLargestInt();
64-
if (config["sample_bits"].isInt()) prm_.samplebits = config["sample_bits"].asInt();
65-
if (config["stereo"].isBool()) prm_.stereo = config["stereo"].asBool();
66-
if (config["reverse"].isBool()) prm_.reverse = config["reverse"].asBool();
67-
if (config["bar_delimiter"].isInt()) prm_.bar_delim = config["bar_delimiter"].asInt();
68-
if (config["monstercat"].isBool()) prm_.monstercat = config["monstercat"].asBool();
69-
if (config["waves"].isBool()) prm_.waves = config["waves"].asBool();
70-
if (config["noise_reduction"].isDouble())
71-
prm_.noise_reduction = config["noise_reduction"].asDouble();
72-
if (config["input_delay"].isInt())
73-
fetch_input_delay_ = std::chrono::seconds(config["input_delay"].asInt());
74-
75-
audio_raw_.height = prm_.ascii_range;
76-
audio_data_.format = -1;
77-
audio_data_.rate = 0;
78-
audio_data_.samples_counter = 0;
79-
audio_data_.channels = 2;
80-
audio_data_.IEEE_FLOAT = 0;
81-
audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels;
82-
audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8;
83-
audio_data_.terminate = 0;
84-
audio_data_.suspendFlag = false;
85-
input_source_ = get_input(&audio_data_, &prm_);
86-
87-
if (!input_source_) {
88-
spdlog::error("cava backend API didn't provide input audio source method");
89-
exit(EXIT_FAILURE);
90-
}
91-
92-
// Make cava parameters configuration
93-
// Init cava plan, audio_raw structure
94-
audio_raw_init(&audio_data_, &audio_raw_, &prm_, &plan_);
95-
if (!plan_) spdlog::error("cava backend plan is not provided");
96-
audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message
14+
loadConfig();
9715
// Read audio source trough cava API. Cava orginizes this process via infinity loop
9816
read_thread_ = [this] {
17+
// Thread safe reading incoming source and do callbacks
18+
doOutReadConnect();
19+
9920
try {
10021
input_source_(&audio_data_);
10122
} catch (const std::runtime_error& e) {
10223
spdlog::warn("Cava backend. Read source error: {0}", e.what());
10324
}
10425
read_thread_.sleep_for(fetch_input_delay_);
105-
};
106-
107-
thread_ = [this] {
108-
doUpdate();
109-
thread_.sleep_for(frame_time_milsec_);
26+
loadConfig();
11027
};
11128
}
11229

113-
waybar::modules::cava::CavaBackend::~CavaBackend() {
114-
thread_.stop();
115-
read_thread_.stop();
116-
cava_destroy(plan_);
117-
delete plan_;
118-
plan_ = nullptr;
119-
audio_raw_clean(&audio_raw_);
120-
pthread_mutex_lock(&audio_data_.lock);
121-
audio_data_.terminate = 1;
122-
pthread_mutex_unlock(&audio_data_.lock);
123-
config_clean(&prm_);
124-
free(audio_data_.source);
125-
free(audio_data_.cava_in);
126-
}
30+
waybar::modules::cava::CavaBackend::~CavaBackend() { freeBackend(); }
12731

128-
static void upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
32+
static bool upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
12933
if (delta == std::chrono::seconds{0}) {
13034
delta += std::chrono::seconds{1};
13135
delay += delta;
36+
return true;
13237
}
38+
return false;
13339
}
13440

135-
static void downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
41+
static bool downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
13642
if (delta > std::chrono::seconds{0}) {
13743
delay -= delta;
13844
delta -= std::chrono::seconds{1};
45+
return true;
13946
}
47+
return false;
14048
}
14149

14250
bool waybar::modules::cava::CavaBackend::isSilence() {
@@ -186,6 +94,7 @@ void waybar::modules::cava::CavaBackend::doPauseResume() {
18694
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
18795
}
18896
pthread_mutex_unlock(&audio_data_.lock);
97+
doOutReadConnect();
18998
}
19099

191100
waybar::modules::cava::CavaBackend::type_signal_update
@@ -215,12 +124,129 @@ void waybar::modules::cava::CavaBackend::doUpdate(bool force) {
215124
}
216125

217126
if (!silence_ || prm_.sleep_timer == 0) {
218-
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
127+
if (downThreadDelay(frame_time_milsec_, suspend_silence_delay_)) doOutReadConnect();
219128
execute();
220129
if (re_paint_ == 1 || force) m_signal_update_.emit(output_);
221130
} else {
222-
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
131+
if (upThreadDelay(frame_time_milsec_, suspend_silence_delay_)) doOutReadConnect();
223132
if (silence_ != silence_prev_ || force) m_signal_silence_.emit();
224133
}
225134
silence_prev_ = silence_;
226135
}
136+
137+
void waybar::modules::cava::CavaBackend::freeBackend() {
138+
out_thread_.disconnect();
139+
140+
if (plan_ != NULL) {
141+
cava_destroy(plan_);
142+
delete plan_;
143+
plan_ = NULL;
144+
}
145+
146+
audio_raw_clean(&audio_raw_);
147+
pthread_mutex_lock(&audio_data_.lock);
148+
audio_data_.terminate = 1;
149+
pthread_mutex_unlock(&audio_data_.lock);
150+
free_config(&prm_);
151+
free(audio_data_.source);
152+
free(audio_data_.cava_in);
153+
}
154+
155+
void waybar::modules::cava::CavaBackend::loadConfig() {
156+
freeBackend();
157+
// Load waybar module config
158+
char cfgPath[PATH_MAX];
159+
cfgPath[0] = '\0';
160+
161+
if (config_["cava_config"].isString()) strcpy(cfgPath, config_["cava_config"].asString().data());
162+
// Load cava config
163+
error_.length = 0;
164+
165+
if (!load_config(cfgPath, &prm_, &error_)) {
166+
spdlog::error("cava backend. Error loading config. {0}", error_.message);
167+
exit(EXIT_FAILURE);
168+
}
169+
170+
// Override cava parameters by the user config
171+
prm_.inAtty = 0;
172+
prm_.output = ::cava::output_method::OUTPUT_RAW;
173+
if (prm_.data_format) free(prm_.data_format);
174+
prm_.data_format = strdup("ascii");
175+
if (prm_.raw_target) free(prm_.raw_target);
176+
prm_.raw_target = strdup("/dev/stdout");
177+
prm_.ascii_range = config_["format-icons"].size() - 1;
178+
179+
prm_.bar_width = 2;
180+
prm_.bar_spacing = 0;
181+
prm_.bar_height = 32;
182+
prm_.bar_width = 1;
183+
prm_.orientation = ::cava::ORIENT_TOP;
184+
prm_.xaxis = ::cava::xaxis_scale::NONE;
185+
prm_.mono_opt = ::cava::AVERAGE;
186+
prm_.autobars = 0;
187+
prm_.gravity = 0;
188+
prm_.integral = 1;
189+
190+
if (config_["framerate"].isInt()) prm_.framerate = config_["framerate"].asInt();
191+
// Calculate delay for Update() thread
192+
frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate));
193+
if (config_["autosens"].isInt()) prm_.autosens = config_["autosens"].asInt();
194+
if (config_["sensitivity"].isInt()) prm_.sens = config_["sensitivity"].asInt();
195+
if (config_["bars"].isInt()) prm_.fixedbars = config_["bars"].asInt();
196+
if (config_["lower_cutoff_freq"].isNumeric())
197+
prm_.lower_cut_off = config_["lower_cutoff_freq"].asLargestInt();
198+
if (config_["higher_cutoff_freq"].isNumeric())
199+
prm_.upper_cut_off = config_["higher_cutoff_freq"].asLargestInt();
200+
if (config_["sleep_timer"].isInt()) prm_.sleep_timer = config_["sleep_timer"].asInt();
201+
if (config_["method"].isString())
202+
prm_.input = ::cava::input_method_by_name(config_["method"].asString().c_str());
203+
if (config_["source"].isString()) {
204+
if (prm_.audio_source) free(prm_.audio_source);
205+
prm_.audio_source = config_["source"].asString().data();
206+
}
207+
if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt();
208+
if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt();
209+
if (config_["stereo"].isBool()) prm_.stereo = config_["stereo"].asBool();
210+
if (config_["reverse"].isBool()) prm_.reverse = config_["reverse"].asBool();
211+
if (config_["bar_delimiter"].isInt()) prm_.bar_delim = config_["bar_delimiter"].asInt();
212+
if (config_["monstercat"].isBool()) prm_.monstercat = config_["monstercat"].asBool();
213+
if (config_["waves"].isBool()) prm_.waves = config_["waves"].asBool();
214+
if (config_["noise_reduction"].isDouble())
215+
prm_.noise_reduction = config_["noise_reduction"].asDouble();
216+
if (config_["input_delay"].isInt())
217+
fetch_input_delay_ = std::chrono::seconds(config_["input_delay"].asInt());
218+
219+
audio_raw_.height = prm_.ascii_range;
220+
audio_data_.format = -1;
221+
audio_data_.rate = 0;
222+
audio_data_.samples_counter = 0;
223+
audio_data_.channels = 2;
224+
audio_data_.IEEE_FLOAT = 0;
225+
audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels;
226+
audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8;
227+
audio_data_.terminate = 0;
228+
audio_data_.suspendFlag = false;
229+
input_source_ = get_input(&audio_data_, &prm_);
230+
231+
if (!input_source_) {
232+
spdlog::error("cava backend API didn't provide input audio source method");
233+
exit(EXIT_FAILURE);
234+
}
235+
236+
// Make cava parameters configuration
237+
// Init cava plan, audio_raw structure
238+
audio_raw_init(&audio_data_, &audio_raw_, &prm_, &plan_);
239+
if (!plan_) spdlog::error("cava backend plan is not provided");
240+
audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message
241+
}
242+
243+
void waybar::modules::cava::CavaBackend::doOutReadConnect() {
244+
out_thread_.disconnect();
245+
// Thread safe reading incoming source and do callbacks
246+
out_thread_ = Glib::signal_timeout().connect(
247+
[&]() {
248+
Update();
249+
return true;
250+
},
251+
frame_time_milsec_.count());
252+
}

subprojects/cava.wrap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[wrap-git]
22
url = https://github.com/LukashonakV/cava.git
3-
revision = 23efcced43b5a395747b18a2e5f2171fc0925d18
3+
revision = ddeecc8a2c9f786a03ff3ce2a29918573ef9c42e
44
depth = 1
55

66
#directory = cava-0.10.6

0 commit comments

Comments
 (0)