mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-25 03:37:50 +00:00
201 lines
5.6 KiB
Plaintext
201 lines
5.6 KiB
Plaintext
![]() |
|
||
|
#include "ui_grapheq.hpp"
|
||
|
|
||
|
/* GraphEq *************************************************************/
|
||
|
|
||
|
GraphEq::GraphEq(
|
||
|
Rect parent_rect,
|
||
|
bool clickable)
|
||
|
: Widget{parent_rect},
|
||
|
clickable_{clickable},
|
||
|
bar_heights(NUM_BARS, 0),
|
||
|
prev_bar_heights(NUM_BARS, 0) {
|
||
|
if (clickable) {
|
||
|
set_focusable(true);
|
||
|
// previous_data.resize(length_, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GraphEq::set_parent_rect(const Rect new_parent_rect) {
|
||
|
Widget::set_parent_rect(new_parent_rect);
|
||
|
calculate_params();
|
||
|
}
|
||
|
|
||
|
void GraphEq::calculate_params() {
|
||
|
y_top = screen_rect().top();
|
||
|
RENDER_HEIGHT = parent_rect().height();
|
||
|
BAR_WIDTH = (parent_rect().width() - (BAR_SPACING * (NUM_BARS - 1))) / NUM_BARS;
|
||
|
HORIZONTAL_OFFSET = screen_rect().left();
|
||
|
}
|
||
|
|
||
|
bool GraphEq::is_paused() const {
|
||
|
return paused_;
|
||
|
}
|
||
|
|
||
|
void GraphEq::set_paused(bool paused) {
|
||
|
paused_ = paused;
|
||
|
needs_background_redraw = true;
|
||
|
set_dirty();
|
||
|
}
|
||
|
|
||
|
bool GraphEq::is_clickable() const {
|
||
|
return clickable_;
|
||
|
}
|
||
|
|
||
|
void GraphEq::getAccessibilityText(std::string& result) {
|
||
|
result = paused_ ? "paused GraphEq" : "GraphEq";
|
||
|
}
|
||
|
|
||
|
void GraphEq::getWidgetName(std::string& result) {
|
||
|
result = "GraphEq";
|
||
|
}
|
||
|
|
||
|
bool GraphEq::on_key(const KeyEvent key) {
|
||
|
if (!clickable_) return false;
|
||
|
|
||
|
if (key == KeyEvent::Select) {
|
||
|
set_paused(!paused_);
|
||
|
if (on_select) {
|
||
|
on_select(*this);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool GraphEq::on_keyboard(const KeyboardEvent key) {
|
||
|
if (!clickable_) return false;
|
||
|
|
||
|
if (key == 32 || key == 10) {
|
||
|
set_paused(!paused_);
|
||
|
if (on_select) {
|
||
|
on_select(*this);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool GraphEq::on_touch(const TouchEvent event) {
|
||
|
if (!clickable_) return false;
|
||
|
|
||
|
switch (event.type) {
|
||
|
case TouchEvent::Type::Start:
|
||
|
focus();
|
||
|
return true;
|
||
|
|
||
|
case TouchEvent::Type::End:
|
||
|
set_paused(!paused_);
|
||
|
if (on_select) {
|
||
|
on_select(*this);
|
||
|
}
|
||
|
return true;
|
||
|
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GraphEq::set_theme(Color base_color_, Color peak_color_) {
|
||
|
base_color = base_color_;
|
||
|
peak_color = peak_color_;
|
||
|
set_dirty();
|
||
|
}
|
||
|
|
||
|
void GraphEq::update_audio_spectrum(const AudioSpectrum& spectrum) {
|
||
|
const float bin_frequency_size = 48000.0f / 128;
|
||
|
|
||
|
for (int bar = 0; bar < NUM_BARS; bar++) {
|
||
|
float start_freq = FREQUENCY_BANDS[bar];
|
||
|
float end_freq = FREQUENCY_BANDS[bar + 1];
|
||
|
|
||
|
int start_bin = std::max(1, (int)(start_freq / bin_frequency_size));
|
||
|
int end_bin = std::min(127, (int)(end_freq / bin_frequency_size));
|
||
|
|
||
|
if (start_bin >= end_bin) {
|
||
|
end_bin = start_bin + 1;
|
||
|
}
|
||
|
|
||
|
float total_energy = 0;
|
||
|
int bin_count = 0;
|
||
|
|
||
|
for (int bin = start_bin; bin <= end_bin; bin++) {
|
||
|
total_energy += spectrum.db[bin];
|
||
|
bin_count++;
|
||
|
}
|
||
|
|
||
|
float avg_db = bin_count > 0 ? (total_energy / bin_count) : 0;
|
||
|
|
||
|
// Manually boost highs for better visual balance
|
||
|
float treble_boost = 1.0f;
|
||
|
if (bar == 10)
|
||
|
treble_boost = 1.7f;
|
||
|
else if (bar >= 9)
|
||
|
treble_boost = 1.3f;
|
||
|
else if (bar >= 7)
|
||
|
treble_boost = 1.3f;
|
||
|
|
||
|
// Mid emphasis for a V-shape effect
|
||
|
float mid_boost = 1.0f;
|
||
|
if (bar == 4 || bar == 5 || bar == 6) mid_boost = 1.2f;
|
||
|
|
||
|
float amplified_db = avg_db * treble_boost * mid_boost;
|
||
|
|
||
|
if (amplified_db > 255) amplified_db = 255;
|
||
|
|
||
|
float band_scale = 1.0f;
|
||
|
int target_height = (amplified_db * RENDER_HEIGHT * band_scale) / 255;
|
||
|
|
||
|
if (target_height > RENDER_HEIGHT) {
|
||
|
target_height = RENDER_HEIGHT;
|
||
|
}
|
||
|
|
||
|
// Adjusted to look nice to my eyes
|
||
|
float rise_speed = 0.8f;
|
||
|
float fall_speed = 1.0f;
|
||
|
|
||
|
if (target_height > bar_heights[bar]) {
|
||
|
bar_heights[bar] = bar_heights[bar] * (1.0f - rise_speed) + target_height * rise_speed;
|
||
|
} else {
|
||
|
bar_heights[bar] = bar_heights[bar] * (1.0f - fall_speed) + target_height * fall_speed;
|
||
|
}
|
||
|
}
|
||
|
set_dirty();
|
||
|
}
|
||
|
|
||
|
void GraphEq::paint(Painter& painter) {
|
||
|
if (!visible()) return;
|
||
|
if (!is_calculated) { // calc positions first
|
||
|
calculate_params();
|
||
|
is_calculated = true;
|
||
|
}
|
||
|
if (needs_background_redraw) {
|
||
|
painter.fill_rectangle(screen_rect(), Theme::getInstance()->bg_darkest->background);
|
||
|
needs_background_redraw = false;
|
||
|
}
|
||
|
if (paused_) {
|
||
|
return;
|
||
|
}
|
||
|
const int num_segments = RENDER_HEIGHT / SEGMENT_HEIGHT;
|
||
|
uint16_t bottom = screen_rect().bottom();
|
||
|
for (int bar = 0; bar < NUM_BARS; bar++) {
|
||
|
int x = HORIZONTAL_OFFSET + bar * (BAR_WIDTH + BAR_SPACING);
|
||
|
int active_segments = (bar_heights[bar] * num_segments) / RENDER_HEIGHT;
|
||
|
|
||
|
if (prev_bar_heights[bar] > active_segments) {
|
||
|
int clear_height = (prev_bar_heights[bar] - active_segments) * SEGMENT_HEIGHT;
|
||
|
int clear_y = bottom - prev_bar_heights[bar] * SEGMENT_HEIGHT;
|
||
|
painter.fill_rectangle({x, clear_y, BAR_WIDTH, clear_height}, Theme::getInstance()->bg_darkest->background);
|
||
|
}
|
||
|
|
||
|
for (int seg = 0; seg < active_segments; seg++) {
|
||
|
int y = bottom - (seg + 1) * SEGMENT_HEIGHT;
|
||
|
if (y < y_top) break;
|
||
|
|
||
|
Color segment_color = (seg >= active_segments - 2 && seg < active_segments) ? peak_color : base_color;
|
||
|
painter.fill_rectangle({x, y, BAR_WIDTH, SEGMENT_HEIGHT - 1}, segment_color);
|
||
|
}
|
||
|
prev_bar_heights[bar] = active_segments;
|
||
|
}
|
||
|
}
|