Allow customizing EQ filter types
rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]> Thu, 30 Jul 2026 07:48:46 -0400
| Newsgroups | gmane.comp.systems.archos.rockbox.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit e764656ab7856b825d94a855455484e63c2089da Author: Skye <[email protected]> Date: Mon Jul 20 21:52:07 2026 +0900 Allow customizing EQ filter types Allows any EQ band to be set to any of Low Shelf, Peak, or High Shelf, instead of hardcoding the types per band. Change-Id: I470ab916359092ba465e7b6331baed3bf11b2fc9 diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 6ed795f602..a4092a606c 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -1091,10 +1091,10 @@ desc: in the equalizer settings menu user: core <source> - *: "Peak Filter %d" + *: "Peak Filter" </source> <dest> - *: "Peak Filter %d" + *: "Peak Filter" </dest> <voice> *: "Peak filter" @@ -17169,3 +17169,31 @@ *: "Finished" </voice> </phrase> +<phrase> + id: LANG_EQUALIZER_BAND + desc: in the equalizer settings menu + user: core + <source> + *: "Filter %d" + </source> + <dest> + *: "Filter %d" + </dest> + <voice> + *: "Filter" + </voice> +</phrase> +<phrase> + id: LANG_EQUALIZER_FILTER_TYPE + desc: in the equalizer settings menu + user: core + <source> + *: "Filter Type" + </source> + <dest> + *: "Filter Type" + </dest> + <voice> + *: "Filter Type" + </voice> +</phrase> diff --git a/apps/menus/eq_menu.c b/apps/menus/eq_menu.c index 267019035c..370f8bcad2 100644 --- a/apps/menus/eq_menu.c +++ b/apps/menus/eq_menu.c @@ -180,6 +180,17 @@ static const struct int_setting cutoff_int_setting = { .get_talk_id = NULL, }; +static const struct choice_setting type_choice_setting = { + .option_callback = NULL, + .count = 3, + .cfg_vals = "low_shelf,peaking,high_shelf", + {.desc = (const unsigned char*[]) { + ID2P(LANG_EQUALIZER_BAND_LOW_SHELF), + ID2P(LANG_EQUALIZER_BAND_PEAK), + ID2P(LANG_EQUALIZER_BAND_HIGH_SHELF), + }} +}; + static int simplelist_action_callback(int action, struct gui_synclist *lists) { (void)lists; @@ -234,7 +245,7 @@ static void selection_to_banditem(int selection, int expanded_band, int *band, i *item = 0; *band = selection; } - else if (diff < 4) + else if (diff < 5) { *item = selection - expanded_band; *band = expanded_band; @@ -242,7 +253,7 @@ static void selection_to_banditem(int selection, int expanded_band, int *band, i else { *item = 0; - *band = expanded_band + diff - 3; + *band = expanded_band + diff - 4; } } @@ -257,23 +268,13 @@ static char *advancedmenu_item_get_name(int selected_item, void *data, char *buf switch (item) { case 0: /* Band title */ - if (band == 0) - return str(LANG_EQUALIZER_BAND_LOW_SHELF); - else if (band == EQ_NUM_BANDS - 1) - return str(LANG_EQUALIZER_BAND_HIGH_SHELF); - else - { - snprintf(buffer, len, str(LANG_EQUALIZER_BAND_PEAK), band); - return buffer; - } - break; + snprintf(buffer, len, str(LANG_EQUALIZER_BAND), band); + return buffer; case 1: /* cutoff */ - if (band == 0) - lang = LANG_EQUALIZER_BAND_CUTOFF; - else if (band == EQ_NUM_BANDS - 1) - lang = LANG_EQUALIZER_BAND_CUTOFF; - else + if (global_settings.eq_band_settings[band].type == EQ_FILTER_PEAK) lang = LANG_EQUALIZER_BAND_CENTER; + else + lang = LANG_EQUALIZER_BAND_CUTOFF; break; case 2: /* Q */ lang = LANG_EQUALIZER_BAND_Q; @@ -281,6 +282,9 @@ static char *advancedmenu_item_get_name(int selected_item, void *data, char *buf case 3: /* Gain */ lang = LANG_GAIN; break; + case 4: /* Type */ + lang = LANG_EQUALIZER_FILTER_TYPE; + break; } if(lang < 0) @@ -305,24 +309,14 @@ static int advancedmenu_speak_item(int selected_item, void *data) switch (item) { case 0: /* Band title */ - if (band == 0) - lang = LANG_EQUALIZER_BAND_LOW_SHELF; - else if (band == EQ_NUM_BANDS - 1) - lang = LANG_EQUALIZER_BAND_HIGH_SHELF; - else - { - talk_id(LANG_EQUALIZER_BAND_PEAK, false); - talk_number(band, true); - return -1; - } - break; + talk_id(LANG_EQUALIZER_BAND, false); + talk_number(band, true); + return -1; case 1: /* cutoff */ - if (band == 0) - lang = LANG_EQUALIZER_BAND_CUTOFF; - else if (band == EQ_NUM_BANDS - 1) - lang = LANG_EQUALIZER_BAND_CUTOFF; - else + if (global_settings.eq_band_settings[band].type == EQ_FILTER_PEAK) lang = LANG_EQUALIZER_BAND_CENTER; + else + lang = LANG_EQUALIZER_BAND_CUTOFF; break; case 2: /* Q */ lang = LANG_EQUALIZER_BAND_Q; @@ -368,7 +362,6 @@ static int eq_do_advanced_menu(void * param) info.action_callback = simplelist_action_callback; info.selection = -1; info.title_icon = Icon_EQ; - setting.flags = F_BANFROMQS|F_INT_SETTING|F_T_INT|F_NO_WRAP; while (true) { @@ -388,7 +381,7 @@ static int eq_do_advanced_menu(void * param) } else { - extra = 3; + extra = 4; selected_band = band; } info.selection = band; @@ -396,7 +389,8 @@ static int eq_do_advanced_menu(void * param) continue; } case 1: /* cutoff */ - if (band == 0 || band == EQ_NUM_BANDS - 1) + setting.flags = F_BANFROMQS|F_INT_SETTING|F_T_INT|F_NO_WRAP; + if (global_settings.eq_band_settings[band].type != EQ_FILTER_PEAK) setting.lang_id = LANG_EQUALIZER_BAND_CUTOFF; else setting.lang_id = LANG_EQUALIZER_BAND_CENTER; @@ -405,17 +399,26 @@ static int eq_do_advanced_menu(void * param) setting.setting = &global_settings.eq_band_settings[band].cutoff; break; case 2: /* Q */ + setting.flags = F_BANFROMQS|F_INT_SETTING|F_T_INT|F_NO_WRAP; setting.lang_id = LANG_EQUALIZER_BAND_Q; setting.default_val.int_ = eq_defaults[band].q; setting.int_setting = &q_int_setting; setting.setting = &global_settings.eq_band_settings[band].q; break; case 3: /* Gain */ + setting.flags = F_BANFROMQS|F_INT_SETTING|F_T_INT|F_NO_WRAP; setting.lang_id = LANG_GAIN; setting.default_val.int_ = eq_defaults[band].gain; setting.int_setting = &gain_int_setting; setting.setting = &global_settings.eq_band_settings[band].gain; break; + case 4: /* Type */ + setting.flags = F_BANFROMQS|F_CHOICE_SETTING|F_T_INT; + setting.lang_id = LANG_EQUALIZER_FILTER_TYPE; + setting.default_val.int_ = eq_defaults[band].type; + setting.choice_setting = &type_choice_setting; + setting.setting = &global_settings.eq_band_settings[band].type; + break; } pcmbuf_set_low_latency(true); advancedmenu_item_get_name(info.selection, &selected_band, title, MAX_PATH); @@ -430,21 +433,17 @@ MENUITEM_FUNCTION(advanced_menu, 0, ID2P(LANG_EQUALIZER_ADVANCED), eq_do_advanced_menu, NULL, Icon_EQ); enum eq_slider_mode { + TYPE, GAIN, CUTOFF, Q, }; -enum eq_type { - LOW_SHELF, - PEAK, - HIGH_SHELF -}; - /* Draw the UI for a whole EQ band */ static int draw_eq_slider(struct screen * screen, int x, int y, int width, int cutoff, int q, int gain, bool selected, - enum eq_slider_mode mode, int band, int scrollbar_size) + enum eq_slider_mode mode, int band, int scrollbar_size, + enum eq_filter_type type) { char buf[26]; int steps, min_item, max_item; @@ -453,6 +452,11 @@ static int draw_eq_slider(struct screen * screen, int x, int y, int w, h; switch(mode) { + case TYPE: + steps = 5; + min_item = (type * 2); + max_item = (type * 2) + 1; + break; case Q: steps = EQ_Q_MAX - EQ_Q_MIN; min_item = q - EQ_Q_STEP - EQ_Q_MIN; @@ -476,11 +480,17 @@ static int draw_eq_slider(struct screen * screen, int x, int y, y1 = y + 2; /* Print out the band label */ - if (band == 0) { - screen->putsxy(x1, y1, "LS: "); + if (mode == TYPE && selected) + screen->set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); + else + screen->set_drawmode(DRMODE_SOLID); + if (type == EQ_FILTER_LOW_SHELF) { + snprintf(buf, sizeof(buf), "LS%d:", band); + screen->putsxy(x1, y1, buf); /*screen->getstringsize("LS:", &w, &h); UNUSED*/ - } else if (band == EQ_NUM_BANDS - 1) { - screen->putsxy(x1, y1, "HS: "); + } else if (type == EQ_FILTER_HIGH_SHELF) { + snprintf(buf, sizeof(buf), "HS%d:", band); + screen->putsxy(x1, y1, buf); /*screen->getstringsize("HS:", &w, &h); UNUSED*/ } else { snprintf(buf, sizeof(buf), "PK%d:", band); @@ -557,6 +567,7 @@ static void draw_eq_sliders(struct screen * screen, int x, int y, int cutoff = setting->cutoff; int q = setting->q; int gain = setting->gain; + enum eq_filter_type type = setting->type; if (i == start_item + nb_eq_sliders) break; @@ -564,7 +575,7 @@ static void draw_eq_sliders(struct screen * screen, int x, int y, if (i >= start_item) { height += draw_eq_slider(screen, x, height, screen->lcdwidth - x - 1, cutoff, q, gain, i == current_band, mode, - i, scrollbar_size); + i, scrollbar_size, type); /* add a margin */ height++; } @@ -629,7 +640,18 @@ int eq_menu_graphical(void) screens[i].clear_display(); /* Set pointer to the band data currently editable */ - if (mode == GAIN) { + if (mode == TYPE) { + /* type */ + setting = (int*) &global_settings.eq_band_settings[current_band].type; + + step = 1; + fast_step = 1; + min = EQ_FILTER_LOW_SHELF; + max = EQ_FILTER_HIGH_SHELF; + + screens[i].putsxyf(0, 0, str(LANG_SYSFONT_EQUALIZER_EDIT_MODE), + str(LANG_EQUALIZER_FILTER_TYPE), ""); + } else if (mode == GAIN) { /* gain */ setting = &global_settings.eq_band_settings[current_band].gain; @@ -734,7 +756,7 @@ int eq_menu_graphical(void) case ACTION_STD_OK: mode++; if (mode > Q) - mode = GAIN; /* wrap around */ + mode = TYPE; /* wrap around */ break; case ACTION_STD_CANCEL: @@ -785,6 +807,7 @@ static struct browse_folder_info eqs = { EQS_DIR, SHOW_CFG }; static void eq_reset_defaults(void) { for (int i = 0; i < EQ_NUM_BANDS; i++) { + global_settings.eq_band_settings[i].type = eq_defaults[i].type; global_settings.eq_band_settings[i].cutoff = eq_defaults[i].cutoff; global_settings.eq_band_settings[i].q = eq_defaults[i].q; global_settings.eq_band_settings[i].gain = eq_defaults[i].gain; diff --git a/apps/settings_list.c b/apps/settings_list.c index 42d4dc4245..6821671d05 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c @@ -676,16 +676,16 @@ static int32_t get_precut_talkid(int value, int unit) #endif /* __PCTOOL__ */ struct eq_band_setting eq_defaults[EQ_NUM_BANDS] = { - { 32, 7, 0 }, - { 64, 10, 0 }, - { 125, 10, 0 }, - { 250, 10, 0 }, - { 500, 10, 0 }, - { 1000, 10, 0 }, - { 2000, 10, 0 }, - { 4000, 10, 0 }, - { 8000, 10, 0 }, - { 16000, 7, 0 }, + { EQ_FILTER_LOW_SHELF, 32, 7, 0 }, + { EQ_FILTER_PEAK, 64, 10, 0 }, + { EQ_FILTER_PEAK, 125, 10, 0 }, + { EQ_FILTER_PEAK, 250, 10, 0 }, + { EQ_FILTER_PEAK, 500, 10, 0 }, + { EQ_FILTER_PEAK, 1000, 10, 0 }, + { EQ_FILTER_PEAK, 2000, 10, 0 }, + { EQ_FILTER_PEAK, 4000, 10, 0 }, + { EQ_FILTER_PEAK, 8000, 10, 0 }, + { EQ_FILTER_HIGH_SHELF, 16000, 7, 0 }, }; static const int wps_context_menu_default = @@ -704,7 +704,7 @@ static const int tree_hotkey_default = HOTKEY_OFF; #endif #ifndef __PCTOOL__ -static void eq_load_from_cfg(void *setting, char *value) +static void eq_load_from_cfg(void *setting, char *value, bool new) { struct eq_band_setting *eq = setting; char *val_end, *end; @@ -728,14 +728,68 @@ static void eq_load_from_cfg(void *setting, char *value) /* gain */ value = end + 1; if (value > val_end) return; + if (new) + { + end = strchr(value, ','); + if (!end) return; + } eq->gain = atoi(value); + + if (!new) return; + + /* type */ + value = end + 1; + if (value > val_end) return; + value = skip_whitespace(value); + if (strcasecmp(value, "LOW_SHELF") == 0) + eq->type = EQ_FILTER_LOW_SHELF; + else if (strcasecmp(value, "PEAK") == 0) + eq->type = EQ_FILTER_PEAK; + else if (strcasecmp(value, "HIGH_SHELF") == 0) + eq->type = EQ_FILTER_HIGH_SHELF; +} + +static void eq_load_from_cfg_old_low_shelf(void *setting, char *value) +{ + eq_load_from_cfg(setting, value, false); + ((struct eq_band_setting *)setting)->type = EQ_FILTER_LOW_SHELF; +} + +static void eq_load_from_cfg_old_peak(void *setting, char *value) +{ + eq_load_from_cfg(setting, value, false); + ((struct eq_band_setting *)setting)->type = EQ_FILTER_PEAK; +} + +static void eq_load_from_cfg_old_high_shelf(void *setting, char *value) +{ + eq_load_from_cfg(setting, value, false); + ((struct eq_band_setting *)setting)->type = EQ_FILTER_HIGH_SHELF; +} + +static void eq_load_from_cfg_new(void *setting, char *value) +{ + eq_load_from_cfg(setting, value, true); } static char* eq_write_to_cfg(void *setting, char *buf, int buf_len) { struct eq_band_setting *eq = setting; - snprintf(buf, buf_len, "%d, %d, %d", eq->cutoff, eq->q, eq->gain); + const char *type = "PEAK"; + switch (eq->type) + { + case EQ_FILTER_LOW_SHELF: + type = "LOW_SHELF"; + break; + case EQ_FILTER_PEAK: + type = "PEAK"; + break; + case EQ_FILTER_HIGH_SHELF: + type = "HIGH_SHELF"; + break; + } + snprintf(buf, buf_len, "%d, %d, %d, %s", eq->cutoff, eq->q, eq->gain, type); return buf; } @@ -1835,21 +1889,39 @@ const struct settings_list settings[] = { "eq precut", UNIT_DB, 0, 240, 1, eq_precut_format, get_precut_talkid, dsp_set_eq_precut), + +#define EQ_BAND_OLD(id, string, type) \ + CUSTOM_SETTING(F_EQSETTING|F_DEPRECATED, eq_band_settings[id], -1, \ + &eq_defaults[id], string, \ + eq_load_from_cfg_old_##type, eq_write_to_cfg, \ + eq_is_changed, eq_set_default) + EQ_BAND_OLD(0, "eq low shelf filter", low_shelf), + EQ_BAND_OLD(1, "eq peak filter 1", peak), + EQ_BAND_OLD(2, "eq peak filter 2", peak), + EQ_BAND_OLD(3, "eq peak filter 3", peak), + EQ_BAND_OLD(4, "eq peak filter 4", peak), + EQ_BAND_OLD(5, "eq peak filter 5", peak), + EQ_BAND_OLD(6, "eq peak filter 6", peak), + EQ_BAND_OLD(7, "eq peak filter 7", peak), + EQ_BAND_OLD(8, "eq peak filter 8", peak), + EQ_BAND_OLD(9, "eq high shelf filter", high_shelf), +#undef EQ_BAND_OLD + #define EQ_BAND(id, string) \ CUSTOM_SETTING(F_EQSETTING, eq_band_settings[id], -1, \ &eq_defaults[id], string, \ - eq_load_from_cfg, eq_write_to_cfg, \ + eq_load_from_cfg_new, eq_write_to_cfg, \ eq_is_changed, eq_set_default) - EQ_BAND(0, "eq low shelf filter"), - EQ_BAND(1, "eq peak filter 1"), - EQ_BAND(2, "eq peak filter 2"), - EQ_BAND(3, "eq peak filter 3"), - EQ_BAND(4, "eq peak filter 4"), - EQ_BAND(5, "eq peak filter 5"), - EQ_BAND(6, "eq peak filter 6"), - EQ_BAND(7, "eq peak filter 7"), - EQ_BAND(8, "eq peak filter 8"), - EQ_BAND(9, "eq high shelf filter"), + EQ_BAND(0, "eq filter 0"), + EQ_BAND(1, "eq filter 1"), + EQ_BAND(2, "eq filter 2"), + EQ_BAND(3, "eq filter 3"), + EQ_BAND(4, "eq filter 4"), + EQ_BAND(5, "eq filter 5"), + EQ_BAND(6, "eq filter 6"), + EQ_BAND(7, "eq filter 7"), + EQ_BAND(8, "eq filter 8"), + EQ_BAND(9, "eq filter 9"), #undef EQ_BAND /* dithering */ diff --git a/lib/rbcodec/dsp/eq.c b/lib/rbcodec/dsp/eq.c index 81c4c972ec..a0839c9de0 100644 --- a/lib/rbcodec/dsp/eq.c +++ b/lib/rbcodec/dsp/eq.c @@ -71,15 +71,22 @@ static void update_band_filter(int band, unsigned int fout) functions */ typeof (filter_pk_coefs) *coef_gen = filter_pk_coefs; - /* Only first and last bands are not peaking filters */ - if (band == 0) - coef_gen = filter_ls_coefs; - else if (band == EQ_NUM_BANDS-1) - coef_gen = filter_hs_coefs; - const struct eq_band_setting *setting = &settings[band]; struct dsp_filter *filter = &eq_data.filters[band]; + switch (setting->type) + { + case EQ_FILTER_LOW_SHELF: + coef_gen = filter_ls_coefs; + break; + case EQ_FILTER_PEAK: + coef_gen = filter_pk_coefs; + break; + case EQ_FILTER_HIGH_SHELF: + coef_gen = filter_hs_coefs; + break; + } + coef_gen(fp_div(setting->cutoff, fout, 32), setting->q ?: 1, setting->gain, filter); } diff --git a/lib/rbcodec/dsp/eq.h b/lib/rbcodec/dsp/eq.h index 6e02999933..419998ab8c 100644 --- a/lib/rbcodec/dsp/eq.h +++ b/lib/rbcodec/dsp/eq.h @@ -23,8 +23,16 @@ /* => support from 3 to 32 bands */ #define EQ_NUM_BANDS 10 +enum eq_filter_type +{ + EQ_FILTER_LOW_SHELF = 0, + EQ_FILTER_PEAK, + EQ_FILTER_HIGH_SHELF, +}; + struct eq_band_setting { + enum eq_filter_type type; int cutoff; /* Hz */ int q; int gain; /* +/- dB */ diff --git a/manual/configure_rockbox/sound_settings.tex b/manual/configure_rockbox/sound_settings.tex index 7e9d09b75e..50e6177093 100644 --- a/manual/configure_rockbox/sound_settings.tex +++ b/manual/configure_rockbox/sound_settings.tex @@ -315,7 +315,7 @@ change to customise your listening experience. \section{\label{ref:EQ}Equalizer} \screenshot{configure_rockbox/images/ss-equalizer}{The graphical equalizer}{} Rockbox features a parametric equalizer (EQ). In contrast to non-parametric - equalizers, a parametric EQ enables adjusting the center frequency, gain, and + equalizers, a parametric EQ enables adjusting the type, center frequency, gain, and width of EQ bands separately. The ability to adjust the frequency and width of bands enables more precise control of the EQ frequency response while avoiding the use of a large number of bands (often 12+) needed in a @@ -338,10 +338,11 @@ change to customise your listening experience. than are required will waste battery and introduce additional rounding noise. For best results, use the fewest number of bands required.} - Rockbox's parametric EQ is composed of up to ten different bands: + Rockbox's parametric EQ is composed of up to ten EQ bands consisting of any combination of three different filter types: + \begin{description} - \item[Band 0: Low shelf filter.] - The low shelf filter boosts or lowers all frequencies below a certain + \item[Low shelf filter.] + Low shelf filters boost or lower all frequencies below a certain frequency limit, much as the ``bass'' control found on ordinary stereo systems does. Adjust the ``cutoff'' frequency parameter to decide where the shelving @@ -354,12 +355,12 @@ change to customise your listening experience. The ``Q'' parameter should always be set to 0.7 for the shelving filters. Higher values will add a small boost around the cutoff frequency that is almost always undesirable. - \item[Bands 1-8: Peaking filters.] + \item[Peaking filters.] Peaking EQ filters boost or lower a frequency range centered at the centre frequency chosen. Graphic equalizers in home stereos are usually peaking filters. The peaking filters in Rockbox's EQ lets you adjust three - different parameters for EQ bands 1 through 8. The ``centre'' parameter + different parameters. The ``centre'' parameter controls the centre frequency of the frequency range that is affected as described above. The ``gain'' parameter controls how much each band is adjusted, and @@ -368,11 +369,11 @@ change to customise your listening experience. frequency range is. Higher Q values will affect a narrower band of frequencies, while lower Q values will affect a wider band of frequencies. - \item[Band 9: High shelf filter.] + \item[High shelf filter.] A high shelf filter boosts or lowers all frequencies above a certain frequency limit, much as the ``treble'' control found on ordinary stereo systems does. - The high shelf filter is adjusted the same way as the low shelf filter, + A high shelf filter is adjusted the same way as A low shelf filter, except that it works on the high end of the frequency spectrum rather than the low end. \end{description} @@ -385,7 +386,7 @@ change to customise your listening experience. \item[Graphical EQ.] This option brings up a graphic EQ screen, which allows adjustment of each of - the three parameters described above (gain, centre frequency, and Q) for each + the four parameters described above (gain, centre frequency, Q, and type) for each of the five EQ bands. \begin{btnmap} @@ -476,8 +477,8 @@ change to customise your listening experience. \opt{IRIVER_RC_H100_PAD}{\ButtonRCSelect} \opt{GIGABEAT_RC_PAD,IAUDIO_RC_PAD}{\ButtonRCPlay} &} - Toggles the cursor among the three parameters (gain, centre frequency, - Q) for the selected EQ band + Toggles the cursor among the four parameters (type, gain, centre + frequency, Q) for the selected EQ band \\ % \opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonMode} -- rockbox-cvs mailing list [email protected] https://lists.haxx.se/mailman/listinfo/rockbox-cvs