Added pre-amp setting for files with ReplayGain information.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7303 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/dsp.c b/apps/dsp.c
index 21effc5..701ffb4 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -23,13 +23,14 @@
 #include "playback.h"
 #include "system.h"
 #include "settings.h"
+#include "replaygain.h"
 #include "debug.h"
 
 /* The "dither" code to convert the 24-bit samples produced by libmad was
  * taken from the coolplayer project - coolplayer.sourceforge.net
  */
 
-/* 16-bit samples are scaled based on these constants. The shift should be 
+/* 16-bit samples are scaled based on these constants. The shift should be
  * no more than 15.
  */
 #define WORD_SHIFT          12
@@ -336,7 +337,7 @@
     return sample;
 }
 
-/* The "dither" code to convert the 24-bit samples produced by libmad was 
+/* The "dither" code to convert the 24-bit samples produced by libmad was
  * taken from the coolplayer project - coolplayer.sourceforge.net
  */
 
@@ -386,15 +387,15 @@
         long* d1 = (s0 == s1) ? d0 : &sample_buf[SAMPLE_BUF_SIZE / 2];
         long gain = dsp.replaygain;
         long i;
-        
+
         src[0] = d0;
         src[1] = d1;
-        
+
         for (i = 0; i < count; i++)
         {
             *d0++ = FRACMUL_8(*s0++, gain);
         }
-        
+
         if (src [0] != src [1])
         {
             for (i = 0; i < count; i++)
@@ -639,7 +640,7 @@
         long gain = 0;
 
         dsp.new_gain = false;
-    
+
         if (global_settings.replaygain || global_settings.replaygain_noclip)
         {
             long peak;
@@ -648,28 +649,36 @@
             {
                 gain = (global_settings.replaygain_track || !dsp.album_gain)
                     ? dsp.track_gain : dsp.album_gain;
+
+                if (global_settings.replaygain_preamp)
+                {
+                    long preamp = get_replaygain_int(
+                        global_settings.replaygain_preamp * 10);
+
+                    gain = (long) ((((int64_t) gain * preamp)) >> 24);
+                }
             }
-            
+
             peak = (global_settings.replaygain_track || !dsp.album_peak)
                 ? dsp.track_peak : dsp.album_peak;
-            
+
             if (gain == 0)
             {
                 /* So that noclip can work even with no gain information. */
                 gain = DEFAULT_REPLAYGAIN;
             }
-            
+
             if (global_settings.replaygain_noclip && (peak != 0)
                 && ((((int64_t) gain * peak) >> 24) >= DEFAULT_REPLAYGAIN))
             {
                 gain = (((int64_t) DEFAULT_REPLAYGAIN << 24) / peak);
             }
-            
+
             if (gain == DEFAULT_REPLAYGAIN)
             {
                 /* Nothing to do, disable processing. */
                 gain = 0;
-    
+
             }
         }
 
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 3137ff7..a11da03 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -3244,3 +3244,15 @@
 eng: "BiDi Hebrew/Arabic"
 voice ""
 new:
+
+id: LANG_REPLAYGAIN_PREAMP
+desc: in browse_id3
+eng: "Pre-amp"
+voice "Preamp"
+new:
+
+id: LANG_UNIT_DB
+desc: in browse_id3
+eng: "dB"
+voice ""
+new:
diff --git a/apps/settings.c b/apps/settings.c
index 63e7a3b..295a32f 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -431,6 +431,7 @@
     {1, S_O(replaygain), false, "replaygain", off_on },
     {1, S_O(replaygain_track), false, "replaygain type", "track,album" },
     {1, S_O(replaygain_noclip), false, "replaygain noclip", off_on },
+    {8, S_O(replaygain_preamp), 0, "replaygain preamp", NULL },
 #endif
     
     /* If values are just added to the end, no need to bump the version. */
@@ -1388,7 +1389,8 @@
              void (*function)(int),
              int step,
              int min,
-             int max )
+             int max,
+             void (*formatter)(char*, int, int, const char*) )
 {
     bool done = false;
     int button;
@@ -1407,7 +1409,16 @@
 
     while (!done) {
         char str[32];
-        snprintf(str,sizeof str,"%d %s  ", *variable, unit);
+        
+        if (formatter) 
+        {
+            formatter(str, sizeof str, *variable, unit);
+        }
+        else
+        {
+            snprintf(str,sizeof str,"%d %s  ", *variable, unit);
+        }
+
         lcd_puts(0, 1, str);
 #ifdef HAVE_LCD_BITMAP
         status_draw(true);
diff --git a/apps/settings.h b/apps/settings.h
index ff12186..055b893 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -334,6 +334,7 @@
     bool replaygain;        /* enable replaygain */
     bool replaygain_track;  /* true for track gain, false for album gain */
     bool replaygain_noclip; /* scale to prevent clips */
+    int  replaygain_preamp; /* scale replaygained tracks by this */
 #endif
 };
 
@@ -366,7 +367,8 @@
 bool set_option(const char* string, void* variable, enum optiontype type,
                 const struct opt_items* options, int numoptions, void (*function)(int));
 bool set_int(const char* string, const char* unit, int voice_unit, int* variable,
-             void (*function)(int), int step, int min, int max );
+             void (*function)(int), int step, int min, int max, 
+             void (*formatter)(char*, int, int, const char*) );
 bool set_time_screen(const char* string, struct tm *tm);
 int read_line(int fd, char* buffer, int buffer_size);
 void set_file(char* filename, char* setting, int maxlen);
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index fadbb11..57b4cc8 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -95,7 +95,7 @@
     return set_int( str(LANG_CONTRAST), "", UNIT_INT,
                     &global_settings.remote_contrast, 
                     lcd_remote_set_contrast, 1, MIN_CONTRAST_SETTING,
-                    MAX_CONTRAST_SETTING );
+                    MAX_CONTRAST_SETTING, NULL );
 }
 
 static bool remote_invert(void)
@@ -235,7 +235,7 @@
     return set_int( str(LANG_CONTRAST), "", UNIT_INT,
                     &global_settings.contrast, 
                     lcd_set_contrast, 1, MIN_CONTRAST_SETTING,
-                    MAX_CONTRAST_SETTING );
+                    MAX_CONTRAST_SETTING, NULL );
 }
 
 #ifdef HAVE_LCD_BITMAP
@@ -322,7 +322,7 @@
     bool retval = false;
     retval = set_int( "Refresh rate", "/s", UNIT_PER_SEC,
              &peak_meter_fps,
-             NULL, 1, 5, 40);
+             NULL, 1, 5, 40, NULL);
     return retval;
 }
 #endif /* PM_DEBUG */
@@ -420,7 +420,7 @@
     retval = set_int( str(LANG_PM_RELEASE), str(LANG_PM_UNITS_PER_READ),
                       LANG_PM_UNITS_PER_READ,
                       &global_settings.peak_meter_release,
-                      NULL, 1, 1, 0x7e);
+                      NULL, 1, 1, 0x7e, NULL);
 
     peak_meter_init_times(global_settings.peak_meter_release,
         global_settings.peak_meter_hold, 
@@ -488,7 +488,7 @@
         int min = -global_settings.peak_meter_min;
 
         retval =  set_int(str(LANG_PM_MIN), str(LANG_PM_DBFS), UNIT_DB,
-            &min, NULL, 1, -89, range_max);
+            &min, NULL, 1, -89, range_max, NULL);
 
         global_settings.peak_meter_min = - min;
     } 
@@ -499,7 +499,7 @@
 
         retval =  set_int(str(LANG_PM_MIN), "%", UNIT_PERCENT,
             &min, NULL, 
-            1, 0, global_settings.peak_meter_max - 1);
+            1, 0, global_settings.peak_meter_max - 1, NULL);
 
         global_settings.peak_meter_min = (unsigned char)min;
     }
@@ -522,7 +522,7 @@
         int max = -global_settings.peak_meter_max;;
 
         retval =  set_int(str(LANG_PM_MAX), str(LANG_PM_DBFS), UNIT_DB,
-            &max, NULL, 1, range_min, 0);
+            &max, NULL, 1, range_min, 0, NULL);
 
         global_settings.peak_meter_max = - max;
 
@@ -534,7 +534,7 @@
 
         retval =  set_int(str(LANG_PM_MAX), "%", UNIT_PERCENT,
             &max, NULL, 
-            1, global_settings.peak_meter_min + 1, 100);
+            1, global_settings.peak_meter_min + 1, 100, NULL);
 
         global_settings.peak_meter_max = (unsigned char)max;
     }
@@ -765,7 +765,7 @@
 {
     return set_int(str(LANG_SCROLL), "", UNIT_INT,
                    &global_settings.scroll_speed,
-                   &lcd_scroll_speed, 1, 0, 15 );
+                   &lcd_scroll_speed, 1, 0, 15, NULL );
 }
 
 
@@ -774,7 +774,7 @@
     int dummy = global_settings.scroll_delay * (HZ/10);
     int rc = set_int(str(LANG_SCROLL_DELAY), "ms", UNIT_MS,
                      &dummy, 
-                     &lcd_scroll_delay, 100, 0, 2500 );
+                     &lcd_scroll_delay, 100, 0, 2500, NULL );
     global_settings.scroll_delay = dummy / (HZ/10);
     return rc;
 }
@@ -784,7 +784,7 @@
 {
     return set_int(str(LANG_SCROLL_STEP_EXAMPLE), "pixels", UNIT_PIXEL,
                    &global_settings.scroll_step,
-                   &lcd_scroll_step, 1, 1, LCD_WIDTH );
+                   &lcd_scroll_step, 1, 1, LCD_WIDTH, NULL );
 }
 #endif
 
@@ -792,7 +792,7 @@
 {
     return set_int(str(LANG_BIDIR_SCROLL), "%", UNIT_PERCENT,
                    &global_settings.bidir_limit, 
-                   &lcd_bidir_scroll, 25, 0, 200 );
+                   &lcd_bidir_scroll, 25, 0, 200, NULL );
 }
 
 #ifdef HAVE_LCD_CHARCELLS
@@ -816,7 +816,7 @@
     int dummy = global_settings.jump_scroll_delay * (HZ/10);
     int rc = set_int(str(LANG_JUMP_SCROLL_DELAY), "ms", UNIT_MS,
                      &dummy, 
-                     &lcd_jump_scroll_delay, 100, 0, 2500 );
+                     &lcd_jump_scroll_delay, 100, 0, 2500, NULL );
     global_settings.jump_scroll_delay = dummy / (HZ/10);
     return rc;
 }
@@ -831,7 +831,7 @@
     return set_int(str(LANG_BATTERY_CAPACITY), "mAh", UNIT_MAH,
                    &global_settings.battery_capacity, 
                    &set_battery_capacity, 50, BATTERY_CAPACITY_MIN,
-                   BATTERY_CAPACITY_MAX );
+                   BATTERY_CAPACITY_MAX, NULL );
 }
 
 #if BATTERY_TYPES_COUNT > 1
@@ -895,7 +895,7 @@
 {
     return set_int(str(LANG_SPINDOWN), "s", UNIT_SEC,
                    &global_settings.disk_spindown,
-                   ata_spindown, 1, 3, 254 );
+                   ata_spindown, 1, 3, 254, NULL );
 }
 
 #ifdef HAVE_ATA_POWER_OFF
@@ -921,14 +921,14 @@
 {
     return set_int(str(LANG_MAX_FILES_IN_DIR), "", UNIT_INT,
                    &global_settings.max_files_in_dir,
-                   NULL, 50, 50, 10000 );
+                   NULL, 50, 50, 10000, NULL );
 }
 
 static bool max_files_in_playlist(void)
 {
     return set_int(str(LANG_MAX_FILES_IN_PLAYLIST), "", UNIT_INT,
                    &global_settings.max_files_in_playlist,
-                   NULL, 1000, 1000, 20000 );
+                   NULL, 1000, 1000, 20000, NULL );
 }
 
 #if CONFIG_HWCODEC == MASNONE
@@ -957,7 +957,7 @@
 {
     return set_int(str(LANG_MP3BUFFER_MARGIN), "s", UNIT_SEC,
                    &global_settings.buffer_margin,
-                   audio_set_buffer_margin, 1, 0, 7 );
+                   audio_set_buffer_margin, 1, 0, 7, NULL );
 }
 #endif
 
@@ -1232,6 +1232,25 @@
     return result;
 }
 
+void replaygain_preamp_format(char* buffer, int buffer_size, int value, 
+    const char* unit)
+{
+    int v = abs(value);
+    
+    snprintf(buffer, buffer_size, "%s%d.%d %s", value < 0 ? "-" : "",
+        v / 10, v % 10, unit);
+}
+
+static bool replaygain_preamp(void)
+{
+    bool result = set_int(str(LANG_REPLAYGAIN_PREAMP), str(LANG_UNIT_DB), 
+        UNIT_DB, &global_settings.replaygain_preamp, NULL, 1, -120, 120, 
+        replaygain_preamp_format);
+
+    dsp_set_replaygain(true);
+    return result;
+}
+
 static bool replaygain_settings_menu(void)
 {
     int m;
@@ -1241,6 +1260,7 @@
         { ID2P(LANG_REPLAYGAIN_ENABLE), replaygain },
         { ID2P(LANG_REPLAYGAIN_NOCLIP), replaygain_noclip },
         { ID2P(LANG_REPLAYGAIN_MODE), replaygain_mode },
+        { ID2P(LANG_REPLAYGAIN_PREAMP), replaygain_preamp },
     };
 
     m=menu_init( items, sizeof(items) / sizeof(*items), NULL,
diff --git a/apps/sound_menu.c b/apps/sound_menu.c
index 1b19593..0c298b0 100644
--- a/apps/sound_menu.c
+++ b/apps/sound_menu.c
@@ -292,7 +292,7 @@
 {
     return set_int(str(LANG_RECORDING_QUALITY), "", UNIT_INT,
                    &global_settings.rec_quality, 
-                   NULL, 1, 0, 7 );
+                   NULL, 1, 0, 7, NULL );
 }
 
 static bool receditable(void)
diff --git a/firmware/export/replaygain.h b/firmware/export/replaygain.h
index e96a7f9..c29d4b6 100644
--- a/firmware/export/replaygain.h
+++ b/firmware/export/replaygain.h
@@ -22,6 +22,7 @@
 
 #include "id3.h"
 
+long get_replaygain_int(long int_gain);
 long get_replaygain(const char* str);
 long get_replaypeak(const char* str);
 long parse_replaygain(const char* key, const char* value, 
diff --git a/firmware/replaygain.c b/firmware/replaygain.c
index 542eee6..a213360 100644
--- a/firmware/replaygain.c
+++ b/firmware/replaygain.c
@@ -305,6 +305,18 @@
     return gain;
 }
 
+long get_replaygain_int(long int_gain)
+{
+    long gain = 0;
+    
+    if (int_gain)
+    {
+        gain = convert_gain(int_gain * FP_ONE / 100);
+    }
+    
+    return gain;
+}
+
 long get_replaygain(const char* str)
 {
     long gain = 0;