28class FilterContainer final :
public HorizontalMenuContainer {
30 FilterContainer(std::initializer_list<MenuItem*> items,
FilterParam* morph_item)
31 : HorizontalMenuContainer(items), morph_item_{morph_item} {}
33 : HorizontalMenuContainer(items), morph_item_unpatched_{morph_item} {}
38 const auto [freq_raw, reso_raw, morph_raw, is_morphable, is_hpf] = getFilterValues();
39 const float freq_value = freq_raw / 50.f;
40 const float reso_value = sigmoidLikeCurve(reso_raw, 50.f, 30.f);
41 const float morph_value = [&] {
44 result = morph_raw / 50.f;
47 result = 1.0f - result;
52 constexpr uint8_t reso_segment_width = 6;
53 constexpr uint8_t freq_slope_width = 5;
54 constexpr uint8_t padding_x = 5;
55 const uint8_t total_width = slots.width - 2 - padding_x * 2;
56 const uint8_t base_width = total_width - freq_slope_width - reso_segment_width;
58 uint8_t min_x = slots.start_x + padding_x;
59 uint8_t max_x = min_x + total_width;
60 uint8_t reso_x0 = min_x - reso_segment_width + base_width * freq_value;
61 uint8_t reso_x1 = reso_x0 + reso_segment_width;
62 uint8_t reso_x2 = reso_x1 + reso_segment_width;
63 int8_t left_slope_x0 = reso_x0 - base_width - freq_slope_width;
64 int8_t left_slope_x1 = left_slope_x0 + freq_slope_width;
65 uint8_t right_slope_x0 = reso_x2;
66 uint8_t right_slope_x1 = right_slope_x0 + freq_slope_width;
68 if (morph_value > 0) {
69 const uint8_t slope_shift = std::lerp(0, total_width, morph_value);
70 const uint8_t reso_shift = std::lerp(0, freq_slope_width + reso_segment_width, morph_value);
71 left_slope_x0 += slope_shift;
72 left_slope_x1 += slope_shift;
73 right_slope_x0 += slope_shift;
74 right_slope_x1 += slope_shift;
75 reso_x0 += reso_shift;
76 reso_x1 += reso_shift;
77 reso_x2 += reso_shift;
80 constexpr uint8_t container_height = 23;
81 const uint8_t start_y = slots.start_y + 1;
82 const uint8_t end_y = start_y + container_height - 1;
83 const uint8_t center_y = start_y + (end_y - start_y) / 2;
84 const uint8_t reso_y = std::lerp(center_y, start_y, reso_value);
87 image.drawRectangleRounded(min_x - 1, start_y - 1, max_x + 1, end_y + 1);
89 auto draw_segment = [&](int32_t x0, int32_t y0, int32_t x1, int32_t y1) {
92 if (last_point.x != point.x && point.x % 3 == 0) {
93 for (int32_t y = point.y; y <= end_y; y++) {
95 image.drawPixel(point.x, y);
101 image.drawLine(x0, y0, x1, y1, {.min_x = min_x, .max_x = max_x, .point_callback = draw_fill_pattern});
105 draw_segment(left_slope_x0, end_y, left_slope_x1, center_y);
108 draw_segment(left_slope_x1, center_y, reso_x0, center_y);
111 draw_segment(reso_x0, center_y, reso_x1, reso_y);
112 draw_segment(reso_x1, reso_y, reso_x2, center_y);
113 draw_segment(reso_x2, center_y, right_slope_x0, center_y);
116 draw_segment(right_slope_x0, center_y, right_slope_x1, end_y);
124 int32_t freq_value{0};
125 int32_t reso_value{0};
126 int32_t morph_value{0};
127 bool is_morphable{
false};
132 if (morph_item_ !=
nullptr) {
134 auto freq_item =
static_cast<FilterParam*
>(items_[0]);
135 auto reso_item =
static_cast<FilterParam*
>(items_[1]);
136 bool is_morphable = morph_item_->getFilterInfo().isMorphable();
137 bool is_hpf = freq_item->getP() == params::LOCAL_HPF_FREQ;
138 return {freq_item->getValue(), reso_item->getValue(), morph_item_->getValue(), is_morphable, is_hpf};
142 auto freq_item =
static_cast<UnpatchedFilterParam*
>(items_[0]);
143 auto reso_item =
static_cast<UnpatchedFilterParam*
>(items_[1]);
144 bool is_morphable = morph_item_unpatched_->getFilterInfo().isMorphable();
145 bool is_hpf = freq_item->getP() == params::UNPATCHED_HPF_FREQ;
146 return {freq_item->getValue(), reso_item->getValue(), morph_item_unpatched_->getValue(), is_morphable, is_hpf};