diff --git a/apps/gui/option_select.c b/apps/gui/option_select.c
index d42d08188a..0452467994 100644
--- a/apps/gui/option_select.c
+++ b/apps/gui/option_select.c
@@ -345,7 +345,8 @@ static int selection_to_val(const struct settings_list *setting, int selection)
int setting_id = setting->sound_setting->setting;
#ifndef ASCENDING_INT_SETTINGS
step = sound_steps(setting_id);
- max = sound_max(setting_id);
+ max = (setting_id == SOUND_VOLUME) ?
+ global_settings.volume_limit : sound_max(setting_id);
/* min = sound_min(setting_id); */
#else
step = -sound_steps(setting_id);
@@ -429,7 +430,8 @@ static void val_to_selection(const struct settings_list *setting, int oldvalue,
int setting_id = setting->sound_setting->setting;
int steps = sound_steps(setting_id);
int min = sound_min(setting_id);
- int max = sound_max(setting_id);
+ int max = (setting_id == SOUND_VOLUME) ?
+ global_settings.volume_limit : sound_max(setting_id);
*nb_items = (max-min)/steps + 1;
#ifndef ASCENDING_INT_SETTINGS
*selected = (max - oldvalue) / steps;
diff --git a/apps/lang/chinese-simp.lang b/apps/lang/chinese-simp.lang
index 4c3dd3c18f..e90ec510d2 100644
--- a/apps/lang/chinese-simp.lang
+++ b/apps/lang/chinese-simp.lang
@@ -13039,3 +13039,17 @@
*: "扫描媒体时扫描指定文件夹"
+
+ id: LANG_VOLUME_LIMIT
+ desc: in sound_settings
+ user: core
+
+
+ *: "音量限制"
+
+
+ *: "音量限制"
+
+
diff --git a/apps/lang/chinese-trad.lang b/apps/lang/chinese-trad.lang
index f8dc5c38d6..0d0b68d1e5 100644
--- a/apps/lang/chinese-trad.lang
+++ b/apps/lang/chinese-trad.lang
@@ -11240,3 +11240,17 @@
*: "檔案大小"
+
+ id: LANG_VOLUME_LIMIT
+ desc: in sound_settings
+ user: core
+
+
+ *: "音量限制"
+
+
+ *: "音量限制"
+
+
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index dcad532f7a..d7f4476be6 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -13178,3 +13178,17 @@
*: "Slow"
+
+ id: LANG_VOLUME_LIMIT
+ desc: in sound_settings
+ user: core
+
+
+ *: "Maximum Volume Limit"
+
+
+ *: "Maximum Volume Limit"
+
+
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index 28cc257193..8913266a5f 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -34,10 +34,50 @@
#include "menu_common.h"
#include "splash.h"
#include "kernel.h"
+#include "talk.h"
+#include "option_select.h"
+#include "misc.h"
+
+static int32_t get_dec_talkid(int value, int unit)
+{
+ return TALK_ID_DECIMAL(value, 1, unit);
+}
+
+static int volume_limit_callback(int action,const struct menu_item_ex *this_item)
+{
+ (void)this_item;
+
+ static struct int_setting volume_limit_int_setting;
+ volume_limit_int_setting.option_callback = NULL;
+ volume_limit_int_setting.unit = UNIT_DB;
+ volume_limit_int_setting.min = sound_min(SOUND_VOLUME);
+ volume_limit_int_setting.max = sound_max(SOUND_VOLUME);
+ volume_limit_int_setting.step = sound_steps(SOUND_VOLUME);
+ volume_limit_int_setting.formatter = NULL;
+ volume_limit_int_setting.get_talk_id = get_dec_talkid;
+
+ struct settings_list setting;
+ setting.flags = F_BANFROMQS|F_INT_SETTING|F_T_INT|F_NO_WRAP;
+ setting.lang_id = LANG_VOLUME_LIMIT;
+ setting.default_val.int_ = sound_max(SOUND_VOLUME);
+ setting.int_setting = &volume_limit_int_setting;
+
+ switch (action)
+ {
+ case ACTION_ENTER_MENUITEM:
+ setting.setting = &global_settings.volume_limit;
+ option_screen(&setting, NULL, false, ID2P(LANG_VOLUME_LIMIT));
+ case ACTION_EXIT_MENUITEM: /* on exit */
+ setvol();
+ break;
+ }
+ return action;
+}
/***********************************/
/* SOUND MENU */
MENUITEM_SETTING(volume, &global_settings.volume, NULL);
+MENUITEM_SETTING(volume_limit, &global_settings.volume_limit, volume_limit_callback);
#ifdef AUDIOHW_HAVE_BASS
MENUITEM_SETTING(bass, &global_settings.bass,
#ifdef HAVE_SW_TONE_CONTROLS
@@ -171,6 +211,7 @@ static int timestretch_callback(int action,const struct menu_item_ex *this_item)
MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, Icon_Audio,
&volume
+ ,&volume_limit
#ifdef AUDIOHW_HAVE_BASS
,&bass
#endif
diff --git a/apps/misc.c b/apps/misc.c
index e746c432e6..fa33bb5c4f 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -793,6 +793,9 @@ void setvol(void)
global_settings.volume = min_vol;
if (global_settings.volume > max_vol)
global_settings.volume = max_vol;
+ if (global_settings.volume > global_settings.volume_limit)
+ global_settings.volume = global_settings.volume_limit;
+
sound_set_volume(global_settings.volume);
global_status.last_volume_change = current_tick;
settings_save();
diff --git a/apps/settings.h b/apps/settings.h
index 087ff0cb45..62ae038385 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -828,6 +828,7 @@ struct user_settings
#ifdef HAVE_PLAY_FREQ
int play_frequency; /* core audio output frequency selection */
#endif
+ int volume_limit; /* maximum volume limit */
};
/** global variables **/
diff --git a/apps/settings_list.c b/apps/settings_list.c
index bd2bfce36f..01bc5c53f4 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -661,9 +661,39 @@ static int32_t hotkey_getlang(int value, int unit)
return get_hotkey_lang_id(value);
}
#endif /* HAVE_HOTKEY */
+
+/* volume limiter */
+static void volume_limit_load_from_cfg(void* var, char*value)
+{
+ *(int*)var = atoi(value);
+}
+static char* volume_limit_write_to_cfg(void* setting, char*buf, int buf_len)
+{
+ int current = *(int*)setting;
+ snprintf(buf, buf_len, "%d", current);
+ return buf;
+}
+static bool volume_limit_is_changed(void* setting, void* defaultval)
+{
+ int current = *(int*)setting;
+
+ if ((int*)defaultval == NULL)
+ *(int*)defaultval = sound_max(SOUND_VOLUME);
+ return (current != sound_max(SOUND_VOLUME));
+}
+static void volume_limit_set_default(void* setting, void* defaultval)
+{
+ (void)defaultval;
+ *(int*)setting = sound_max(SOUND_VOLUME);
+}
+
const struct settings_list settings[] = {
/* sound settings */
- SOUND_SETTING(F_NO_WRAP,volume, LANG_VOLUME, "volume", SOUND_VOLUME),
+ SOUND_SETTING(F_NO_WRAP, volume, LANG_VOLUME, "volume", SOUND_VOLUME),
+ CUSTOM_SETTING(F_NO_WRAP, volume_limit, LANG_VOLUME_LIMIT,
+ NULL, "volume limit",
+ volume_limit_load_from_cfg, volume_limit_write_to_cfg,
+ volume_limit_is_changed, volume_limit_set_default),
SOUND_SETTING(0, balance, LANG_BALANCE, "balance", SOUND_BALANCE),
/* Tone controls */
#ifdef AUDIOHW_HAVE_BASS
diff --git a/manual/configure_rockbox/sound_settings.tex b/manual/configure_rockbox/sound_settings.tex
index d2da07b983..7022315c83 100644
--- a/manual/configure_rockbox/sound_settings.tex
+++ b/manual/configure_rockbox/sound_settings.tex
@@ -48,6 +48,12 @@ change to customise your listening experience.
\opt{iaudiom3,iaudiom5,iaudiox5,sansa,sansaAMS,iriverh10,iriverh10_5gb,vibe500,fuzeplus}{%
The minimum setting is -24~dB and the maximum is 24~dB.}
+\section{\label{ref:volume_limit}Volume Limit}
+ This setting adjusts the maximum volume of your music. The setting is by
+ default set to the maximum volume which equals to no limit. To set a volume
+ limit, select a volume from the list and the maximum volume will be limited to
+ the selected value all over the system.
+
\opt{ipodvideo}{
\section{Bass Cutoff}
This setting controls the frequency below which the bass adjustment applies.