Add perceptual volume adjustment
The perceived loudness change of a change in volume depends on the listening volume: at high volumes a 1 dB increment is noticeable, but at low volumes a larger increment is needed to get a comparable change in loudness. Perceptual volume adjustment accounts for this fact, and divides the hardware volume range into a number of steps. Each step changes the dB volume by a variable amount, with most of the steps concentrated at higher volumes. This makes it possible to sweep over the entire hardware volume range quickly, without losing the ability to finely adjust the volume at normal listening levels. Use "Volume Adjustment Mode" in the system settings menu to select perceptual volume mode. The number of steps used is controlled by "Number of Volume Steps". (Number of steps has no effect in direct adjustment mode.) It's still possible to set a specific dB volume level from the sound settings menu when perceptual volume is enabled, and perceptual volume does not affect the volume displayed by themes. Change-Id: I6f91fd3f7c5e2d323a914e47b5653033e92b4b3b
This commit is contained in:
parent
15c4447b66
commit
5b27e2255a
17 changed files with 240 additions and 35 deletions
|
@ -296,3 +296,7 @@ hibylinux
|
|||
(CONFIG_KEYPAD == IRIVER_H10_PAD)
|
||||
clear_settings_on_hold
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PERCEPTUAL_VOLUME)
|
||||
perceptual_volume
|
||||
#endif
|
||||
|
|
|
@ -663,12 +663,10 @@ bool gui_synclist_do_button(struct gui_synclist * lists, int *actionptr)
|
|||
|
||||
#ifdef HAVE_VOLUME_IN_LIST
|
||||
case ACTION_LIST_VOLUP:
|
||||
global_settings.volume += sound_steps(SOUND_VOLUME);
|
||||
setvol();
|
||||
adjust_volume(1);
|
||||
return true;
|
||||
case ACTION_LIST_VOLDOWN:
|
||||
global_settings.volume -= sound_steps(SOUND_VOLUME);
|
||||
setvol();
|
||||
adjust_volume(-1);
|
||||
return true;
|
||||
#endif
|
||||
case ACTION_STD_PREV:
|
||||
|
|
|
@ -378,14 +378,12 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter
|
|||
else if (button == button_enter)
|
||||
can_quit = true;
|
||||
else if (button == ACTION_QS_VOLUP) {
|
||||
global_settings.volume += sound_steps(SOUND_VOLUME);
|
||||
setvol();
|
||||
adjust_volume(1);
|
||||
FOR_NB_SCREENS(i)
|
||||
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_NON_STATIC);
|
||||
}
|
||||
else if (button == ACTION_QS_VOLDOWN) {
|
||||
global_settings.volume -= sound_steps(SOUND_VOLUME);
|
||||
setvol();
|
||||
adjust_volume(-1);
|
||||
FOR_NB_SCREENS(i)
|
||||
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_NON_STATIC);
|
||||
}
|
||||
|
|
|
@ -841,9 +841,9 @@ long gui_wps_show(void)
|
|||
case ACTION_WPS_VOLUP: /* fall through */
|
||||
case ACTION_WPS_VOLDOWN:
|
||||
if (button == ACTION_WPS_VOLUP)
|
||||
global_settings.volume += sound_steps(SOUND_VOLUME);
|
||||
adjust_volume(1);
|
||||
else
|
||||
global_settings.volume -= sound_steps(SOUND_VOLUME);
|
||||
adjust_volume(-1);
|
||||
|
||||
setvol();
|
||||
FOR_NB_SCREENS(i)
|
||||
|
|
|
@ -15163,7 +15163,7 @@
|
|||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_DIRECT
|
||||
desc: in the pictureflow settings
|
||||
desc: in the pictureflow settings, also a volume adjustment mode
|
||||
user: core
|
||||
<source>
|
||||
*: "Direct"
|
||||
|
@ -16559,3 +16559,54 @@
|
|||
*: "Play Last Shuffled"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_VOLUME_ADJUST_MODE
|
||||
desc: in system settings
|
||||
user: core
|
||||
<source>
|
||||
*: none
|
||||
perceptual_volume: "Volume Adjustment Mode"
|
||||
</source>
|
||||
<dest>
|
||||
*: none
|
||||
perceptual_volume: "Volume Adjustment Mode"
|
||||
</dest>
|
||||
<voice>
|
||||
*: none
|
||||
perceptual_volume: "Volume Adjustment Mode"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_VOLUME_ADJUST_NORM_STEPS
|
||||
desc: in system settings
|
||||
user: core
|
||||
<source>
|
||||
*: none
|
||||
perceptual_volume: "Number of Volume Steps"
|
||||
</source>
|
||||
<dest>
|
||||
*: none
|
||||
perceptual_volume: "Number of Volume Steps"
|
||||
</dest>
|
||||
<voice>
|
||||
*: none
|
||||
perceptual_volume: "Number of Volume Steps"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_PERCEPTUAL
|
||||
desc: in system settings -> volume adjustment mode
|
||||
user: core
|
||||
<source>
|
||||
*: none
|
||||
perceptual_volume: "Perceptual"
|
||||
</source>
|
||||
<dest>
|
||||
*: none
|
||||
perceptual_volume: "Perceptual"
|
||||
</dest>
|
||||
<voice>
|
||||
*: none
|
||||
perceptual_volume: "Perceptual"
|
||||
</voice>
|
||||
</phrase>
|
||||
|
|
|
@ -338,6 +338,11 @@ MAKE_MENU(limits_menu, ID2P(LANG_LIMITS_MENU), 0, Icon_NOICON,
|
|||
,&default_glyphs
|
||||
);
|
||||
|
||||
#ifdef HAVE_PERCEPTUAL_VOLUME
|
||||
/* Volume adjustment */
|
||||
MENUITEM_SETTING(volume_adjust_mode, &global_settings.volume_adjust_mode, NULL);
|
||||
MENUITEM_SETTING(volume_adjust_norm_steps, &global_settings.volume_adjust_norm_steps, NULL);
|
||||
#endif
|
||||
|
||||
/* Keyclick menu */
|
||||
MENUITEM_SETTING(keyclick, &global_settings.keyclick, NULL);
|
||||
|
@ -424,6 +429,10 @@ MAKE_MENU(system_menu, ID2P(LANG_SYSTEM),
|
|||
&disk_menu,
|
||||
#endif
|
||||
&limits_menu,
|
||||
#ifdef HAVE_PERCEPTUAL_VOLUME
|
||||
&volume_adjust_mode,
|
||||
&volume_adjust_norm_steps,
|
||||
#endif
|
||||
#ifdef HAVE_QUICKSCREEN
|
||||
&shortcuts_replaces_quickscreen,
|
||||
#endif
|
||||
|
|
107
apps/misc.c
107
apps/misc.c
|
@ -824,6 +824,113 @@ void setvol(void)
|
|||
settings_save();
|
||||
}
|
||||
|
||||
#ifdef HAVE_PERCEPTUAL_VOLUME
|
||||
static short norm_tab[MAX_NORM_VOLUME_STEPS+2];
|
||||
static int norm_tab_num_steps;
|
||||
static int norm_tab_size;
|
||||
|
||||
static void update_norm_tab(void)
|
||||
{
|
||||
const int lim = global_settings.volume_adjust_norm_steps;
|
||||
if (lim == norm_tab_num_steps)
|
||||
return;
|
||||
norm_tab_num_steps = lim;
|
||||
|
||||
const int min = sound_min(SOUND_VOLUME);
|
||||
const int max = sound_max(SOUND_VOLUME);
|
||||
const int step = sound_steps(SOUND_VOLUME);
|
||||
|
||||
/* Ensure the table contains the minimum volume */
|
||||
norm_tab[0] = min;
|
||||
norm_tab_size = 1;
|
||||
|
||||
for (int i = 0; i < lim; ++i)
|
||||
{
|
||||
int vol = from_normalized_volume(i, min, max, lim);
|
||||
int rem = vol % step;
|
||||
|
||||
vol -= rem;
|
||||
if (abs(rem) > step/2)
|
||||
vol += rem < 0 ? -step : step;
|
||||
|
||||
/* Add volume step, ignoring any duplicate entries that may
|
||||
* occur due to rounding */
|
||||
if (vol != norm_tab[norm_tab_size-1])
|
||||
norm_tab[norm_tab_size++] = vol;
|
||||
}
|
||||
|
||||
/* Ensure the table contains the maximum volume */
|
||||
if (norm_tab[norm_tab_size-1] != max)
|
||||
norm_tab[norm_tab_size++] = max;
|
||||
}
|
||||
|
||||
void set_normalized_volume(int vol)
|
||||
{
|
||||
update_norm_tab();
|
||||
|
||||
if (vol < 0)
|
||||
vol = 0;
|
||||
if (vol >= norm_tab_size)
|
||||
vol = norm_tab_size - 1;
|
||||
|
||||
global_settings.volume = norm_tab[vol];
|
||||
}
|
||||
|
||||
int get_normalized_volume(void)
|
||||
{
|
||||
update_norm_tab();
|
||||
|
||||
int a = 0, b = norm_tab_size - 1;
|
||||
while (a != b)
|
||||
{
|
||||
int i = (a + b + 1) / 2;
|
||||
if (global_settings.volume < norm_tab[i])
|
||||
b = i - 1;
|
||||
else
|
||||
a = i;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
#else
|
||||
void set_normalized_volume(int vol)
|
||||
{
|
||||
global_settings.volume = vol * sound_steps(SOUND_VOLUME);
|
||||
}
|
||||
|
||||
int get_normalized_volume(void)
|
||||
{
|
||||
return global_settings.volume / sound_steps(SOUND_VOLUME);
|
||||
}
|
||||
#endif
|
||||
|
||||
void adjust_volume(int steps)
|
||||
{
|
||||
#ifdef HAVE_PERCEPTUAL_VOLUME
|
||||
adjust_volume_ex(steps, global_settings.volume_adjust_mode);
|
||||
#else
|
||||
adjust_volume_ex(steps, VOLUME_ADJUST_DIRECT);
|
||||
#endif
|
||||
}
|
||||
|
||||
void adjust_volume_ex(int steps, enum volume_adjust_mode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case VOLUME_ADJUST_PERCEPTUAL:
|
||||
#ifdef HAVE_PERCEPTUAL_VOLUME
|
||||
set_normalized_volume(get_normalized_volume() + steps);
|
||||
break;
|
||||
#endif
|
||||
case VOLUME_ADJUST_DIRECT:
|
||||
default:
|
||||
global_settings.volume += steps * sound_steps(SOUND_VOLUME);
|
||||
break;
|
||||
}
|
||||
|
||||
setvol();
|
||||
}
|
||||
|
||||
char* strrsplt(char* str, int c)
|
||||
{
|
||||
char* s = strrchr(str, c);
|
||||
|
|
14
apps/misc.h
14
apps/misc.h
|
@ -137,8 +137,22 @@ void check_bootfile(bool do_rolo);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
enum volume_adjust_mode
|
||||
{
|
||||
VOLUME_ADJUST_DIRECT, /* adjust in units of the volume step size */
|
||||
VOLUME_ADJUST_PERCEPTUAL, /* adjust using perceptual steps */
|
||||
};
|
||||
|
||||
/* min/max values for global_settings.volume_adjust_norm_steps */
|
||||
#define MIN_NORM_VOLUME_STEPS 10
|
||||
#define MAX_NORM_VOLUME_STEPS 100
|
||||
|
||||
/* check range, set volume and save settings */
|
||||
void setvol(void);
|
||||
void set_normalized_volume(int vol);
|
||||
int get_normalized_volume(void);
|
||||
void adjust_volume(int steps);
|
||||
void adjust_volume_ex(int steps, enum volume_adjust_mode mode);
|
||||
|
||||
#ifdef HAVE_LCD_COLOR
|
||||
int hex_to_rgb(const char* hex, int* color);
|
||||
|
|
|
@ -828,6 +828,7 @@ static const struct plugin_api rockbox_api = {
|
|||
#if defined(HAVE_TAGCACHE)
|
||||
tagtree_subentries_do_action,
|
||||
#endif
|
||||
adjust_volume,
|
||||
};
|
||||
|
||||
static int plugin_buffer_handle;
|
||||
|
|
|
@ -158,7 +158,7 @@ int plugin_open(const char *plugin, const char *parameter);
|
|||
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
||||
|
||||
/* increase this every time the api struct changes */
|
||||
#define PLUGIN_API_VERSION 264
|
||||
#define PLUGIN_API_VERSION 265
|
||||
|
||||
/* update this to latest version if a change to the api struct breaks
|
||||
backwards compatibility (and please take the opportunity to sort in any
|
||||
|
@ -954,6 +954,7 @@ struct plugin_api {
|
|||
#ifdef HAVE_TAGCACHE
|
||||
bool (*tagtree_subentries_do_action)(bool (*action_cb)(const char *file_name));
|
||||
#endif
|
||||
void (*adjust_volume)(int steps);
|
||||
};
|
||||
|
||||
/* plugin header */
|
||||
|
|
|
@ -2625,16 +2625,10 @@ static int handle_button(void)
|
|||
ff_rewind(0, false);
|
||||
break;
|
||||
case ACTION_WPS_VOLDOWN:
|
||||
limit = rb->sound_min(SOUND_VOLUME);
|
||||
if (--rb->global_settings->volume < limit)
|
||||
rb->global_settings->volume = limit;
|
||||
rb->sound_set(SOUND_VOLUME, rb->global_settings->volume);
|
||||
rb->adjust_volume(-1);
|
||||
break;
|
||||
case ACTION_WPS_VOLUP:
|
||||
limit = rb->sound_max(SOUND_VOLUME);
|
||||
if (++rb->global_settings->volume > limit)
|
||||
rb->global_settings->volume = limit;
|
||||
rb->sound_set(SOUND_VOLUME, rb->global_settings->volume);
|
||||
rb->adjust_volume(1);
|
||||
break;
|
||||
case ACTION_WPS_CONTEXT:
|
||||
ret = LRC_GOTO_EDITOR;
|
||||
|
|
|
@ -710,7 +710,6 @@ static void mm_errorhandler(void)
|
|||
|
||||
static int playfile(char* filename)
|
||||
{
|
||||
int vol = 0;
|
||||
int button;
|
||||
int retval = PLUGIN_OK;
|
||||
bool changingpos = false;
|
||||
|
@ -789,13 +788,8 @@ static int playfile(char* filename)
|
|||
}
|
||||
break;
|
||||
}
|
||||
vol = rb->global_settings->volume;
|
||||
if (vol < rb->sound_max(SOUND_VOLUME))
|
||||
{
|
||||
vol++;
|
||||
rb->sound_set(SOUND_VOLUME, vol);
|
||||
rb->global_settings->volume = vol;
|
||||
}
|
||||
|
||||
rb->adjust_volume(1);
|
||||
break;
|
||||
|
||||
case ACTION_WPS_VOLDOWN:
|
||||
|
@ -808,13 +802,8 @@ static int playfile(char* filename)
|
|||
}
|
||||
break;
|
||||
}
|
||||
vol = rb->global_settings->volume;
|
||||
if (vol > rb->sound_min(SOUND_VOLUME))
|
||||
{
|
||||
vol--;
|
||||
rb->sound_set(SOUND_VOLUME, vol);
|
||||
rb->global_settings->volume = vol;
|
||||
}
|
||||
|
||||
rb->adjust_volume(-1);
|
||||
break;
|
||||
|
||||
case ACTION_WPS_SKIPPREV:
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include "tables.h"
|
||||
|
||||
/* ROCKBOX HACK: avoid a conflict with adjust_volume() in misc.h */
|
||||
#define adjust_volume adjust_midi_volume
|
||||
|
||||
static int opt_expression_curve = 2;
|
||||
static int opt_volume_curve = 2;
|
||||
|
|
|
@ -855,6 +855,11 @@ struct user_settings
|
|||
#endif
|
||||
int volume_limit; /* maximum volume limit */
|
||||
|
||||
#ifdef HAVE_PERCEPTUAL_VOLUME
|
||||
int volume_adjust_mode;
|
||||
int volume_adjust_norm_steps;
|
||||
#endif
|
||||
|
||||
int surround_enabled;
|
||||
int surround_balance;
|
||||
int surround_fx1;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "powermgmt.h"
|
||||
#include "kernel.h"
|
||||
#include "open_plugin.h"
|
||||
#include "misc.h"
|
||||
#ifdef HAVE_REMOTE_LCD
|
||||
#include "lcd-remote.h"
|
||||
#endif
|
||||
|
@ -1057,6 +1058,16 @@ const struct settings_list settings[] = {
|
|||
MAX_FILES_IN_DIR_STEP /* min */, MAX_FILES_IN_DIR_MAX,
|
||||
MAX_FILES_IN_DIR_STEP,
|
||||
NULL, NULL, NULL),
|
||||
#ifdef HAVE_PERCEPTUAL_VOLUME
|
||||
CHOICE_SETTING(0, volume_adjust_mode, LANG_VOLUME_ADJUST_MODE,
|
||||
VOLUME_ADJUST_DIRECT, "volume adjustment mode",
|
||||
"direct,perceptual", NULL, 2,
|
||||
ID2P(LANG_DIRECT), ID2P(LANG_PERCEPTUAL)),
|
||||
INT_SETTING_NOWRAP(0, volume_adjust_norm_steps, LANG_VOLUME_ADJUST_NORM_STEPS,
|
||||
50, "perceptual volume step count", UNIT_INT,
|
||||
MIN_NORM_VOLUME_STEPS, MAX_NORM_VOLUME_STEPS, 5,
|
||||
NULL, NULL, NULL),
|
||||
#endif
|
||||
/* use this setting for user code even if there's no exchangable battery
|
||||
* support enabled */
|
||||
#if BATTERY_CAPACITY_INC > 0
|
||||
|
|
|
@ -1319,6 +1319,10 @@ Lyre prototype 1 */
|
|||
# define HAVE_SCREENDUMP
|
||||
#endif
|
||||
|
||||
#if !defined(BOOTLOADER) && MEMORYSIZE > 2
|
||||
# define HAVE_PERCEPTUAL_VOLUME
|
||||
#endif
|
||||
|
||||
/* null audiohw setting macro for when codec header is included for reasons
|
||||
other than audio support */
|
||||
#define AUDIOHW_SETTING(name, us, nd, st, minv, maxv, defv, expr...)
|
||||
|
|
|
@ -137,6 +137,23 @@ This sub menu relates to limits in the Rockbox operating system.
|
|||
\item LAN party computer $\rightarrow$ \dap $\rightarrow$ human
|
||||
\end{itemize}
|
||||
}
|
||||
\opt{perceptual_volume}{
|
||||
\subsection{Volume Adjustment Mode}
|
||||
This setting selects the method used to adjust volume with \ButtonVolUp{} and
|
||||
\ButtonVolDown{}. In \setting{Direct} mode each volume step changes the volume
|
||||
by a fixed number of decibels (dB).
|
||||
|
||||
In \setting{Perceptual} mode, the hardware volume range is divided into a
|
||||
number of steps, controlled by the \setting{Number of Volume Steps} option.
|
||||
Each step changes the volume by a variable number of decibels (dB) so the
|
||||
perceived loudness changes by about the same amount at each step. The dB
|
||||
change is smaller at high volumes and larger at low volumes, so a large
|
||||
range of low volumes are effectively compressed into a smaller number of
|
||||
volume steps.
|
||||
|
||||
\setting{Volume Adjustment Mode} does not affect how volume is displayed by
|
||||
themes.
|
||||
}
|
||||
\opt{quickscreen}{
|
||||
\subsection{Use Shortcuts Menu Instead of Quick Screen} This option
|
||||
activates the shortcuts menu instead of opening the quick screen when enabled.
|
||||
|
|
Loading…
Reference in a new issue