summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2002-10-29 12:09:15 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2002-10-29 12:09:15 +0000
commitfd0cc3b2b1302d77f3404861509e75c64fd505af (patch)
treeacdb25b87a549908424e052c04cc323d02b681f4 /apps
parent8f11dc00ac1a0a5fe009d1d07d9a1378c3300ba8 (diff)
downloadrockbox-fd0cc3b2b1302d77f3404861509e75c64fd505af.tar.gz
rockbox-fd0cc3b2b1302d77f3404861509e75c64fd505af.tar.bz2
rockbox-fd0cc3b2b1302d77f3404861509e75c64fd505af.zip
Phil Pertermann's dB peak meter patch
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2774 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/debug_menu.c7
-rw-r--r--apps/lang/english.lang40
-rw-r--r--apps/recorder/peakmeter.c631
-rw-r--r--apps/recorder/peakmeter.h22
-rw-r--r--apps/settings.c86
-rw-r--r--apps/settings.h5
-rw-r--r--apps/settings_menu.c194
-rw-r--r--apps/wps-display.c72
-rw-r--r--apps/wps-display.h13
-rw-r--r--apps/wps.c62
10 files changed, 1034 insertions, 98 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 97db40ee7d..4dd98e0498 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -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 */
-
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 3f1eb6efb8..373e072395 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -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"
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c
index df7c672617..e1850a4bb0 100644
--- a/apps/recorder/peakmeter.c
+++ b/apps/recorder/peakmeter.c
@@ -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,20 +502,58 @@ 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.
@@ -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);
+
+ 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);
+ }
+ }
- /* scale the samples */
- left /= (MAX_PEAK / meterwidth);
- right /= (MAX_PEAK / meterwidth);
+ /* 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
diff --git a/apps/recorder/peakmeter.h b/apps/recorder/peakmeter.h
index a5f25808e6..356926f9de 100644
--- a/apps/recorder/peakmeter.h
+++ b/apps/recorder/peakmeter.h
@@ -19,12 +19,32 @@
#ifndef __PEAKMETER_H__
#define __PEAKMETER_H__
+/*#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__ */
diff --git a/apps/settings.c b/apps/settings.c
index cee7a286f6..c17f42fd52 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -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;
diff --git a/apps/settings.h b/apps/settings.h
index bc8767d532..655b2e0245 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -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);
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index 6f527f5224..ed2f97e390 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -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)
{
diff --git a/apps/wps-display.c b/apps/wps-display.c
index 6bca8cb40d..b70c1359d1 100644
--- a/apps/wps-display.c
+++ b/apps/wps-display.c
@@ -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;
diff --git a/apps/wps-display.h b/apps/wps-display.h
index fc40e19136..cda90ffffe 100644
--- a/apps/wps-display.h
+++ b/apps/wps-display.h
@@ -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);
diff --git a/apps/wps.c b/apps/wps.c
index 3e48333818..8f9295a293 100644
--- a/apps/wps.c
+++ b/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;