Philip Pertermanns peak meter


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2436 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 83ccf03..afea869 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -834,3 +834,33 @@
 desc: bool false representation
 eng: "No"
 new:
+
+id: LANG_PM_MENU
+desc: in the display menu
+eng: "Peak meter"
+new: 
+
+id:   LANG_PM_RELEASE
+desc: in the peak meter menu
+eng: "Peak release"
+new: 
+
+id: LANG_PM_PEAK_HOLD
+desc: in the peak meter menu
+eng: "Peak hold time"
+new: 
+
+id: LANG_PM_CLIP_HOLD
+desc: in the peak meter menu
+eng: "Clip hold time"
+new: 
+
+id: LANG_PM_ETERNAL
+desc: in the peak meter menu
+eng: "eternal"
+new: 
+
+id: LANG_PM_UNITS_PER_READ
+desc: in the peak meter menu
+eng: "Units per read"
+new: 
diff --git a/apps/settings.c b/apps/settings.c
index 31d4f1a..1b4f864 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -53,6 +53,12 @@
 #define CONFIG_BLOCK_SIZE 512
 #define RTC_BLOCK_SIZE 44
 
+#ifdef HAVE_LCD_BITMAP
+#define MAX_LINES 10
+#else
+#define MAX_LINES 2
+#endif
+
 /********************************************
 
 Config block as saved on the battery-packed RTC user RAM memory block
@@ -80,6 +86,9 @@
 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>
 
         <all unused space filled with 0xff>
 
@@ -290,6 +299,10 @@
         (((global_settings.browse_current & 1)) |
          ((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;
+
     memcpy(&config_block[0xF8], &global_settings.resume_seed, 4);
 
     memcpy(&config_block[0x24], &global_settings.total_uptime, 4);
@@ -400,6 +413,15 @@
             global_settings.play_selected = (config_block[0x1b] >> 1) & 1;
         }
 
+        if (config_block[0x1c] != 0xFF)
+            global_settings.peak_meter_hold = (config_block[0x1c]) & 0x1f;
+
+        if (config_block[0x1d] != 0xFF)
+            global_settings.peak_meter_clip_hold = (config_block[0x1d]) & 0x1f;
+
+        if (config_block[0x1e] != 0xFF)
+            global_settings.peak_meter_release = config_block[0x1e];
+
         memcpy(&global_settings.resume_seed, &config_block[0xF8], 4);
 
         if (config_block[0x24] != 0xFF)
@@ -471,7 +493,7 @@
                                 break;
                             case 3:
                                 snprintf(buf_disp,sizeof(buf_disp),"[%s]%s", buf_set, buf_val);
-                                lcd_puts(0,line++ % 6,buf_disp);
+                                lcd_puts(0,line++ % MAX_LINES, buf_disp);
                                 lcd_update();
                                 sleep(HZ/2);
                                 if (!strcasecmp(buf_set,"volume")) {
@@ -610,6 +632,9 @@
     global_settings.disk_spindown = 5;
     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_clip_hold = 16;
 }
 
 
diff --git a/apps/settings.h b/apps/settings.h
index 048c673..bbc7bb3 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -86,6 +86,10 @@
     int ff_rewind_accel; /* FF/Rewind acceleration (in seconds per doubling) */
     int disk_spindown; /* time until disk spindown, in seconds (0=off) */
 
+    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 */
+
     /* show status bar */
     bool statusbar;    /* 0=hide, 1=show */
 
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index 8aba4bc..643f5be 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -36,6 +36,7 @@
 #include "powermgmt.h"
 #include "rtc.h"
 #include "ata.h"
+#include "peakmeter.h"
 #include "lang.h"
 
 static bool contrast(void)
@@ -44,6 +45,68 @@
                     lcd_set_contrast, 1, 0, MAX_CONTRAST_SETTING );
 }
 
+#ifdef HAVE_LCD_BITMAP
+/**
+ * Menu to set the hold time of normal peaks.
+ */
+static bool peak_meter_hold(void) {
+    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), 
+        &global_settings.peak_meter_hold, names,
+        18, NULL);
+}
+
+/**
+ * Menu to set the hold time of clips.
+ */
+static bool peak_meter_clip_hold(void) {
+    char* names[] = { str(LANG_PM_ETERNAL),
+        "1s ", "2s ", "3s ", "4s ", "5s ",
+        "6s ", "7s ", "8s ", "9s ", "10s",
+        "15s", "20s", "25s", "30s", "45s",
+        "60s", "90s", "2min", "3min", "5min",
+        "10min", "20min", "45min", "90min"
+    };
+    return set_option( str(LANG_PM_CLIP_HOLD), 
+        &global_settings.peak_meter_clip_hold, names,
+        25, peak_meter_set_clip_hold);
+}
+
+/**
+ * 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), 
+             &global_settings.peak_meter_release,
+             NULL, 1, 1, LCD_WIDTH);
+}
+
+/**
+ * Menu to configure the peak meter
+ */
+static bool peak_meter_menu(void) 
+{
+    int m;
+    bool result;
+
+    struct menu_items items[] = {
+        { str(LANG_PM_RELEASE)  , peak_meter_release   },  
+        { str(LANG_PM_PEAK_HOLD), peak_meter_hold      },  
+        { str(LANG_PM_CLIP_HOLD), peak_meter_clip_hold },
+    };
+    
+    m=menu_init( items, sizeof items / sizeof(struct menu_items) );
+    result = menu_run(m);
+    menu_exit(m);
+    return result;
+}
+#endif
+
 #ifndef HAVE_RECORDER_KEYPAD
 static bool shuffle(void)
 {
@@ -313,6 +376,9 @@
         { str(LANG_SCROLL_MENU),     scroll_speed    },  
         { str(LANG_BACKLIGHT),       backlight_timer },
         { str(LANG_CONTRAST),        contrast        },  
+#ifdef HAVE_LCD_BITMAP
+        { str(LANG_PM_MENU),         peak_meter_menu },  
+#endif
     };
     
     m=menu_init( items, sizeof items / sizeof(struct menu_items) );
diff --git a/apps/wps-display.c b/apps/wps-display.c
index ec1e9c7..a898085 100644
--- a/apps/wps-display.c
+++ b/apps/wps-display.c
@@ -37,10 +37,13 @@
 #include "status.h"
 #include "wps-display.h"
 #include "debug.h"
+#include "mas.h"
 #include "lang.h"
+
 #ifdef HAVE_LCD_BITMAP
 #include "icons.h"
 #include "widgets.h"
+#include "peakmeter.h"
 #endif
 
 #define WPS_CONFIG ROCKBOX_DIR "/default.wps"
@@ -52,12 +55,12 @@
 #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];
@@ -331,6 +334,13 @@
                 case 't':  /* Total Time */
                     format_time(buf, buf_size, id3->length);
                     return buf;
+
+#ifdef HAVE_LCD_BITMAP
+                case 'm': /* Peak Meter */
+                    flags->peak_meter = true;
+                    flags->dynamic = true;
+                    return "\x01";
+#endif
             }
             break;
     
@@ -518,6 +528,15 @@
     bool scroll_active = false;
     int i;
 
+    /* to find out wether the peak meter is enabled we
+       assume it wasn't until we find a line that contains
+       the peak meter. We can't use peak_meter_enabled itself
+       because that would mean to turn off the meter thread 
+       temporarily. (That shouldn't matter unless yield 
+       or sleep is called but who knows...)
+    */
+    bool enable_pm = false;
+
     if (!id3)
     {
         lcd_stop_scroll();
@@ -537,6 +556,7 @@
             flags.dynamic = false;
             flags.scroll = false;
             flags.player_progress = false;
+            flags.peak_meter = false;
             format_display(buf, sizeof(buf), id3, format_lines[i], &flags);
             dynamic_lines[i] = flags.dynamic;
             
@@ -556,6 +576,30 @@
 #endif
             }
 
+#ifdef HAVE_LCD_BITMAP
+            if (flags.peak_meter) {
+                int peak_meter_y;
+                int w,h;
+                int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0;
+                lcd_getstringsize("M",&w,&h);
+
+                peak_meter_y = i * h + offset;
+
+                /* The user might decide to have the peak meter in the last 
+                   line so that it is only displayed if no status bar is 
+                   visible. If so we neither want do draw nor enable the
+                   peak meter. */
+                if (peak_meter_y + h <= LCD_HEIGHT) {
+                    /* found a line with a peak meter -> remember that we must
+                       enable it later */
+                    enable_pm = true;
+                    peak_meter_draw(0, peak_meter_y, LCD_WIDTH,
+                                    MIN(h, LCD_HEIGHT - peak_meter_y));
+                }
+                continue;
+            }
+#endif
+
             if (!scroll_active && flags.scroll && !flags.dynamic)
             {
                 scroll_active = true;
@@ -567,6 +611,10 @@
             }
         }
     }
+
+    /* Now we know wether the peak meter is used. 
+       So we can enable / disable the peak meter thread */
+    peak_meter_enabled = enable_pm;
     lcd_update();
 
     return true;
@@ -602,7 +650,8 @@
                            "%ia\n"
                            "%fb kbit %fv\n"
                            "Time: %pc / %pt\n"
-                           "%pb\n");
+                           "%pb\n"
+                           "%pm\n");
 #else
                 wps_format("%s%pp/%pe: %?ia<%ia - >%?it<%it|%fm>\n"
                            "%pc/%pt\n");
diff --git a/apps/wps.c b/apps/wps.c
index 2729b28..db4459c 100644
--- a/apps/wps.c
+++ b/apps/wps.c
@@ -40,6 +40,7 @@
 #include "screens.h"
 #ifdef HAVE_LCD_BITMAP
 #include "icons.h"
+#include "peakmeter.h"
 #endif
 #include "lang.h"
 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ 
@@ -610,7 +611,7 @@
 /* demonstrates showing different formats from playtune */
 int wps_show(void)
 {
-    int button, lastbutton = 0;
+    int button = 0, lastbutton = 0;
     int old_repeat_mask;
     bool ignore_keyup = true;
     bool restore = false;
@@ -644,7 +645,27 @@
 
     while ( 1 )
     {
+
+#ifdef HAVE_LCD_BITMAP
+        /* when the peak meter is enabled we want to have a
+            few extra updates to make it look smooth. On the
+            other hand we don't want to waste energy if it 
+            isn't displayed */
+        if (peak_meter_enabled) {
+            int i;
+            for (i = 0; i < 4; i++) {
+                button = button_get_w_tmo(HZ / 20);
+                if (button != 0) {
+                    break;
+                }
+                wps_refresh(id3, 0, false);
+            }
+        } else {
+            button = button_get_w_tmo(HZ/5);
+        }
+#else
         button = button_get_w_tmo(HZ/5);
+#endif
 
         /* discard first event if it's a button release */
         if (button && ignore_keyup)
@@ -839,4 +860,5 @@
         if(button != BUTTON_NONE)
             lastbutton = button;
     }
+    return 0; /* unreachable - just to reduce compiler warnings */
 }
diff --git a/docs/CUSTOM_WPS_FORMAT b/docs/CUSTOM_WPS_FORMAT
index 5162d2e..cecb4af 100644
--- a/docs/CUSTOM_WPS_FORMAT
+++ b/docs/CUSTOM_WPS_FORMAT
@@ -46,6 +46,8 @@
         Player: This will display a 1 character "cup" that empties as the
                 progresses.
         Recorder: This will replace the entire line with a progress bar.
+  %pm : Peak Meter (Recorder only)
+        The entire line is used as volume peak meter.
   %pp : Playlist Position
   %pe : Total Number of Playlist Entries
   %pc : Current Time In Song
diff --git a/firmware/drivers/mas.h b/firmware/drivers/mas.h
index 5ccffdf..71604fb 100644
--- a/firmware/drivers/mas.h
+++ b/firmware/drivers/mas.h
@@ -22,6 +22,8 @@
 #define MAS_BANK_D0 0
 #define MAS_BANK_D1 1
 
+#define MAX_PEAK 0x8000
+
 /*
 	MAS I2C	defs
 */
diff --git a/uisimulator/win32/Makefile b/uisimulator/win32/Makefile
index 28e0eea..aa4136c 100644
--- a/uisimulator/win32/Makefile
+++ b/uisimulator/win32/Makefile
@@ -67,7 +67,7 @@
 
 APPS = main.c tree.c menu.c credits.c main_menu.c icons.c language.c \
 	playlist.c showtext.c wps.c wps-display.c settings.c status.c \
-	screens.c
+	screens.c peakmeter.c
 
 MENUS = games_menu.c demo_menu.c settings_menu.c sound_menu.c
 
@@ -203,6 +203,9 @@
 $(OBJDIR)/screens.o: $(APPDIR)/screens.c
 	$(CC) $(APPCFLAGS) -c $< -o $@
 
+$(OBJDIR)/peakmeter.o: $(RECDIR)/peakmeter.c
+	$(CC) $(APPCFLAGS) -c $< -o $@
+
 $(OBJDIR)/version.o: $(FIRMWAREDIR)/version.c
 	$(CC) $(CFLAGS) -c $< -o $@
 
diff --git a/uisimulator/x11/Makefile b/uisimulator/x11/Makefile
index 074b7f0..c3bb4e6 100644
--- a/uisimulator/x11/Makefile
+++ b/uisimulator/x11/Makefile
@@ -82,7 +82,7 @@
 
 APPS = main.c tree.c menu.c credits.c main_menu.c language.c\
 	playlist.c showtext.c wps.c wps-display.c settings.c status.c icons.c\
-	screens.c
+	screens.c peakmeter.c
 
 MENUS = games_menu.c demo_menu.c settings_menu.c sound_menu.c
 
@@ -242,6 +242,9 @@
 $(OBJDIR)/screens.o: $(APPDIR)/screens.c
 	$(CC) $(APPCFLAGS) -c $< -o $@
 
+$(OBJDIR)/peakmeter.o: $(RECDIR)/peakmeter.c
+	$(CC) $(APPCFLAGS) -c $< -o $@
+
 $(OBJDIR)/id3.o: $(FIRMWAREDIR)/id3.c
 	$(CC) $(APPCFLAGS) -c $< -o $@