Phil Pertermann's dB peak meter patch
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2774 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
8f11dc00ac
commit
fd0cc3b2b1
10 changed files with 1035 additions and 99 deletions
|
@ -40,6 +40,7 @@
|
|||
#include "mpeg.h"
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
#include "widgets.h"
|
||||
#include "peakmeter.h"
|
||||
#endif
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
|
@ -1031,7 +1032,10 @@ bool debug_menu(void)
|
|||
{ "View partitions", dbg_partitions },
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
{ "View mpeg thread", dbg_mpeg_thread },
|
||||
#endif
|
||||
#ifdef PM_DEBUG
|
||||
{ "pm histogram", peak_meter_histogram},
|
||||
#endif /* PM_DEBUG */
|
||||
#endif /* HAVE_LCD_BITMAP */
|
||||
};
|
||||
|
||||
m=menu_init( items, sizeof items / sizeof(struct menu_items) );
|
||||
|
@ -1042,4 +1046,3 @@ bool debug_menu(void)
|
|||
}
|
||||
|
||||
#endif /* SIMULATOR */
|
||||
|
||||
|
|
|
@ -866,6 +866,46 @@ desc: in the peak meter menu
|
|||
eng: "Units Per Read"
|
||||
new:
|
||||
|
||||
id: LANG_PM_PERFORMANCE
|
||||
desc: in the peak meter menu
|
||||
eng: "Performance"
|
||||
new:
|
||||
|
||||
id: LANG_PM_HIGH_PERFORMANCE
|
||||
desc: in the peak meter menu
|
||||
eng: "High performance"
|
||||
new:
|
||||
|
||||
id: LANG_PM_ENERGY_SAVER
|
||||
desc: in the peak meter menu
|
||||
eng: "Save energy"
|
||||
new:
|
||||
|
||||
id: LANG_PM_SCALE
|
||||
desc: in the peak meter menu
|
||||
eng: "dBfs <-> linear"
|
||||
new:
|
||||
|
||||
id: LANG_PM_DBFS
|
||||
desc: in the peak meter menu
|
||||
eng: "dBfs"
|
||||
new:
|
||||
|
||||
id: LANG_PM_LINEAR
|
||||
desc: in the peak meter menu
|
||||
eng: "linear"
|
||||
new:
|
||||
|
||||
id: LANG_PM_MIN
|
||||
desc: in the peak meter menu
|
||||
eng: "Minimum of range"
|
||||
new:
|
||||
|
||||
id: LANG_PM_MAX
|
||||
desc: in the peak meter menu
|
||||
eng: "Maximum of range"
|
||||
new:
|
||||
|
||||
id: LANG_BACKLIGHT_ON_WHEN_CHARGING
|
||||
desc: in display_settings_menu
|
||||
eng: "Backlight On When Plugged"
|
||||
|
|
|
@ -46,24 +46,52 @@ static bool peak_meter_r_clip = false;
|
|||
static long peak_meter_clip_timeout_l;
|
||||
static long peak_meter_clip_timeout_r;
|
||||
|
||||
static int peak_meter_clip_hold;
|
||||
|
||||
/* specifies the value range in peak volume values */
|
||||
unsigned short peak_meter_range_min;
|
||||
unsigned short peak_meter_range_max;
|
||||
|
||||
/* if set to true clip timeout is disabled */
|
||||
static bool peak_meter_clip_eternal = false;
|
||||
|
||||
static bool peak_meter_use_dbfs = true;
|
||||
|
||||
|
||||
#ifndef SIMULATOR
|
||||
static int peak_meter_src_l = MAS_REG_DQPEAK_L;
|
||||
static int peak_meter_src_r = MAS_REG_DQPEAK_R;
|
||||
#endif
|
||||
|
||||
/* temporarily en- / disables peak meter. This is
|
||||
|
||||
especially for external applications to detect
|
||||
|
||||
if the peak_meter is in use and needs drawing at all */
|
||||
bool peak_meter_enabled = true;
|
||||
|
||||
/*
|
||||
bool peak_meter_use_thread = false;
|
||||
static char peak_meter_stack[DEFAULT_STACK_SIZE];
|
||||
*/
|
||||
/* used in wps.c to set the display frame rate of the peak meter */
|
||||
int peak_meter_fps = 20;
|
||||
|
||||
static int peak_meter_l;
|
||||
static int peak_meter_r;
|
||||
|
||||
static int peak_meter_hold = 1;
|
||||
static int peak_meter_release = 8;
|
||||
|
||||
/* debug only */
|
||||
#ifdef PM_DEBUG
|
||||
static int peek_calls = 0;
|
||||
|
||||
#define PEEKS_PER_DRAW_SIZE 40
|
||||
static unsigned int peeks_per_redraw[PEEKS_PER_DRAW_SIZE];
|
||||
|
||||
#define TICKS_PER_DRAW_SIZE 20
|
||||
static unsigned int ticks_per_redraw[TICKS_PER_DRAW_SIZE];
|
||||
#endif
|
||||
|
||||
/* time out values for max */
|
||||
static long max_time_out[] = {
|
||||
0 * HZ, HZ / 5, 30, HZ / 2, HZ, 2 * HZ,
|
||||
|
@ -80,6 +108,353 @@ static long clip_time_out[] = {
|
|||
2700 * HZ, 5400 * HZ
|
||||
};
|
||||
|
||||
/* precalculated peak values that represent magical
|
||||
dBfs values. Used to draw the scale */
|
||||
#if 0
|
||||
static int db_scale_src_values[] = {
|
||||
32767, /* 0 db */
|
||||
23197, /* - 3 db */
|
||||
16422, /* - 6 db */
|
||||
11626, /* - 9 db */
|
||||
8231, /* -12 db */
|
||||
4125, /* -18 db */
|
||||
2067, /* -24 db */
|
||||
1036, /* -30 db */
|
||||
328, /* -40 db */
|
||||
104, /* -50 db */
|
||||
33, /* -60 db */
|
||||
};
|
||||
#else
|
||||
static int db_scale_src_values[] = {
|
||||
32752, /* 0 db */
|
||||
22784, /* - 3 db */
|
||||
14256, /* - 6 db */
|
||||
11752, /* - 9 db */
|
||||
9256, /* -12 db */
|
||||
4256, /* -18 db */
|
||||
2186, /* -24 db */
|
||||
1186, /* -30 db */
|
||||
373, /* -40 db */
|
||||
102, /* -50 db */
|
||||
33, /* -60 db */
|
||||
};
|
||||
#endif
|
||||
|
||||
int db_scale_count = sizeof db_scale_src_values / sizeof (int);
|
||||
|
||||
/* if db_scale_valid is false the content of
|
||||
db_scale_lcd_coord needs recalculation */
|
||||
static bool db_scale_valid = false;
|
||||
|
||||
/* contains the lcd x coordinates of the magical
|
||||
scale values in db_scale_src_values */
|
||||
static int db_scale_lcd_coord[sizeof db_scale_src_values / sizeof (int)];
|
||||
|
||||
|
||||
/**
|
||||
* Calculates dB Value for the peak meter, uses peak value as input
|
||||
* @param int sample - The input value
|
||||
* Make sure that 0 <= value < SAMPLE_RANGE
|
||||
*
|
||||
* @return int - The 2 digit fixed comma result of the euation
|
||||
* 20 * log (sample / SAMPLE_RANGE) + 90
|
||||
* Output range is 0-8961 (that is 0,0 - 89,6 dB).
|
||||
* Normally 0dB is full scale, here it is shifted +90dB.
|
||||
* The calculation is based on the results of a linear
|
||||
* approximation tool written specifically for this problem
|
||||
* by Andreas Zwirtes (radhard@gmx.de). The result hat an
|
||||
* accurracy of better than 2%. It is highly runtime optimized,
|
||||
* the cascading if-clauses do an successive approximation on
|
||||
* the input value. This avoids big lookup-tables and
|
||||
* for-loops.
|
||||
*/
|
||||
|
||||
int calc_db (int isample) {
|
||||
/* return n+m*(isample-istart)/100 */
|
||||
int n;
|
||||
long m;
|
||||
int istart;
|
||||
|
||||
/* Range 1-4 */
|
||||
if (isample < 119) {
|
||||
|
||||
/* Range 1-2 */
|
||||
if (isample < 5) {
|
||||
|
||||
/* Range 1 */
|
||||
if (isample < 1) {
|
||||
istart = 0;
|
||||
n = 0;
|
||||
m = 5900;
|
||||
}
|
||||
|
||||
/* Range 2 */
|
||||
else {
|
||||
istart = 1;
|
||||
n = 59;
|
||||
m = 34950;
|
||||
}
|
||||
}
|
||||
|
||||
/* Range 3-4 */
|
||||
else {
|
||||
|
||||
/* Range 3 */
|
||||
if (isample < 24) {
|
||||
istart = 5;
|
||||
n = 1457;
|
||||
m = 7168;
|
||||
}
|
||||
|
||||
/* Range 4 */
|
||||
else {
|
||||
istart = 24;
|
||||
n = 2819;
|
||||
m = 1464;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Range 5-8 */
|
||||
else {
|
||||
|
||||
/* Range 5-6 */
|
||||
if (isample < 2918) {
|
||||
|
||||
/* Range 5 */
|
||||
if (isample < 592) {
|
||||
istart = 119;
|
||||
n = 4210;
|
||||
m = 295;
|
||||
}
|
||||
|
||||
/* Range 6 */
|
||||
else {
|
||||
istart = 592;
|
||||
n = 5605;
|
||||
m = 60;
|
||||
}
|
||||
}
|
||||
|
||||
/* Range 7-8 */
|
||||
else {
|
||||
|
||||
/* Range 7 */
|
||||
if (isample < 15352) {
|
||||
istart = 2918;
|
||||
n = 7001;
|
||||
m = 12;
|
||||
}
|
||||
|
||||
/* Range 8 */
|
||||
else {
|
||||
istart = 15352;
|
||||
n = 8439;
|
||||
m = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n + (m * (long)(isample - istart)) / 100L;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A helper function for db_to_sample. Don't call it separately but
|
||||
* use db_to_sample. If one or both of min and max are outside the
|
||||
* range 0 <= min (or max) < 8961 the behaviour of this function is
|
||||
* undefined. It may not return.
|
||||
* @param int min - The minimum of the value range that is searched.
|
||||
* @param int max - The maximum of the value range that is searched.
|
||||
* @param int db - The value in dBfs * (-100) for which the according
|
||||
* minimal peak sample is searched.
|
||||
* @return int - A linear volume value with 0 <= value < MAX_PEAK
|
||||
*/
|
||||
static int db_to_sample_bin_search(int min, int max, int db){
|
||||
int test = min + (max - min) / 2;
|
||||
|
||||
if (min < max) {
|
||||
if (calc_db(test) < db) {
|
||||
test = db_to_sample_bin_search(test, max, db);
|
||||
} else {
|
||||
if (calc_db(test-1) > db) {
|
||||
test = db_to_sample_bin_search(min, test, db);
|
||||
}
|
||||
}
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a value representing dBfs to a linear
|
||||
* scaled volume info as it is used by the MAS.
|
||||
* An incredibly inefficiant function which is
|
||||
* the vague inverse of calc_db. This really
|
||||
* should be replaced by something better soon.
|
||||
*
|
||||
* @param int db - A dBfs * 100 value with
|
||||
* -9000 < value <= 0
|
||||
* @return int - The return value is in the range of
|
||||
* 0 <= return value < MAX_PEAK
|
||||
*/
|
||||
static int db_to_sample(int db) {
|
||||
int retval = 0;
|
||||
|
||||
/* what is the maximum pseudo db value */
|
||||
int max_peak_db = calc_db(MAX_PEAK - 1);
|
||||
|
||||
/* range check: db value to big */
|
||||
if (max_peak_db + db < 0) {
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
/* range check: db value too small */
|
||||
else if (max_peak_db + db >= max_peak_db) {
|
||||
retval = MAX_PEAK -1;
|
||||
}
|
||||
|
||||
/* value in range: find the matching linear value */
|
||||
else {
|
||||
retval = db_to_sample_bin_search(0, MAX_PEAK, max_peak_db + db);
|
||||
|
||||
/* as this is a dirty function anyway, we want to adjust the
|
||||
full scale hit manually to avoid users complaining that when
|
||||
they adjust maximum for 0 dBfs and display it in percent it
|
||||
shows 99%. That is due to precision loss and this is the
|
||||
optical fix */
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the min value for restriction of the value range.
|
||||
* @param int newmin - depending wether dBfs is used
|
||||
* newmin is a value in dBfs * 100 or in linear percent values.
|
||||
* for dBfs: -9000 < newmin <= 0
|
||||
* for linear: 0 <= newmin <= 100
|
||||
*/
|
||||
void peak_meter_set_min(int newmin) {
|
||||
if (peak_meter_use_dbfs) {
|
||||
peak_meter_range_min = db_to_sample(newmin);
|
||||
|
||||
} else {
|
||||
if (newmin < peak_meter_range_max) {
|
||||
peak_meter_range_min = newmin * MAX_PEAK / 100;
|
||||
}
|
||||
}
|
||||
db_scale_valid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum value of the range the meter
|
||||
* displays. If the scale is set to dBfs it returns
|
||||
* dBfs values * 100 or linear percent values.
|
||||
* @return: using dBfs : -9000 < value <= 0
|
||||
* using linear scale: 0 <= value <= 100
|
||||
*/
|
||||
int peak_meter_get_min(void) {
|
||||
int retval = 0;
|
||||
if (peak_meter_use_dbfs) {
|
||||
retval = calc_db(peak_meter_range_min) - calc_db(MAX_PEAK - 1);
|
||||
} else {
|
||||
retval = peak_meter_range_min * 100 / MAX_PEAK;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max value for restriction of the value range.
|
||||
* @param int newmax - depending wether dBfs is used
|
||||
* newmax is a value in dBfs * 100 or in linear percent values.
|
||||
* for dBfs: -9000 < newmax <= 0
|
||||
* for linear: 0 <= newmax <= 100
|
||||
*/
|
||||
void peak_meter_set_max(int newmax) {
|
||||
if (peak_meter_use_dbfs) {
|
||||
peak_meter_range_max = db_to_sample(newmax);
|
||||
} else {
|
||||
if (newmax > peak_meter_range_min) {
|
||||
peak_meter_range_max = newmax * MAX_PEAK / 100;
|
||||
}
|
||||
}
|
||||
db_scale_valid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum value of the range the meter
|
||||
* displays. If the scale is set to dBfs it returns
|
||||
* dBfs values * 100 or linear percent values
|
||||
* @return: using dBfs : -9000 < value <= 0
|
||||
* using linear scale: 0 <= value <= 100
|
||||
*/
|
||||
int peak_meter_get_max(void) {
|
||||
int retval = 0;
|
||||
if (peak_meter_use_dbfs) {
|
||||
retval = calc_db(peak_meter_range_max) - calc_db(MAX_PEAK - 1);
|
||||
} else {
|
||||
retval = peak_meter_range_max * 100 / MAX_PEAK;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if the meter currently is
|
||||
* displaying dBfs values, 0 if the meter
|
||||
* displays percent values.
|
||||
* @return int - returns 0 or 1.
|
||||
*/
|
||||
int peak_meter_get_use_dbfs(void) {
|
||||
return peak_meter_use_dbfs ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies wether the values displayed are scaled
|
||||
* as dBfs or as linear percent values.
|
||||
* @param int - Set to 0 for linear percent scale. Any other value
|
||||
* switches on dBfs.
|
||||
*/
|
||||
void peak_meter_set_use_dbfs(int use){
|
||||
peak_meter_use_dbfs = ((use & 1) == 1);
|
||||
db_scale_valid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the range of the meter. Only values
|
||||
* that are in the range of [range_min ... range_max]
|
||||
* are displayed.
|
||||
* @param bool dbfs - set to true for dBfs,
|
||||
* set to false for linear scaling in percent
|
||||
* @param int range_min - Specifies the lower value of the range.
|
||||
* Pass a value dBfs * 100 when dbfs is set to true.
|
||||
* Pass a percent value when dbfs is set to false.
|
||||
* @param int range_max - Specifies the upper value of the range.
|
||||
* Pass a value dBfs * 100 when dbfs is set to true.
|
||||
* Pass a percent value when dbfs is set to false.
|
||||
*/
|
||||
void peak_meter_init_range( bool dbfs, int range_min, int range_max) {
|
||||
peak_meter_use_dbfs = dbfs;
|
||||
peak_meter_set_min(range_min);
|
||||
peak_meter_set_max(range_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the peak meter with all relevant values concerning times.
|
||||
* @param int release - Set the maximum amount of pixels the meter is allowed
|
||||
* to decrease with each redraw
|
||||
* @param int hold - Select the time preset for the time the peak indicator
|
||||
* is reset after a peak occurred. The preset values are
|
||||
* stored in max_time_out.
|
||||
* @param int clip_hold - Select the time preset for the time the peak
|
||||
* indicator is reset after a peak occurred. The preset
|
||||
* values are stored in clip_time_out.
|
||||
*/
|
||||
void peak_meter_init_times(int release, int hold, int clip_hold) {
|
||||
peak_meter_hold = hold;
|
||||
peak_meter_release = release;
|
||||
peak_meter_clip_hold = clip_hold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source of the peak meter to playback or to
|
||||
* record.
|
||||
|
@ -127,21 +502,59 @@ void peak_meter_peek(void) {
|
|||
(left == MAX_PEAK - 1)) {
|
||||
peak_meter_l_clip = true;
|
||||
peak_meter_clip_timeout_l =
|
||||
current_tick + clip_time_out[global_settings.peak_meter_clip_hold];
|
||||
current_tick + clip_time_out[peak_meter_clip_hold];
|
||||
}
|
||||
|
||||
if ((right == peak_meter_r) &&
|
||||
(right == MAX_PEAK - 1)) {
|
||||
peak_meter_r_clip = true;
|
||||
peak_meter_clip_timeout_r =
|
||||
current_tick + clip_time_out[global_settings.peak_meter_clip_hold];
|
||||
current_tick + clip_time_out[peak_meter_clip_hold];
|
||||
}
|
||||
|
||||
/* peaks are searched -> we have to find the maximum */
|
||||
/* peaks are searched -> we have to find the maximum. When
|
||||
many calls of peak_meter_peek the maximum value will be
|
||||
stored in peak_meter_x. This maximum is reset by the
|
||||
functions peak_meter_read_x. */
|
||||
peak_meter_l = MAX(peak_meter_l, left);
|
||||
peak_meter_r = MAX(peak_meter_r, right);
|
||||
|
||||
#ifdef PM_DEBUG
|
||||
peek_calls++;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The thread function for the peak meter calls peak_meter_peek
|
||||
* to reas out the mas and find maxima, clips, etc. No display
|
||||
* is performed.
|
||||
*/
|
||||
/*
|
||||
void peak_meter_thread(void) {
|
||||
sleep(5000);
|
||||
while (1) {
|
||||
if (peak_meter_enabled && peak_meter_use_thread){
|
||||
peak_meter_peek();
|
||||
}
|
||||
yield();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates the peak meter thread
|
||||
*/
|
||||
/*
|
||||
void peak_meter_init(void) {
|
||||
create_thread(
|
||||
peak_meter_thread,
|
||||
peak_meter_stack,
|
||||
sizeof peak_meter_stack,
|
||||
"peakmeter");
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Reads out the peak volume of the left channel.
|
||||
* @return int - The maximum value that has been detected
|
||||
|
@ -149,10 +562,20 @@ void peak_meter_peek(void) {
|
|||
* is in the range 0 <= value < MAX_PEAK.
|
||||
*/
|
||||
static int peak_meter_read_l (void) {
|
||||
/* peak_meter_l contains the maximum of
|
||||
all peak values that were read by peak_meter_peek
|
||||
since the last call of peak_meter_read_r */
|
||||
int retval = peak_meter_l;
|
||||
#ifdef PM_DEBUG
|
||||
peek_calls = 0;
|
||||
#endif
|
||||
|
||||
#ifdef SIMULATOR
|
||||
peak_meter_l = 8000;
|
||||
#else
|
||||
/* reset peak_meter_l so that subsequent calls of
|
||||
peak_meter_peek doesn't get fooled by an old
|
||||
maximum value */
|
||||
peak_meter_l = mas_codec_readreg(peak_meter_src_l);
|
||||
#endif
|
||||
return retval;
|
||||
|
@ -165,10 +588,20 @@ static int peak_meter_read_l (void) {
|
|||
* is in the range 0 <= value < MAX_PEAK.
|
||||
*/
|
||||
static int peak_meter_read_r (void) {
|
||||
/* peak_meter_r contains the maximum of
|
||||
all peak values that were read by peak_meter_peek
|
||||
since the last call of peak_meter_read_r */
|
||||
int retval = peak_meter_r;
|
||||
#ifdef PM_DEBUG
|
||||
peek_calls = 0;
|
||||
#endif
|
||||
|
||||
#ifdef SIMULATOR
|
||||
peak_meter_l = 8000;
|
||||
#else
|
||||
/* reset peak_meter_r so that subsequent calls of
|
||||
peak_meter_peek doesn't get fooled by an old
|
||||
maximum value */
|
||||
peak_meter_r = mas_codec_readreg(peak_meter_src_r);
|
||||
#endif
|
||||
return retval;
|
||||
|
@ -190,6 +623,51 @@ void peak_meter_set_clip_hold(int time) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales a peak value as read from the MAS to the range of meterwidth.
|
||||
* The scaling is performed according to the scaling method (dBfs / linear)
|
||||
* and the range (peak_meter_range_min .. peak_meter_range_max).
|
||||
* @param unsigned short val - The volume value. Range: 0 <= val < MAX_PEAK
|
||||
* @param int meterwidht - The widht of the meter in pixel
|
||||
* @return unsigned short - A value 0 <= return value <= meterwidth
|
||||
*/
|
||||
unsigned short peak_meter_scale_value(unsigned short val, int meterwidth){
|
||||
int range;
|
||||
int retval;
|
||||
|
||||
if (val <= peak_meter_range_min) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (val >= peak_meter_range_max) {
|
||||
return meterwidth;
|
||||
}
|
||||
|
||||
retval = val;
|
||||
|
||||
/* different scaling is used for dBfs and linear percent */
|
||||
if (peak_meter_use_dbfs) {
|
||||
|
||||
/* needed the offset in 'zoomed' meters */
|
||||
int dbmin = calc_db(peak_meter_range_min);
|
||||
|
||||
range = calc_db(peak_meter_range_max) - dbmin;
|
||||
|
||||
/* scale the samples dBfs */
|
||||
retval = (calc_db(retval) - dbmin) * meterwidth / range;
|
||||
}
|
||||
|
||||
/* Scale for linear percent display */
|
||||
else
|
||||
{
|
||||
range =(peak_meter_range_max - peak_meter_range_min);
|
||||
|
||||
/* scale the samples */
|
||||
retval = ((retval - peak_meter_range_min) * meterwidth) / range;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draws a peak meter in the specified size at the specified position.
|
||||
|
@ -208,22 +686,66 @@ void peak_meter_draw(int x, int y, int width, int height) {
|
|||
int meterwidth = width - 3;
|
||||
int i;
|
||||
|
||||
#ifdef PM_DEBUG
|
||||
static long pm_tick = 0;
|
||||
int tmp = peek_calls;
|
||||
#endif
|
||||
|
||||
/* if disabled only draw the peak meter */
|
||||
if (peak_meter_enabled) {
|
||||
|
||||
/* read the volume info from MAS */
|
||||
left = peak_meter_read_l();
|
||||
right = peak_meter_read_r();
|
||||
|
||||
peak_meter_peek();
|
||||
|
||||
/* restrict the range to avoid drawing outside the lcd */
|
||||
left = MAX(peak_meter_range_min, left);
|
||||
left = MIN(peak_meter_range_max, left);
|
||||
|
||||
/* scale the samples */
|
||||
left /= (MAX_PEAK / meterwidth);
|
||||
right /= (MAX_PEAK / meterwidth);
|
||||
right = MAX(peak_meter_range_min, right);
|
||||
right = MIN(peak_meter_range_max, right);
|
||||
|
||||
/* scale the samples dBfs */
|
||||
left = peak_meter_scale_value(left, meterwidth);
|
||||
right = peak_meter_scale_value(right, meterwidth);
|
||||
|
||||
/* if the scale has changed -> recalculate the scale
|
||||
(The scale becomes invalid when the range changed.) */
|
||||
if (!db_scale_valid){
|
||||
|
||||
if (peak_meter_use_dbfs) {
|
||||
db_scale_count = sizeof db_scale_src_values / sizeof (int);
|
||||
for (i = 0; i < db_scale_count; i++){
|
||||
/* find the real x-coords for predefined interesting
|
||||
dBfs values. These only are recalculated when the
|
||||
scaling of the meter changed. */
|
||||
db_scale_lcd_coord[i] =
|
||||
peak_meter_scale_value(
|
||||
db_scale_src_values[i],
|
||||
meterwidth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* when scaling linear we simly make 10% steps */
|
||||
else {
|
||||
int range = peak_meter_range_max - peak_meter_range_min;
|
||||
db_scale_count = 10;
|
||||
for (i = 0; i < db_scale_count; i++) {
|
||||
db_scale_lcd_coord[i] =
|
||||
(i * (MAX_PEAK / 10) - peak_meter_range_min) *
|
||||
meterwidth / range;
|
||||
}
|
||||
}
|
||||
|
||||
/* mark scale valid to avoid recalculating dBfs values
|
||||
of the scale. */
|
||||
db_scale_valid = true;
|
||||
}
|
||||
|
||||
/* apply release */
|
||||
left = MAX(left , last_left - global_settings.peak_meter_release);
|
||||
right = MAX(right, last_right - global_settings.peak_meter_release);
|
||||
left = MAX(left , last_left - peak_meter_release);
|
||||
right = MAX(right, last_right - peak_meter_release);
|
||||
|
||||
/* reset max values after timeout */
|
||||
if (TIME_AFTER(current_tick, peak_meter_timeout_l)){
|
||||
|
@ -250,13 +772,13 @@ void peak_meter_draw(int x, int y, int width, int height) {
|
|||
if (left > peak_meter_max_l) {
|
||||
peak_meter_max_l = left - 1;
|
||||
peak_meter_timeout_l =
|
||||
current_tick + max_time_out[global_settings.peak_meter_hold];
|
||||
current_tick + max_time_out[peak_meter_hold];
|
||||
}
|
||||
|
||||
if (right > peak_meter_max_r) {
|
||||
peak_meter_max_r = right - 1;
|
||||
peak_meter_timeout_r =
|
||||
current_tick + max_time_out[global_settings.peak_meter_hold];
|
||||
current_tick + max_time_out[peak_meter_hold];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,13 +805,90 @@ void peak_meter_draw(int x, int y, int width, int height) {
|
|||
lcd_fillrect(x + meterwidth, y + height / 2, 3, height / 2 - 1);
|
||||
}
|
||||
|
||||
/* draw scale */
|
||||
/* draw scale end */
|
||||
lcd_drawline(x + meterwidth, y,
|
||||
x + meterwidth, y + height - 2);
|
||||
for (i = 0; i < 10; i++) {
|
||||
lcd_invertpixel(x + meterwidth * i / 10, y + height / 2 - 1);
|
||||
|
||||
/* draw dots for scale marks */
|
||||
for (i = 0; i < db_scale_count; i++) {
|
||||
/* The x-coordinates of interesting scale mark points
|
||||
have been calculated before */
|
||||
lcd_invertpixel(db_scale_lcd_coord[i], y + height / 2 - 1);
|
||||
}
|
||||
|
||||
#ifdef PM_DEBUG
|
||||
/* display a bar to show how many calls to peak_meter_peek
|
||||
have ocurred since the last display */
|
||||
lcd_invertrect(x, y, tmp, 3);
|
||||
|
||||
if (tmp < PEEKS_PER_DRAW_SIZE) {
|
||||
peeks_per_redraw[tmp]++;
|
||||
}
|
||||
|
||||
tmp = current_tick - pm_tick;
|
||||
if (tmp < TICKS_PER_DRAW_SIZE ){
|
||||
ticks_per_redraw[tmp] ++;
|
||||
}
|
||||
|
||||
/* display a bar to show how many ticks have passed since
|
||||
the last redraw */
|
||||
lcd_invertrect(x, y + height / 2, current_tick - pm_tick, 2);
|
||||
pm_tick = current_tick;
|
||||
#endif
|
||||
|
||||
last_left = left;
|
||||
last_right = right;
|
||||
}
|
||||
|
||||
#ifdef PM_DEBUG
|
||||
static void peak_meter_clear_histogram(void) {
|
||||
int i = 0;
|
||||
for (i = 0; i < TICKS_PER_DRAW_SIZE; i++) {
|
||||
ticks_per_redraw[i] = (unsigned int)0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PEEKS_PER_DRAW_SIZE; i++) {
|
||||
peeks_per_redraw[i] = (unsigned int)0;
|
||||
}
|
||||
}
|
||||
|
||||
bool peak_meter_histogram(void) {
|
||||
int i;
|
||||
int btn = BUTTON_NONE;
|
||||
while ((btn & BUTTON_OFF) != BUTTON_OFF )
|
||||
{
|
||||
unsigned int max = 0;
|
||||
int y = 0;
|
||||
int x = 0;
|
||||
lcd_clear_display();
|
||||
|
||||
for (i = 0; i < PEEKS_PER_DRAW_SIZE; i++) {
|
||||
max = MAX(max, peeks_per_redraw[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < PEEKS_PER_DRAW_SIZE; i++) {
|
||||
x = peeks_per_redraw[i] * (LCD_WIDTH - 1)/ max;
|
||||
lcd_drawline(0, y + i, x, y + i);
|
||||
}
|
||||
|
||||
y = PEEKS_PER_DRAW_SIZE + 1;
|
||||
max = 0;
|
||||
|
||||
for (i = 0; i < TICKS_PER_DRAW_SIZE; i++) {
|
||||
max = MAX(max, ticks_per_redraw[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < TICKS_PER_DRAW_SIZE; i++) {
|
||||
x = ticks_per_redraw[i] * (LCD_WIDTH - 1)/ max;
|
||||
lcd_drawline(0, y + i, x, y + i);
|
||||
}
|
||||
lcd_update();
|
||||
|
||||
btn = button_get(true);
|
||||
if (btn == BUTTON_PLAY) {
|
||||
peak_meter_clear_histogram();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -19,12 +19,32 @@
|
|||
#ifndef __PEAKMETER_H__
|
||||
#define __PEAKMETER_H__
|
||||
|
||||
extern bool peak_meter_enabled;
|
||||
/*#define PM_DEBUG */
|
||||
#ifdef PM_DEBUG
|
||||
extern bool peak_meter_histogramm(void);
|
||||
#endif
|
||||
|
||||
|
||||
extern bool peak_meter_enabled;
|
||||
extern int peak_meter_fps;
|
||||
|
||||
extern void peak_meter_init(void);
|
||||
extern void peak_meter_playback(bool playback);
|
||||
extern void peak_meter_draw(int x, int y, int width, int height);
|
||||
extern void peak_meter_set_clip_hold(int time);
|
||||
extern void peak_meter_peek(void);
|
||||
extern void peak_meter_init_range( bool dbfs, int range_min, int range_max);
|
||||
extern void peak_meter_init_times(int release, int hold, int clip_hold);
|
||||
|
||||
extern void peak_meter_set_min(int newmin);
|
||||
extern int peak_meter_get_min(void);
|
||||
extern void peak_meter_set_max(int newmax);
|
||||
extern int peak_meter_get_max(void);
|
||||
extern void peak_meter_set_use_dbfs(int use);
|
||||
extern int peak_meter_get_use_dbfs(void);
|
||||
extern int calc_db (int isample);
|
||||
extern unsigned short peak_meter_scale_value(unsigned short val, int meterwidth);
|
||||
|
||||
extern unsigned short peak_meter_range_min;
|
||||
extern unsigned short peak_meter_range_max;
|
||||
|
||||
#endif /* __PEAKMETER_H__ */
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#ifdef HAVE_LCD_BITMAP
|
||||
#include "icons.h"
|
||||
#include "font.h"
|
||||
#include "peakmeter.h"
|
||||
#endif
|
||||
#include "lang.h"
|
||||
#include "language.h"
|
||||
|
@ -52,7 +53,7 @@
|
|||
struct user_settings global_settings;
|
||||
char rockboxdir[] = ROCKBOX_DIR; /* config/font/data file directory */
|
||||
|
||||
#define CONFIG_BLOCK_VERSION 2
|
||||
#define CONFIG_BLOCK_VERSION 3
|
||||
#define CONFIG_BLOCK_SIZE 512
|
||||
#define RTC_BLOCK_SIZE 44
|
||||
|
||||
|
@ -89,10 +90,14 @@ offset abs
|
|||
0x16 0x2a <(int) Byte offset into resume file>
|
||||
0x1a 0x2e <time until disk spindown>
|
||||
0x1b 0x2f <browse current, play selected>
|
||||
0x1c 0x30 <peak meter hold timeout (bit 0-4)>
|
||||
0x1d 0x31 <peak meter clip hold timeout (bit 0-4)>
|
||||
0x1e 0x32 <peak meter release step size>
|
||||
0x1f 0x33 <repeat mode>
|
||||
0x1c 0x30 <peak meter hold timeout (bit 0-4)>,
|
||||
peak_meter_performance (bit 7)
|
||||
0x1d 0x31 <peak meter clip hold timeout (bit 0-4)
|
||||
0x1e 0x32 <peak meter release step size,
|
||||
peak_meter_dbfs (bit 7)
|
||||
0x1f 0x33 <peak meter min either in -db or in percent>
|
||||
0x20 0x34 <peak meter max either in -db or in percent>
|
||||
0x21 0x35 <repeat mode>
|
||||
|
||||
<all unused space filled with 0xff>
|
||||
|
||||
|
@ -316,9 +321,13 @@ int settings_save( void )
|
|||
((global_settings.play_selected & 1) << 1));
|
||||
|
||||
config_block[0x1c] = (unsigned char)global_settings.peak_meter_hold;
|
||||
config_block[0x1d] = (unsigned char)global_settings.peak_meter_clip_hold;
|
||||
config_block[0x1e] = (unsigned char)global_settings.peak_meter_release;
|
||||
config_block[0x1f] = (unsigned char)global_settings.repeat_mode;
|
||||
config_block[0x1d] = (unsigned char)global_settings.peak_meter_clip_hold |
|
||||
(global_settings.peak_meter_performance ? 0x80 : 0);
|
||||
config_block[0x1e] = global_settings.peak_meter_release |
|
||||
(global_settings.peak_meter_dbfs ? 0x80 : 0);
|
||||
config_block[0x1f] = (unsigned char)global_settings.peak_meter_min;
|
||||
config_block[0x20] = (unsigned char)global_settings.peak_meter_max;
|
||||
config_block[0x21] = (unsigned char)global_settings.repeat_mode;
|
||||
|
||||
memcpy(&config_block[0x24], &global_settings.total_uptime, 4);
|
||||
|
||||
|
@ -355,6 +364,35 @@ int settings_save( void )
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
/**
|
||||
* Applies the range infos stored in global_settings to
|
||||
* the peak meter.
|
||||
*/
|
||||
void settings_apply_pm_range(void)
|
||||
{
|
||||
int pm_min, pm_max;
|
||||
|
||||
/* depending on the scale mode (dBfs or percent) the values
|
||||
of global_settings.peak_meter_dbfs have different meanings */
|
||||
if (global_settings.peak_meter_dbfs)
|
||||
{
|
||||
/* convert to dBfs * 100 */
|
||||
pm_min = -(((int)global_settings.peak_meter_min) * 100);
|
||||
pm_max = -(((int)global_settings.peak_meter_max) * 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* percent is stored directly -> no conversion */
|
||||
pm_min = global_settings.peak_meter_min;
|
||||
pm_max = global_settings.peak_meter_max;
|
||||
}
|
||||
|
||||
/* apply the range */
|
||||
peak_meter_init_range(global_settings.peak_meter_dbfs, pm_min, pm_max);
|
||||
}
|
||||
#endif /* HAVE_LCD_BITMAP */
|
||||
|
||||
void settings_apply(void)
|
||||
{
|
||||
char buf[64];
|
||||
|
@ -381,6 +419,13 @@ void settings_apply(void)
|
|||
CHARGE_RESTART_LO : CHARGE_RESTART_HI;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
settings_apply_pm_range();
|
||||
peak_meter_init_times(
|
||||
global_settings.peak_meter_release, global_settings.peak_meter_hold,
|
||||
global_settings.peak_meter_clip_hold);
|
||||
#endif
|
||||
|
||||
if ( global_settings.wps_file[0] &&
|
||||
global_settings.wps_file[0] != 0xff ) {
|
||||
snprintf(buf, sizeof buf, ROCKBOX_DIR "/%s.wps",
|
||||
|
@ -500,14 +545,25 @@ void settings_load(void)
|
|||
if (config_block[0x1c] != 0xFF)
|
||||
global_settings.peak_meter_hold = (config_block[0x1c]) & 0x1f;
|
||||
|
||||
if (config_block[0x1d] != 0xFF)
|
||||
if (config_block[0x1d] != 0xFF) {
|
||||
global_settings.peak_meter_clip_hold = (config_block[0x1d]) & 0x1f;
|
||||
global_settings.peak_meter_performance =
|
||||
(config_block[0x1d] & 0x80) != 0;
|
||||
}
|
||||
|
||||
if (config_block[0x1e] != 0xFF)
|
||||
global_settings.peak_meter_release = config_block[0x1e];
|
||||
if (config_block[0x1e] != 0xFF) {
|
||||
global_settings.peak_meter_release = config_block[0x1e] & 0x7f;
|
||||
global_settings.peak_meter_dbfs = (config_block[0x1e] & 0x80) != 0;
|
||||
}
|
||||
|
||||
if (config_block[0x1f] != 0xFF)
|
||||
global_settings.repeat_mode = config_block[0x1f];
|
||||
global_settings.peak_meter_min = config_block[0x1f];
|
||||
|
||||
if (config_block[0x20] != 0xFF)
|
||||
global_settings.peak_meter_max = config_block[0x20];
|
||||
|
||||
if (config_block[0x21] != 0xFF)
|
||||
global_settings.repeat_mode = config_block[0x21];
|
||||
|
||||
if (config_block[0x24] != 0xFF)
|
||||
memcpy(&global_settings.total_uptime, &config_block[0x24], 4);
|
||||
|
@ -685,8 +741,12 @@ void settings_reset(void) {
|
|||
global_settings.browse_current = false;
|
||||
global_settings.play_selected = true;
|
||||
global_settings.peak_meter_release = 8;
|
||||
global_settings.peak_meter_hold = 1;
|
||||
global_settings.peak_meter_hold = 3;
|
||||
global_settings.peak_meter_clip_hold = 16;
|
||||
global_settings.peak_meter_dbfs = true;
|
||||
global_settings.peak_meter_min = 60;
|
||||
global_settings.peak_meter_max = 0;
|
||||
global_settings.peak_meter_performance = false;
|
||||
global_settings.wps_file[0] = 0;
|
||||
global_settings.font_file[0] = 0;
|
||||
global_settings.lang_file[0] = 0;
|
||||
|
|
|
@ -100,6 +100,10 @@ struct user_settings
|
|||
int peak_meter_release; /* units per read out */
|
||||
int peak_meter_hold; /* hold time for peak meter in 1/100 s */
|
||||
int peak_meter_clip_hold; /* hold time for clips */
|
||||
bool peak_meter_dbfs; /* show linear or dbfs values */
|
||||
bool peak_meter_performance; /* true: high performance, else save energy*/
|
||||
unsigned char peak_meter_min; /* range minimum */
|
||||
unsigned char peak_meter_max; /* range maximum */
|
||||
|
||||
/* show status bar */
|
||||
bool statusbar; /* 0=hide, 1=show */
|
||||
|
@ -121,6 +125,7 @@ int settings_save(void);
|
|||
void settings_load(void);
|
||||
void settings_reset(void);
|
||||
void settings_apply(void);
|
||||
void settings_apply_pm_range(void);
|
||||
void settings_display(void);
|
||||
|
||||
bool settings_load_config(char* file);
|
||||
|
|
|
@ -74,27 +74,44 @@ static bool volume_type(void)
|
|||
names, 2, NULL);
|
||||
}
|
||||
|
||||
#ifdef PM_DEBUG
|
||||
static bool peak_meter_fps_menu(void) {
|
||||
bool retval = false;
|
||||
retval = set_int( "Refresh rate", "/s",
|
||||
&peak_meter_fps,
|
||||
NULL, 1, 5, 40);
|
||||
return retval;
|
||||
}
|
||||
#endif /* PM_DEBUG */
|
||||
|
||||
/**
|
||||
* Menu to set the hold time of normal peaks.
|
||||
*/
|
||||
static bool peak_meter_hold(void)
|
||||
{
|
||||
static bool peak_meter_hold(void) {
|
||||
bool retval = false;
|
||||
char* names[] = { str(LANG_OFF),
|
||||
"200 ms ", "300 ms ", "500 ms ", "1 s ", "2 s ",
|
||||
"3 s ", "4 s ", "5 s ", "6 s ", "7 s",
|
||||
"8 s", "9 s", "10 s", "15 s", "20 s",
|
||||
"30 s", "1 min"
|
||||
};
|
||||
return set_option( str(LANG_PM_PEAK_HOLD),
|
||||
retval = set_option( str(LANG_PM_PEAK_HOLD),
|
||||
&global_settings.peak_meter_hold, names,
|
||||
18, NULL);
|
||||
|
||||
peak_meter_init_times(global_settings.peak_meter_release,
|
||||
global_settings.peak_meter_hold,
|
||||
global_settings.peak_meter_clip_hold);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu to set the hold time of clips.
|
||||
*/
|
||||
static bool peak_meter_clip_hold(void)
|
||||
{
|
||||
static bool peak_meter_clip_hold(void) {
|
||||
bool retval = false;
|
||||
|
||||
char* names[] = { str(LANG_PM_ETERNAL),
|
||||
"1s ", "2s ", "3s ", "4s ", "5s ",
|
||||
"6s ", "7s ", "8s ", "9s ", "10s",
|
||||
|
@ -103,19 +120,167 @@ static bool peak_meter_clip_hold(void)
|
|||
"10min", "20min", "45min", "90min"
|
||||
};
|
||||
|
||||
return set_option( str(LANG_PM_CLIP_HOLD),
|
||||
retval = set_option( str(LANG_PM_CLIP_HOLD),
|
||||
&global_settings.peak_meter_clip_hold, names,
|
||||
25, peak_meter_set_clip_hold);
|
||||
|
||||
peak_meter_init_times(global_settings.peak_meter_release,
|
||||
global_settings.peak_meter_hold,
|
||||
global_settings.peak_meter_clip_hold);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu to set the release time of the peak meter.
|
||||
*/
|
||||
static bool peak_meter_release(void)
|
||||
{
|
||||
return set_int( str(LANG_PM_RELEASE), str(LANG_PM_UNITS_PER_READ),
|
||||
static bool peak_meter_release(void) {
|
||||
bool retval = false;
|
||||
|
||||
/* The range of peak_meter_release is restricted so that it
|
||||
fits into a 7 bit number. The 8th bit is used for storing
|
||||
something else in the rtc ram.
|
||||
Also, the max value is 0x7e, since the RTC value 0xff is reserved */
|
||||
retval = set_int( str(LANG_PM_RELEASE), str(LANG_PM_UNITS_PER_READ),
|
||||
&global_settings.peak_meter_release,
|
||||
NULL, 1, 1, LCD_WIDTH);
|
||||
NULL, 1, 1, 0x7e);
|
||||
|
||||
peak_meter_init_times(global_settings.peak_meter_release,
|
||||
global_settings.peak_meter_hold,
|
||||
global_settings.peak_meter_clip_hold);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu to select wether the scale of the meter
|
||||
* displays dBfs of linear values.
|
||||
*/
|
||||
static bool peak_meter_scale(void) {
|
||||
bool retval = false;
|
||||
bool use_dbfs = global_settings.peak_meter_dbfs;
|
||||
retval = set_bool_options(str(LANG_PM_SCALE),
|
||||
&use_dbfs,
|
||||
str(LANG_PM_DBFS), str(LANG_PM_LINEAR));
|
||||
|
||||
/* has the user really changed the scale? */
|
||||
if (use_dbfs != global_settings.peak_meter_dbfs) {
|
||||
|
||||
/* store the change */
|
||||
global_settings.peak_meter_dbfs = use_dbfs;
|
||||
peak_meter_set_use_dbfs(use_dbfs);
|
||||
|
||||
/* If the user changed the scale mode the meaning of
|
||||
peak_meter_min (peak_meter_max) has changed. Thus we have
|
||||
to convert the values stored in global_settings. */
|
||||
if (use_dbfs) {
|
||||
|
||||
/* we only store -dBfs */
|
||||
global_settings.peak_meter_min = -peak_meter_get_min() / 100;
|
||||
global_settings.peak_meter_max = -peak_meter_get_max() / 100;
|
||||
} else {
|
||||
int max;
|
||||
|
||||
/* linear percent */
|
||||
global_settings.peak_meter_min = peak_meter_get_min();
|
||||
|
||||
/* converting dBfs -> percent results in a precision loss.
|
||||
I assume that the user doesn't bother that conversion
|
||||
dBfs <-> percent isn't symmetrical for odd values but that
|
||||
he wants 0 dBfs == 100%. Thus I 'correct' the percent value
|
||||
resulting from dBfs -> percent manually here */
|
||||
max = peak_meter_get_max();
|
||||
global_settings.peak_meter_max = max < 99 ? max : 100;
|
||||
}
|
||||
settings_apply_pm_range();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the min value of the value range that
|
||||
* the peak meter shall visualize.
|
||||
*/
|
||||
static bool peak_meter_min(void) {
|
||||
bool retval = false;
|
||||
if (global_settings.peak_meter_dbfs) {
|
||||
|
||||
/* for dBfs scale */
|
||||
int range_max = -global_settings.peak_meter_max;
|
||||
int min = -global_settings.peak_meter_min;
|
||||
|
||||
retval = set_int(str(LANG_PM_MIN), str(LANG_PM_DBFS),
|
||||
&min, NULL, 1, -89, range_max);
|
||||
|
||||
global_settings.peak_meter_min = - min;
|
||||
}
|
||||
|
||||
/* for linear scale */
|
||||
else {
|
||||
int min = global_settings.peak_meter_min;
|
||||
|
||||
retval = set_int(str(LANG_PM_MIN), "%",
|
||||
&min, NULL,
|
||||
1, 0, global_settings.peak_meter_max - 1);
|
||||
|
||||
global_settings.peak_meter_min = (unsigned char)min;
|
||||
}
|
||||
|
||||
settings_apply_pm_range();
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adjust the max value of the value range that
|
||||
* the peak meter shall visualize.
|
||||
*/
|
||||
static bool peak_meter_max(void) {
|
||||
bool retval = false;
|
||||
if (global_settings.peak_meter_dbfs) {
|
||||
|
||||
/* for dBfs scale */
|
||||
int range_min = -global_settings.peak_meter_min;
|
||||
int max = -global_settings.peak_meter_max;;
|
||||
|
||||
retval = set_int(str(LANG_PM_MAX), str(LANG_PM_DBFS),
|
||||
&max, NULL, 1, range_min, 0);
|
||||
|
||||
global_settings.peak_meter_max = - max;
|
||||
|
||||
}
|
||||
|
||||
/* for linear scale */
|
||||
else {
|
||||
int max = global_settings.peak_meter_max;
|
||||
|
||||
retval = set_int(str(LANG_PM_MAX), "%",
|
||||
&max, NULL,
|
||||
1, global_settings.peak_meter_min + 1, 100);
|
||||
|
||||
global_settings.peak_meter_max = (unsigned char)max;
|
||||
}
|
||||
|
||||
settings_apply_pm_range();
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu to select wether the meter is in
|
||||
* precision or in energy saver mode
|
||||
*/
|
||||
static bool peak_meter_performance(void) {
|
||||
bool retval = false;
|
||||
retval = set_bool_options(str(LANG_PM_PERFORMANCE),
|
||||
&global_settings.peak_meter_performance,
|
||||
str(LANG_PM_HIGH_PERFORMANCE), str(LANG_PM_ENERGY_SAVER));
|
||||
|
||||
if (global_settings.peak_meter_performance) {
|
||||
peak_meter_fps = 25;
|
||||
} else {
|
||||
peak_meter_fps = 20;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,6 +295,13 @@ static bool peak_meter_menu(void)
|
|||
{ str(LANG_PM_RELEASE) , peak_meter_release },
|
||||
{ str(LANG_PM_PEAK_HOLD), peak_meter_hold },
|
||||
{ str(LANG_PM_CLIP_HOLD), peak_meter_clip_hold },
|
||||
{ str(LANG_PM_PERFORMANCE), peak_meter_performance },
|
||||
#ifdef PM_DEBUG
|
||||
{ "Refresh rate" , peak_meter_fps_menu },
|
||||
#endif
|
||||
{ str(LANG_PM_SCALE) , peak_meter_scale },
|
||||
{ str(LANG_PM_MIN) , peak_meter_min },
|
||||
{ str(LANG_PM_MAX) , peak_meter_max },
|
||||
};
|
||||
|
||||
m=menu_init( items, sizeof items / sizeof(struct menu_items) );
|
||||
|
@ -137,7 +309,7 @@ static bool peak_meter_menu(void)
|
|||
menu_exit(m);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#endif /* HAVE_LCD_BITMAP */
|
||||
|
||||
static bool shuffle(void)
|
||||
{
|
||||
|
|
|
@ -55,17 +55,10 @@
|
|||
#endif
|
||||
|
||||
#define FORMAT_BUFFER_SIZE 300
|
||||
struct format_flags
|
||||
{
|
||||
bool dynamic;
|
||||
bool scroll;
|
||||
bool player_progress;
|
||||
bool peak_meter;
|
||||
};
|
||||
|
||||
static char format_buffer[FORMAT_BUFFER_SIZE];
|
||||
static char* format_lines[MAX_LINES];
|
||||
static bool dynamic_lines[MAX_LINES];
|
||||
static unsigned char line_type[MAX_LINES];
|
||||
static int ff_rewind_count;
|
||||
bool wps_time_countup = true;
|
||||
static bool wps_loaded = false;
|
||||
|
@ -218,9 +211,7 @@ static char* get_dir(char* buf, int buf_size, char* path, int level)
|
|||
* buf - buffer to certain tags, such as track number, play time or
|
||||
* directory name.
|
||||
* buf_size - size of buffer.
|
||||
* flags - flags in this struct will be set depending on the tag:
|
||||
* dynamic - if the tag data changes over time (like play time);
|
||||
* player_progress - set if the tag is %pb.
|
||||
* flags - returns the type of the line. See constants i wps-display.h
|
||||
*
|
||||
* Returns the tag. NULL indicates the tag wasn't available.
|
||||
*/
|
||||
|
@ -228,7 +219,7 @@ static char* get_tag(struct mp3entry* id3,
|
|||
char* tag,
|
||||
char* buf,
|
||||
int buf_size,
|
||||
struct format_flags* flags)
|
||||
unsigned char* flags)
|
||||
{
|
||||
if ((0 == tag[0]) || (0 == tag[1]))
|
||||
{
|
||||
|
@ -238,6 +229,7 @@ static char* get_tag(struct mp3entry* id3,
|
|||
switch (tag[0])
|
||||
{
|
||||
case 'i': /* ID3 Information */
|
||||
*flags |= WPS_REFRESH_STATIC;
|
||||
switch (tag[1])
|
||||
{
|
||||
case 't': /* ID3 Title */
|
||||
|
@ -263,6 +255,7 @@ static char* get_tag(struct mp3entry* id3,
|
|||
break;
|
||||
|
||||
case 'f': /* File Information */
|
||||
*flags |= WPS_REFRESH_STATIC;
|
||||
switch(tag[1])
|
||||
{
|
||||
case 'v': /* VBR file? */
|
||||
|
@ -310,40 +303,42 @@ static char* get_tag(struct mp3entry* id3,
|
|||
switch(tag[1])
|
||||
{
|
||||
case 'b': /* progress bar */
|
||||
flags->player_progress = true;
|
||||
flags->dynamic = true;
|
||||
*flags |= WPS_REFRESH_PLAYER_PROGRESS;
|
||||
return "\x01";
|
||||
|
||||
case 'p': /* Playlist Position */
|
||||
*flags |= WPS_REFRESH_STATIC;
|
||||
snprintf(buf, buf_size, "%d", id3->index + 1);
|
||||
return buf;
|
||||
|
||||
case 'n': /* Playlist Name (without path) */
|
||||
*flags |= WPS_REFRESH_STATIC;
|
||||
return playlist_name(buf, buf_size);
|
||||
|
||||
case 'e': /* Playlist Total Entries */
|
||||
*flags |= WPS_REFRESH_STATIC;
|
||||
snprintf(buf, buf_size, "%d", playlist_amount());
|
||||
return buf;
|
||||
|
||||
case 'c': /* Current Time in Song */
|
||||
flags->dynamic = true;
|
||||
*flags |= WPS_REFRESH_DYNAMIC;
|
||||
format_time(buf, buf_size, id3->elapsed + ff_rewind_count);
|
||||
return buf;
|
||||
|
||||
case 'r': /* Remaining Time in Song */
|
||||
flags->dynamic = true;
|
||||
*flags |= WPS_REFRESH_DYNAMIC;
|
||||
format_time(buf, buf_size,
|
||||
id3->length - id3->elapsed - ff_rewind_count);
|
||||
return buf;
|
||||
|
||||
case 't': /* Total Time */
|
||||
*flags |= WPS_REFRESH_STATIC;
|
||||
format_time(buf, buf_size, id3->length);
|
||||
return buf;
|
||||
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
case 'm': /* Peak Meter */
|
||||
flags->peak_meter = true;
|
||||
flags->dynamic = true;
|
||||
*flags |= WPS_REFRESH_PEAK_METER;
|
||||
return "\x01";
|
||||
#endif
|
||||
}
|
||||
|
@ -351,6 +346,7 @@ static char* get_tag(struct mp3entry* id3,
|
|||
|
||||
case 'd': /* Directory path information */
|
||||
{
|
||||
*flags |= WPS_REFRESH_STATIC;
|
||||
int level = tag[1] - '0';
|
||||
/* d1 through d9 */
|
||||
if ((0 < level) && (9 > level))
|
||||
|
@ -437,16 +433,13 @@ static char* skip_conditional(char* fmt, bool to_else)
|
|||
* buf_size - the size of buffer.
|
||||
* id3 - the ID3 data to format with.
|
||||
* fmt - format description.
|
||||
* flags - flags in this struct will be set depending on the tag:
|
||||
* dynamic - if the tag data changes over time (like play time);
|
||||
* player_progress - set if the tag is %pb.
|
||||
* scroll - if line scrolling is requested.
|
||||
* flags - returns the type of the line. See constants i wps-display.h
|
||||
*/
|
||||
static void format_display(char* buf,
|
||||
int buf_size,
|
||||
struct mp3entry* id3,
|
||||
char* fmt,
|
||||
struct format_flags* flags)
|
||||
unsigned char* flags)
|
||||
{
|
||||
char temp_buf[128];
|
||||
char* buf_end = buf + buf_size - 1; /* Leave room for end null */
|
||||
|
@ -483,7 +476,7 @@ static void format_display(char* buf,
|
|||
break;
|
||||
|
||||
case 's':
|
||||
flags->scroll = true;
|
||||
*flags |= WPS_REFRESH_SCROLL;
|
||||
++fmt;
|
||||
break;
|
||||
|
||||
|
@ -526,10 +519,10 @@ static void format_display(char* buf,
|
|||
*buf = 0;
|
||||
}
|
||||
|
||||
bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all)
|
||||
bool wps_refresh(struct mp3entry* id3, int ffwd_offset, unsigned char refresh_mode)
|
||||
{
|
||||
char buf[MAX_PATH];
|
||||
struct format_flags flags;
|
||||
unsigned char flags;
|
||||
int i;
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
/* to find out wether the peak meter is enabled we
|
||||
|
@ -554,16 +547,15 @@ bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all)
|
|||
if ( !format_lines[i] )
|
||||
break;
|
||||
|
||||
if (dynamic_lines[i] || refresh_all)
|
||||
if ((line_type[i] & refresh_mode) ||
|
||||
(refresh_mode == WPS_REFRESH_ALL))
|
||||
{
|
||||
flags.dynamic = false;
|
||||
flags.scroll = false;
|
||||
flags.player_progress = false;
|
||||
flags.peak_meter = false;
|
||||
flags = 0;
|
||||
format_display(buf, sizeof(buf), id3, format_lines[i], &flags);
|
||||
dynamic_lines[i] = flags.dynamic;
|
||||
line_type[i] = flags;
|
||||
|
||||
if (flags.player_progress) {
|
||||
/* progress */
|
||||
if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
|
||||
#ifdef HAVE_LCD_CHARCELLS
|
||||
draw_player_progress(id3, ff_rewind_count);
|
||||
#else
|
||||
|
@ -578,11 +570,12 @@ bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all)
|
|||
}
|
||||
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
if (flags.peak_meter) {
|
||||
/* peak meter */
|
||||
if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
|
||||
int peak_meter_y;
|
||||
int w,h;
|
||||
struct font *fnt = font_get(FONT_UI);
|
||||
int h = fnt->height;
|
||||
int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0;
|
||||
lcd_getstringsize("M",&w,&h);
|
||||
|
||||
peak_meter_y = i * h + offset;
|
||||
|
||||
|
@ -601,10 +594,13 @@ bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_all)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (flags.scroll && !flags.dynamic)
|
||||
/* static line */
|
||||
if (flags & WPS_REFRESH_SCROLL)
|
||||
{
|
||||
if (refresh_mode & WPS_REFRESH_SCROLL) {
|
||||
lcd_puts_scroll(0, i, buf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lcd_puts(0, i, buf);
|
||||
|
@ -656,7 +652,7 @@ bool wps_display(struct mp3entry* id3)
|
|||
}
|
||||
}
|
||||
}
|
||||
wps_refresh(id3, 0, true);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_ALL);
|
||||
status_draw();
|
||||
lcd_update();
|
||||
return false;
|
||||
|
|
|
@ -22,7 +22,18 @@
|
|||
#include <stdbool.h>
|
||||
#include "id3.h"
|
||||
|
||||
bool wps_refresh(struct mp3entry* id3, int ffwd_offset, bool refresh_scroll);
|
||||
/* constants used in line_type and as refresh_mode for wps_refresh */
|
||||
#define WPS_REFRESH_STATIC 1 /* line doesn't change over time */
|
||||
#define WPS_REFRESH_DYNAMIC 2 /* line may change (e.g. time flag) */
|
||||
#define WPS_REFRESH_SCROLL 4 /* line scrolls */
|
||||
#define WPS_REFRESH_PLAYER_PROGRESS 8 /* line contains a progress bar */
|
||||
#define WPS_REFRESH_PEAK_METER 16 /* line contains a peak meter */
|
||||
#define WPS_REFRESH_ALL 0xff /* to refresh all line types */
|
||||
/* to refresh only those lines that change over time */
|
||||
#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_ALL & ~WPS_REFRESH_STATIC & ~WPS_REFRESH_SCROLL)
|
||||
|
||||
|
||||
bool wps_refresh(struct mp3entry* id3, int ffwd_offset, unsigned char refresh_mode);
|
||||
bool wps_display(struct mp3entry* id3);
|
||||
bool wps_load(char* file, bool display);
|
||||
void wps_reset(void);
|
||||
|
|
62
apps/wps.c
62
apps/wps.c
|
@ -68,7 +68,7 @@ void player_change_volume(int button)
|
|||
if(global_settings.volume > mpeg_sound_max(SOUND_VOLUME))
|
||||
global_settings.volume = mpeg_sound_max(SOUND_VOLUME);
|
||||
mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
|
||||
wps_refresh(id3,0,false);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_NON_STATIC);
|
||||
settings_save();
|
||||
break;
|
||||
|
||||
|
@ -78,7 +78,7 @@ void player_change_volume(int button)
|
|||
if(global_settings.volume < mpeg_sound_min(SOUND_VOLUME))
|
||||
global_settings.volume = mpeg_sound_min(SOUND_VOLUME);
|
||||
mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
|
||||
wps_refresh(id3,0,false);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_NON_STATIC);
|
||||
settings_save();
|
||||
break;
|
||||
|
||||
|
@ -100,7 +100,7 @@ void player_change_volume(int button)
|
|||
button = button_get(true);
|
||||
}
|
||||
status_draw();
|
||||
wps_refresh(id3,0,true);
|
||||
wps_refresh(id3,0, WPS_REFRESH_ALL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -366,9 +366,9 @@ static bool ffwd_rew(int button)
|
|||
}
|
||||
|
||||
if(wps_time_countup == false)
|
||||
wps_refresh(id3, -ff_rewind_count, false);
|
||||
wps_refresh(id3, -ff_rewind_count, WPS_REFRESH_PLAYER_PROGRESS);
|
||||
else
|
||||
wps_refresh(id3, ff_rewind_count, false);
|
||||
wps_refresh(id3, ff_rewind_count, WPS_REFRESH_PLAYER_PROGRESS);
|
||||
|
||||
break;
|
||||
|
||||
|
@ -399,7 +399,7 @@ static bool ffwd_rew(int button)
|
|||
if (!exit)
|
||||
button = button_get(true);
|
||||
}
|
||||
wps_refresh(id3,0,true);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_ALL);
|
||||
return usb;
|
||||
}
|
||||
|
||||
|
@ -415,11 +415,11 @@ static bool update(void)
|
|||
if (wps_display(id3))
|
||||
retcode = true;
|
||||
else
|
||||
wps_refresh(id3,0,true);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_ALL);
|
||||
}
|
||||
|
||||
if (id3)
|
||||
wps_refresh(id3,0,false);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_NON_STATIC);
|
||||
|
||||
status_draw();
|
||||
|
||||
|
@ -453,7 +453,7 @@ static bool keylock(void)
|
|||
#endif
|
||||
display_keylock_text(true);
|
||||
keys_locked = true;
|
||||
wps_refresh(id3,0,true);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_ALL);
|
||||
if (wps_display(id3)) {
|
||||
keys_locked = false;
|
||||
#ifdef HAVE_LCD_CHARCELLS
|
||||
|
@ -511,7 +511,7 @@ static bool keylock(void)
|
|||
default:
|
||||
display_keylock_text(true);
|
||||
while (button_get(false)); /* clear button queue */
|
||||
wps_refresh(id3,0,true);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_ALL);
|
||||
if (wps_display(id3)) {
|
||||
keys_locked = false;
|
||||
#ifdef HAVE_LCD_CHARCELLS
|
||||
|
@ -628,7 +628,7 @@ static bool menu(void)
|
|||
#endif
|
||||
|
||||
wps_display(id3);
|
||||
wps_refresh(id3,0,true);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_ALL);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -659,7 +659,7 @@ int wps_show(void)
|
|||
if (id3) {
|
||||
if (wps_display(id3))
|
||||
return 0;
|
||||
wps_refresh(id3,0,true);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_ALL);
|
||||
}
|
||||
restore = true;
|
||||
}
|
||||
|
@ -674,14 +674,44 @@ int wps_show(void)
|
|||
isn't displayed */
|
||||
if (peak_meter_enabled) {
|
||||
int i;
|
||||
|
||||
/* In high performance mode we read out the mas as
|
||||
often as we can. There is no sleep for cpu */
|
||||
if (global_settings.peak_meter_performance) {
|
||||
long next_refresh = current_tick;
|
||||
long next_big_refresh = current_tick + HZ / 5;
|
||||
button = BUTTON_NONE;
|
||||
while (!TIME_AFTER(current_tick, next_big_refresh)) {
|
||||
button = button_get(false);
|
||||
if (button != BUTTON_NONE) {
|
||||
break;
|
||||
}
|
||||
peak_meter_peek();
|
||||
yield();
|
||||
|
||||
if (TIME_AFTER(current_tick, next_refresh)) {
|
||||
wps_refresh(id3, 0, WPS_REFRESH_PEAK_METER);
|
||||
next_refresh = current_tick + HZ / peak_meter_fps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In energy saver mode the cpu may sleep a
|
||||
little bit while waiting for buttons */
|
||||
else {
|
||||
for (i = 0; i < 4; i++) {
|
||||
button = button_get_w_tmo(HZ / 20);
|
||||
button = button_get_w_tmo(HZ / peak_meter_fps);
|
||||
if (button != 0) {
|
||||
break;
|
||||
}
|
||||
wps_refresh(id3, 0, false);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_PEAK_METER);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
/* The peak meter is disabled
|
||||
-> no additional screen updates needed */
|
||||
else {
|
||||
button = button_get_w_tmo(HZ/5);
|
||||
}
|
||||
#else
|
||||
|
@ -880,7 +910,7 @@ int wps_show(void)
|
|||
if (wps_display(id3))
|
||||
return 0;
|
||||
if (id3)
|
||||
wps_refresh(id3,0,false);
|
||||
wps_refresh(id3, 0, WPS_REFRESH_NON_STATIC);
|
||||
}
|
||||
if(button != BUTTON_NONE)
|
||||
lastbutton = button;
|
||||
|
|
Loading…
Reference in a new issue