[vlc-commits] [Git][videolan/vlc][master] ncurses: add equalizer UI

Steve Lhomme (@robUx4) gitlab at videolan.org
Mon Aug 3 19:40:30 UTC 2026



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
9d53af00 by Piotr Danecki at 2026-08-03T19:23:49+00:00
ncurses: add equalizer UI

- - - - -


1 changed file:

- modules/gui/ncurses.c


Changes:

=====================================
modules/gui/ncurses.c
=====================================
@@ -91,6 +91,7 @@ vlc_module_begin ()
 
 vlc_module_end()
 
+#include "audio_filter/equalizer_presets.h"
 #include "eject.c"
 
 /*****************************************************************************
@@ -108,7 +109,9 @@ enum
     BOX_BROWSE,
     BOX_FSEARCH,
     BOX_META,
-    BOX_STATS
+    BOX_STATS,
+    BOX_EQUALIZER,
+    BOX_PRESETS,
 };
 
 static const char box_title[][19] = {
@@ -123,6 +126,8 @@ static const char box_title[][19] = {
     [BOX_FSEARCH]   = " Browse ",
     [BOX_META]      = " Meta-information ",
     [BOX_STATS]     = " Stats ",
+    [BOX_EQUALIZER] = " Equalizer ",
+    [BOX_PRESETS]   = " Presets ",
 };
 
 enum
@@ -138,6 +143,7 @@ enum
     C_CATEGORY,
     C_FOLDER,
     C_PROGRESS,
+    C_BANDS,
     /* XXX: new elements here ! */
 
     C_PLAYLIST,
@@ -204,6 +210,13 @@ struct intf_sys_t
     pl_item_names               pl_item_names;
     bool                        need_update;
     bool                        plidx_follow;
+
+    /* Equalizer context */
+    audio_output_t * paout;
+    int preset_id; // 0 - NB_PRESETS-1
+    float bands[EQZ_BANDS_MAX];
+    float preamp;
+    bool eq_enabled;
 };
 
 /*****************************************************************************
@@ -461,6 +474,9 @@ static const struct { short f; short b; } default_color_pairs[] =
     /* progress bar */
     [C_PROGRESS]    = { COLOR_WHITE,    COLOR_WHITE},
 
+    /* bands */
+    [C_BANDS]       = { COLOR_CYAN,     COLOR_WHITE },
+
     /* jamaican playlist, for rastafari sisters & brothers! */
     [C_PLAYLIST]    = { COLOR_GREEN,    COLOR_BLACK },
     [C_PLAYLIST + 1]= { COLOR_YELLOW,   COLOR_BLACK },
@@ -503,6 +519,8 @@ static const char * element_names[] = {
   [C_FOLDER] = "folder",
 
   [C_PROGRESS] = "progress",
+
+  [C_BANDS] = "bands",
 };
 
 static int get_element_id_from_string(const char * string) {
@@ -976,6 +994,7 @@ static int DrawHelp(intf_thread_t *intf)
     H(_(" P                      Show/Hide playlist box"));
     H(_(" B                      Show/Hide filebrowser"));
     H(_(" S                      Show/Hide statistics box"));
+    H(_(" E                      Show/Hide equalizer box"));
     H(_(" Esc                    Close Add/Search entry"));
     H(_(" Ctrl-l                 Refresh the screen"));
     H("");
@@ -1005,6 +1024,13 @@ static int DrawHelp(intf_thread_t *intf)
     H(_(" <start>,<end>          Navigate to start/end of box"));
     H("");
 
+    if (sys->color) color_set(C_CATEGORY, NULL);
+    H(_("[Equalizer]"));
+    if (sys->color) color_set(C_DEFAULT, NULL);
+    H(_(" +                      Increase value or enable field"));
+    H(_(" -                      Decrease value or disable field"));
+    H("");
+
     if (sys->color) color_set(C_CATEGORY, NULL);
     H(_("[Playlist]"));
     if (sys->color) color_set(C_DEFAULT, NULL);
@@ -1056,6 +1082,54 @@ static int DrawBrowse(intf_thread_t *intf)
     return sys->n_dir_entries;
 }
 
+static void DrawBandsBar(intf_sys_t * sys, int y, int v, float dB)
+{
+    if (sys->color)
+        color_set(C_BANDS, NULL);
+    DrawEmptyLine(y + sys->box_y, 20, 40);
+    attron(A_REVERSE);
+    DrawEmptyLine(y + sys->box_y, 20, v + 20);
+    attroff(A_REVERSE);
+    if (sys->color) {
+        color_set(C_DEFAULT, NULL);
+    }
+    if (y == sys->box_idx) attron(A_REVERSE);
+    mvnprintw(y + sys->box_y, 65, COLS-66, "%c%.1fdB", dB > 0 ? '+' : dB < 0 ? '-' : ' ', fabs(dB));
+    if (y == sys->box_idx) attroff(A_REVERSE);
+}
+
+static int DrawEqualizer(intf_thread_t *intf)
+{
+    intf_sys_t *sys = intf->p_sys;
+    int ret_size = 0;
+
+    MainBoxWrite(sys, ret_size++, _("Enabled: %c"), sys->eq_enabled ? 'x' : ' ');
+    MainBoxWrite(sys, ret_size, _("Preamp:"));
+    DrawBandsBar(sys, ret_size, sys->preamp, sys->preamp);
+    ret_size++;
+    MainBoxWrite(sys, ret_size++, _("Preset: %s (press + or - to open list)"), preset_list_text[sys->preset_id]);
+    MainBoxWrite(sys, ret_size++, _("Press + or - here to save"));
+    for (int i = 0; i < EQZ_BANDS_MAX; i++)
+    {
+        MainBoxWrite(sys, ret_size, _("%.1fHz Band:"), f_vlc_frequency_table_10b[i]);
+        DrawBandsBar(sys, ret_size, sys->bands[i], sys->bands[i]);
+        ret_size++;
+    }
+
+    return ret_size;
+}
+
+static int DrawPresets(intf_thread_t *intf)
+{
+    intf_sys_t * sys = intf->p_sys;
+    int ret_size = 0;
+    for (int i = 0; i < NB_PRESETS; i++)
+    {
+        MainBoxWrite(sys, ret_size++, "%s", preset_list_text[i]); 
+    }
+    return ret_size;
+}
+
 static int DrawPlaylist(intf_thread_t *intf)
 {
     intf_sys_t *sys = intf->p_sys;
@@ -1273,6 +1347,8 @@ static void FillBox(intf_thread_t *intf)
         [BOX_SEARCH]    = DrawPlaylist,
         [BOX_OPEN]      = DrawPlaylist,
         [BOX_LOG]       = DrawMessages,
+        [BOX_EQUALIZER] = DrawEqualizer,
+        [BOX_PRESETS]   = DrawPresets,
     };
 
     sys->box_lines_total = draw[sys->box_type](intf);
@@ -1397,15 +1473,297 @@ static void AddItem(intf_thread_t *intf, const char *path)
     input_item_Release(item);
 }
 
+static void EqualizerDeinit(intf_sys_t * sys);
 static inline void BoxSwitch(intf_sys_t *sys, int box)
 {
     int previous_box_type = sys->box_type;
     sys->box_type = (sys->box_type == box) ? sys->previous_box_type : box;
     sys->previous_box_type = previous_box_type;
+    if (sys->previous_box_type == BOX_EQUALIZER || sys->previous_box_type == BOX_PRESETS)
+        if (sys->box_type != BOX_EQUALIZER && sys->box_type != BOX_PRESETS)
+            EqualizerDeinit(sys);
     sys->box_start = 0;
     sys->box_idx = 0;
 }
 
+static void EqualizerUpdateBands(intf_sys_t * sys, char * bands_str)
+{
+    int bytes_read = 0;
+    for (int i = 0; i < EQZ_BANDS_MAX; i++)
+    {
+        lldiv_t d;
+        int read_now = 0;
+        if (sscanf(bands_str + bytes_read, "%lld.%llu %n", &d.quot, &d.rem, &read_now) != 2)
+            return;
+        bytes_read += read_now;
+        sys->bands[i] = (float)d.quot + (float)d.rem / 10000000.f;
+    }
+}
+
+static void EqualizerUpdatePreset(intf_sys_t * sys, const char * newname)
+{
+    for (int i = 0; i < NB_PRESETS; i++)
+    {
+        if (strcmp(newname, preset_list[i]) == 0)
+        {
+            sys->preset_id = i;
+            break;
+        }
+    }
+}
+
+// Initial sync
+static void EqualizerLoad(intf_sys_t * sys)
+{
+#define EnsureBegin(x) \
+{ \
+    char * var = NULL;\
+    if (sys->paout) \
+        var = var_GetString(sys->paout, x); \
+    if (!var) \
+        var = config_GetPsz(x); \
+    if (var) \
+    {
+#define EnsureEnd() \
+        free(var); \
+    } \
+}
+
+
+    EnsureBegin("equalizer-bands")
+    EqualizerUpdateBands(sys, var);
+    EnsureEnd();
+
+    if (sys->paout)
+        sys->preamp = var_GetFloat(sys->paout, "equalizer-preamp");
+
+    EnsureBegin("audio-filter")
+    sys->eq_enabled = strstr(var, "equalizer");
+    EnsureEnd()
+
+    EnsureBegin("equalizer-preset")
+    EqualizerUpdatePreset(sys, var);
+    EnsureEnd();
+}
+
+static int AudioFilterCallback(vlc_object_t * pobj, const char * pvar, vlc_value_t old, vlc_value_t new, void *vsys)
+{
+    (void)pobj, (void)pvar, (void)old;
+    intf_sys_t * sys = (intf_sys_t *)vsys;
+    sys->eq_enabled = new.psz_string && strstr(new.psz_string, "equalizer");
+    return VLC_SUCCESS;
+}
+
+static int EqualizerBandsCallback(vlc_object_t * pobj, const char * pvar, vlc_value_t old, vlc_value_t new, void *vsys)
+{
+    (void)pobj, (void)pvar, (void)old;
+    intf_sys_t * sys = (intf_sys_t *)vsys;
+
+    if (new.psz_string)
+        EqualizerUpdateBands(sys, new.psz_string);
+    return VLC_SUCCESS;
+}
+
+static int EqualizerPreampCallback(vlc_object_t * pobj, const char * pvar, vlc_value_t old, vlc_value_t new, void *vsys)
+{
+    (void)pobj, (void)pvar, (void)old;
+    intf_sys_t * sys = (intf_sys_t *)vsys;
+
+    sys->preamp = new.f_float;
+    return VLC_SUCCESS;
+}
+
+static int EqualizerPresetCallback(vlc_object_t * pobj, const char * pvar, vlc_value_t old, vlc_value_t new, void *vsys)
+{
+    (void)pobj, (void)pvar, (void)old;
+    intf_sys_t * sys = (intf_sys_t *)vsys;
+    if (new.psz_string)
+        EqualizerUpdatePreset(sys, new.psz_string);
+    return VLC_SUCCESS;
+}
+
+// Add var callbacks to sys paout
+static void EqualizerSysPaoutInit(intf_sys_t * sys)
+{
+    if (!sys->paout)
+        return;
+
+    if (!var_Type(sys->paout, "equalizer-bands"))
+        var_Create(sys->paout, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
+    if (!var_Type(sys->paout, "equalizer-preamp"))
+        var_Create(sys->paout, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
+    var_AddCallback(sys->paout, "audio-filter", AudioFilterCallback, sys);
+    var_AddCallback(sys->paout, "equalizer-bands", EqualizerBandsCallback, sys);
+    var_AddCallback(sys->paout, "equalizer-preamp", EqualizerPreampCallback, sys);
+    var_AddCallback(sys->paout, "equalizer-preset", EqualizerPresetCallback, sys);
+}
+
+// Deals with initialization
+static void EqualizerEnter(intf_sys_t * sys)
+{
+    vlc_player_t * player = vlc_playlist_GetPlayer(sys->playlist);
+    audio_output_t* paout = vlc_player_aout_Hold(player);
+    if (!sys->paout)
+    {
+        sys->paout = paout;
+        EqualizerSysPaoutInit(sys);
+        EqualizerLoad(sys);
+        return;
+    }
+    if (sys->paout == paout)
+    {
+        aout_Release(paout);
+        return;
+    }
+    if (sys->paout != paout)
+    {
+        EqualizerDeinit(sys);
+
+        sys->paout = paout;
+        EqualizerSysPaoutInit(sys);
+        EqualizerLoad(sys);
+    }
+}
+
+// Remove var callbacks
+static void EqualizerDeinit(intf_sys_t * sys)
+{
+    if (!sys->paout)
+        return;
+    var_DelCallback(sys->paout, "audio-filter", AudioFilterCallback, sys);
+    var_DelCallback(sys->paout, "equalizer-bands", EqualizerBandsCallback, sys);
+    var_DelCallback(sys->paout, "equalizer-preamp", EqualizerPreampCallback, sys);
+    var_DelCallback(sys->paout, "equalizer-preset", EqualizerPresetCallback, sys);
+    aout_Release(sys->paout);
+    sys->paout = NULL;
+}
+
+static void EqualizerEnable(intf_sys_t * sys, int diff, audio_output_t * paout)
+{
+    (void)diff, (void)paout;
+    vlc_player_t * player = vlc_playlist_GetPlayer(sys->playlist);
+    if (diff > 0) {
+        vlc_player_aout_EnableFilter(player, "equalizer", true);
+    }
+    else if (diff < 0) {
+        vlc_player_aout_EnableFilter(player, "equalizer", false);
+    }
+}
+
+static void EqualizerPreamp(intf_sys_t * sys, int diff, audio_output_t * paout)
+{
+    sys->preamp += diff;
+    sys->preamp = sys->preamp > 20 ? 20 : sys->preamp;
+    sys->preamp = sys->preamp < -20 ? -20 : sys->preamp;
+    var_SetFloat(paout, "equalizer-preamp", sys->preamp);
+}
+
+static void EqualizerPreset(intf_sys_t * sys, int diff, audio_output_t * paout)
+{
+    (void)diff, (void)paout;
+    BoxSwitch(sys, BOX_PRESETS);
+}
+
+static void EqualizerSaveConfig(intf_sys_t * sys, int diff, audio_output_t * paout)
+{
+    (void)sys, (void)diff;
+    char * internal;
+#define save_var(x) \
+    if ((internal = var_GetString(paout, x))) { \
+        config_PutPsz(x, internal); \
+        free(internal); \
+    }
+
+    save_var("audio-filter");
+    save_var("equalizer-preset");
+    save_var("equalizer-bands");
+    config_PutFloat("equalizer-preamp", var_GetFloat(paout, "equalizer-preamp"));
+#undef save_var
+}
+
+static void EqualizerSetBands(intf_sys_t * sys, audio_output_t * paout)
+{
+    char *bands = NULL;
+
+    for (unsigned i = 0; i < EQZ_BANDS_MAX; i++)
+    {
+        char *psz;
+
+        lldiv_t d = lldiv(lroundf(sys->bands[i] * 10000000.f), 10000000);
+
+        if (asprintf(&psz, "%s %lld.%07llu", i ? bands : "", d.quot, d.rem) == -1)
+            psz = NULL;
+
+        free(bands);
+        if (psz == NULL)
+            return;
+        bands = psz;
+    }
+
+    var_SetString(paout, "equalizer-bands", bands);
+    free(bands);
+}
+
+static void EqualizerBand(intf_sys_t * sys, int diff, audio_output_t * paout)
+{
+    float * band = &sys->bands[sys->box_idx - 4];
+    *band += diff;
+    *band = *band > 20 ? 20 : *band;
+    *band = *band < -20 ? -20 : *band;
+    EqualizerSetBands(sys, paout);
+}
+
+static bool HandleEqualizerKey(intf_thread_t *intf, int key)
+{
+    intf_sys_t *sys = intf->p_sys;
+
+    static void (* const handle[])(intf_sys_t *, int, audio_output_t * paout) = {
+        [0] = EqualizerEnable,
+        [1] = EqualizerPreamp,
+        [2] = EqualizerPreset,
+        [3] = EqualizerSaveConfig,
+    };
+
+    bool handled = false;
+    switch(key)
+    {
+        case '+':
+            handled = true;
+            if (sys->box_idx <= 3)
+                handle[sys->box_idx](sys, 1, sys->paout);
+            else
+                EqualizerBand(sys, 1, sys->paout);
+            break;
+        case '-':
+            handled = true;
+            if (sys->box_idx <= 3)
+                handle[sys->box_idx](sys, -1, sys->paout);
+            else
+                EqualizerBand(sys, -1, sys->paout);
+            break;
+    }
+    return handled;
+}
+
+static bool HandlePresetsKey(intf_thread_t *intf, int key)
+{
+    intf_sys_t * sys = intf->p_sys;
+
+    switch (key)
+    {
+        case KEY_ENTER:
+        case '\r':
+        case '\n':
+        case ' ':
+            sys->preset_id = sys->box_idx;
+            if (sys->paout)
+                var_SetString(sys->paout, "equalizer-preset", preset_list[sys->preset_id]);
+            BoxSwitch(sys, BOX_EQUALIZER);
+            return true;
+    }
+    return false;
+}
+
 static bool HandlePlaylistKey(intf_thread_t *intf, int key)
 {
     intf_sys_t *sys = intf->p_sys;
@@ -1662,6 +2020,7 @@ static void HandleCommonKey(intf_thread_t *intf, vlc_player_t *player, int key)
     case 'P': BoxSwitch(sys, BOX_PLAYLIST);   return;
     case 'B': BoxSwitch(sys, BOX_BROWSE);     return;
     case 'S': BoxSwitch(sys, BOX_STATS);      return;
+    case 'E': EqualizerEnter(sys); BoxSwitch(sys, BOX_EQUALIZER);  return;
 
     case '/': /* Search playlist or in file browser */
         sys->plidx_follow = false;
@@ -1843,6 +2202,12 @@ static void HandleKey(intf_thread_t *intf)
     if (box == BOX_PLAYLIST && HandlePlaylistKey(intf, key))
         return;
 
+    if (box == BOX_EQUALIZER && HandleEqualizerKey(intf, key))
+        return;
+
+    if (box == BOX_PRESETS && HandlePresetsKey(intf, key))
+        return;
+
     if (HandleListKey(intf, key))
         return;
 
@@ -1997,6 +2362,8 @@ static void Close(vlc_object_t *p_this)
     atomic_store_explicit(&sys->alive, false, memory_order_relaxed);
     vlc_join(sys->thread, NULL);
 
+    EqualizerDeinit(sys);
+
     vlc_playlist_t *playlist = sys->playlist;
     vlc_playlist_Lock(playlist);
     vlc_playlist_RemoveListener(playlist, sys->playlist_listener);



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/9d53af008ebaa6d49f65b109dc47b38766d58fc3

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/9d53af008ebaa6d49f65b109dc47b38766d58fc3
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list