Re-enable the currently unused and broken dithering and noise shaping code already in Rockbox, and make it a user option instead of a codec-controlled option. The majority of people probably will not even hear any difference with this enabled, but feedback is welcome. Save your settings!


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11368 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/codecs/a52.c b/apps/codecs/a52.c
index 0c69c6a..3d0c35d 100644
--- a/apps/codecs/a52.c
+++ b/apps/codecs/a52.c
@@ -141,7 +141,6 @@
     ci->memset(iedata, 0, iend - iedata);
     #endif
 
-    ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_NONINTERLEAVED);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)28);
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (long *)(1024*128));
diff --git a/apps/codecs/aac.c b/apps/codecs/aac.c
index 0c48422..56c2a79 100644
--- a/apps/codecs/aac.c
+++ b/apps/codecs/aac.c
@@ -73,7 +73,6 @@
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16));
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
 
-    ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(29));
 
diff --git a/apps/codecs/adx.c b/apps/codecs/adx.c
index 99c5f4b..902f3ce 100644
--- a/apps/codecs/adx.c
+++ b/apps/codecs/adx.c
@@ -62,7 +62,6 @@
     /* we only render 16 bits */
     ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)16);
     /*ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));*/
-    ci->configure(DSP_DITHER, (bool *)false);
   
 next_track:
     DEBUGF("ADX: next_track\n");
diff --git a/apps/codecs/aiff.c b/apps/codecs/aiff.c
index 1e7adca..8e5a392 100644
--- a/apps/codecs/aiff.c
+++ b/apps/codecs/aiff.c
@@ -83,7 +83,6 @@
 
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));
-    ci->configure(DSP_DITHER, (bool *)false);
   
 next_track:
     if (codec_init(api)) {
diff --git a/apps/codecs/alac.c b/apps/codecs/alac.c
index 890dcdb..06c8154 100644
--- a/apps/codecs/alac.c
+++ b/apps/codecs/alac.c
@@ -64,7 +64,6 @@
   ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
   ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128));
 
-  ci->configure(DSP_DITHER, (bool *)false);
   ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
   ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(ALAC_OUTPUT_DEPTH-1));
 
diff --git a/apps/codecs/flac.c b/apps/codecs/flac.c
index 880fd69..ce54e67 100644
--- a/apps/codecs/flac.c
+++ b/apps/codecs/flac.c
@@ -264,7 +264,6 @@
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128));
 
-    ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_NONINTERLEAVED);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(FLAC_OUTPUT_DEPTH-1));
 
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c
index 81604de..ff6090e 100644
--- a/apps/codecs/mpa.c
+++ b/apps/codecs/mpa.c
@@ -95,7 +95,6 @@
 
     /* Create a decoder instance */
 
-    ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(MAD_F_FRACBITS));
     ci->configure(DSP_SET_CLIP_MIN, (int *)-MAD_F_ONE);
     ci->configure(DSP_SET_CLIP_MAX, (int *)(MAD_F_ONE - 1));
diff --git a/apps/codecs/mpc.c b/apps/codecs/mpc.c
index 563b313..f9622f8 100644
--- a/apps/codecs/mpc.c
+++ b/apps/codecs/mpc.c
@@ -90,7 +90,6 @@
     ci->memset(iedata, 0, iend - iedata);
     #endif
     
-    ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)(28));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (long *)(1024*16));
     ci->configure(CODEC_SET_FILEBUF_PRESEEK, (long *)(0));
diff --git a/apps/codecs/shorten.c b/apps/codecs/shorten.c
index 8d62a12..c571df8 100644
--- a/apps/codecs/shorten.c
+++ b/apps/codecs/shorten.c
@@ -63,7 +63,6 @@
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128));
 
-    ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_NONINTERLEAVED);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(SHN_OUTPUT_DEPTH-1));
     
diff --git a/apps/codecs/sid.c b/apps/codecs/sid.c
index bdffb87..c95e44b 100644
--- a/apps/codecs/sid.c
+++ b/apps/codecs/sid.c
@@ -1239,7 +1239,6 @@
 
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));
-    ci->configure(DSP_DITHER, (bool *)false);
 
 next_track:
     if (codec_init(api)) {
diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c
index 1900364..0475572 100644
--- a/apps/codecs/vorbis.c
+++ b/apps/codecs/vorbis.c
@@ -129,7 +129,6 @@
     rb->memset(iedata, 0, iend - iedata);
     #endif
 
-    rb->configure(DSP_DITHER, (bool *)false);
     rb->configure(DSP_SET_SAMPLE_DEPTH, (long *)24);
     rb->configure(DSP_SET_CLIP_MAX, (long *)((1 << 24) - 1));
     rb->configure(DSP_SET_CLIP_MIN, (long *)-((1 << 24) - 1));
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c
index 3c86e3f..ba99b94 100644
--- a/apps/codecs/wav.c
+++ b/apps/codecs/wav.c
@@ -246,7 +246,6 @@
     ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)28);
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));
-    ci->configure(DSP_DITHER, (bool *)false);
   
 next_track:
     if (codec_init(api)) {
diff --git a/apps/codecs/wavpack.c b/apps/codecs/wavpack.c
index 1871b46..de815e6 100644
--- a/apps/codecs/wavpack.c
+++ b/apps/codecs/wavpack.c
@@ -62,7 +62,6 @@
     ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
     ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128));
   
-    ci->configure(DSP_DITHER, (bool *)false);
     ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(28));
 
     next_track:
diff --git a/apps/dsp.c b/apps/dsp.c
index 4ea2a0c..94e825c 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -32,10 +32,6 @@
 #include <dsp_asm.h>
 #endif
 
-/* 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
  * no more than 15.
  */
@@ -180,6 +176,8 @@
     int stereo_mode;
     int frac_bits;
     bool dither_enabled;
+    long dither_bias;
+    long dither_mask;
     bool new_gain;
     bool crossfeed_enabled;
     bool eq_enabled;
@@ -397,7 +395,7 @@
     int i = 0, j;
     int pos;
     int num_channels = dsp->stereo_mode == STEREO_MONO ? 1 : 2;
-    
+      
     while ((pos = phase >> 16) == 0)
     {
        for (j = 0; j < num_channels; j++)
@@ -479,40 +477,60 @@
  * taken from the coolplayer project - coolplayer.sourceforge.net
  */
 
-static long dither_sample(int32_t sample, int32_t bias, int32_t mask,
-    struct dither_data* dither)
+void dsp_dither_enable(bool enable)
 {
-    int32_t output;
+    dsp->dither_enabled = enable;
+}
+
+static void dither_init(void)
+{
+    memset(&dither_data[0], 0, sizeof(dither_data));
+    memset(&dither_data[1], 0, sizeof(dither_data));
+    dsp->dither_bias = (1L << (dsp->frac_bits - NATIVE_DEPTH));
+    dsp->dither_mask = (1L << (dsp->frac_bits + 1 - NATIVE_DEPTH)) - 1;
+}
+
+static void dither_samples(int32_t* src, int num, struct dither_data* dither)
+{
+    int32_t output, sample;
     int32_t random;
-    int32_t min;
-    int32_t max;
+    int32_t min, max;
+    long mask = dsp->dither_mask;
+    long bias = dsp->dither_bias;
+    int i;
+    
+    for (i = 0; i < num; ++i) {
+        /* Noise shape and bias */
+        sample = src[i];
+        sample += dither->error[0] - dither->error[1] + dither->error[2];
+        dither->error[2] = dither->error[1];
+        dither->error[1] = dither->error[0]/2;
 
-    /* Noise shape and bias */
+        output = sample + bias;
 
-    sample += dither->error[0] - dither->error[1] + dither->error[2];
-    dither->error[2] = dither->error[1];
-    dither->error[1] = dither->error[0] / 2;
+        /* Dither */
+        random = dither->random*0x0019660dL + 0x3c6ef35fL;
+        output += (random & mask) - (dither->random & mask);
+        dither->random = random;
 
-    output = sample + bias;
+        /* Clip and quantize */
+        min = dsp->clip_min;
+        max = dsp->clip_max;
+        if (output > max) {
+            output = max;
+            if (sample > max)
+                sample = max;
+        } else if (output < min) {
+            output = min;
+            if (sample < min)
+                sample = min;
+        }
+        output &= ~mask;
 
-    /* Dither */
-
-    random = dither->random * 0x0019660dL + 0x3c6ef35fL;
-    sample += (random & mask) - (dither->random & mask);
-    dither->random = random;
-
-    /* Clip and quantize */
-
-    min = dsp->clip_min;
-    max = dsp->clip_max;
-    sample = clip_sample(sample, min, max);
-    output = clip_sample(output, min, max) & ~mask;
-
-    /* Error feedback */
-
-    dither->error[0] = sample - output;
-
-    return output;
+        /* Error feedback */
+        dither->error[0] = sample - output;
+        src[i] = output;
+    }
 }
 
 void dsp_set_crossfeed(bool enable)
@@ -740,7 +758,7 @@
 
 void channels_set(int value)
 {
-    channels_mode =  value;    
+    channels_mode = value;
 }
 
 void stereo_width_set(int value)
@@ -811,15 +829,13 @@
 
     if (dsp->dither_enabled)
     {
-        long bias = (1L << (dsp->frac_bits - NATIVE_DEPTH));
-        long mask = (1L << scale) - 1;
+        dither_samples(src[0], count, &dither_data[0]);
+        dither_samples(src[1], count, &dither_data[1]);
 
         while (count-- > 0)
         {
-            *dst++ = (short) (dither_sample(*s0++, bias, mask, &dither_data[0])
-                >> scale);
-            *dst++ = (short) (dither_sample(*s1++, bias, mask, &dither_data[1])
-                >> scale);
+            *dst++ = (short) (*s0++ >> scale);
+            *dst++ = (short) (*s1++ >> scale);
         }
     }
     else
@@ -980,7 +996,7 @@
         /* Fall through!!! */
     case DSP_SWITCH_FREQUENCY:
         dsp->codec_frequency = ((long) value == 0) ? NATIVE_FREQUENCY : (long) value;
-        /* Account for playback speed adjustment when settingg dsp->frequency
+        /* Account for playback speed adjustment when setting dsp->frequency
            if we're called from the main audio thread. Voice UI thread should
            not need this feature.
          */
@@ -1001,7 +1017,7 @@
 
     case DSP_SET_SAMPLE_DEPTH:
         dsp->sample_depth = (long) value;
-
+ 
         if (dsp->sample_depth <= NATIVE_DEPTH)
         {
             dsp->frac_bits = WORD_FRACBITS;
@@ -1017,6 +1033,7 @@
             dsp->clip_min = -(1 << (long)value);
         }
 
+        dither_init(); 
         break;
 
     case DSP_SET_STEREO_MODE:
@@ -1024,7 +1041,6 @@
         break;
 
     case DSP_RESET:
-        dsp->dither_enabled = false;
         dsp->stereo_mode = STEREO_NONINTERLEAVED;
         dsp->clip_max =  ((1 << WORD_FRACBITS) - 1);
         dsp->clip_min = -((1 << WORD_FRACBITS));
@@ -1038,11 +1054,6 @@
         dsp->new_gain = true;
         break;
 
-    case DSP_DITHER:
-        memset(dither_data, 0, sizeof(dither_data));
-        dsp->dither_enabled = (bool) value;
-        break;
-
     case DSP_SET_TRACK_GAIN:
         dsp->track_gain = (long) value;
         dsp->new_gain = true;
diff --git a/apps/dsp.h b/apps/dsp.h
index 4221d9e..965eb28 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -39,7 +39,6 @@
     DSP_SET_SAMPLE_DEPTH,
     DSP_SET_STEREO_MODE,
     DSP_RESET,
-    DSP_DITHER,
     DSP_SET_TRACK_GAIN,
     DSP_SET_ALBUM_GAIN,
     DSP_SET_TRACK_PEAK,
@@ -63,5 +62,6 @@
 int sound_get_pitch(void);
 void channels_set(int value);
 void stereo_width_set(int value);
+void dsp_dither_enable(bool enable);
 
 #endif
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 3305bd2..99cee5a 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -9974,3 +9974,17 @@
     *: ""
   </voice>
 </phrase>
+<phrase>
+  id: LANG_DITHERING
+  desc: in the sound settings menu
+  user:
+  <source>
+    *: "Dithering"
+  </source>
+  <dest>
+    *: "Dithering"
+  </dest>
+  <voice>
+    *: "Dithering"
+  </voice>
+</phrase>
diff --git a/apps/settings.c b/apps/settings.c
index 9d618eb..da5a461 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -96,7 +96,7 @@
 #include "eq_menu.h"
 #endif
 
-#define CONFIG_BLOCK_VERSION 54
+#define CONFIG_BLOCK_VERSION 55
 #define CONFIG_BLOCK_SIZE 512
 #define RTC_BLOCK_SIZE 44
 
@@ -591,6 +591,9 @@
     {9|SIGNED, S_O(eq_band2_gain), 0, "eq band 2 gain", NULL },
     {9|SIGNED, S_O(eq_band3_gain), 0, "eq band 3 gain", NULL },
     {9|SIGNED, S_O(eq_band4_gain), 0, "eq band 4 gain", NULL },
+
+    /* dithering */
+    {1, S_O(dithering_enabled), false, "dithering enabled", off_on },
 #endif
 
 #ifdef HAVE_DIRCACHE
@@ -1274,6 +1277,8 @@
     for(i = 0; i < 5; i++) {
         dsp_set_eq_coefs(i);
     }
+
+    dsp_dither_enable(global_settings.dithering_enabled);
 #endif
 
 #ifdef HAVE_WM8758
diff --git a/apps/settings.h b/apps/settings.h
index 0dca114..435d8e9 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -435,6 +435,8 @@
     int eq_band4_cutoff;        /* Hz */
     int eq_band4_q;
     int eq_band4_gain;          /* +/- dB */
+
+    bool dithering_enabled;
 #endif
 
 #ifdef HAVE_LCD_COLOR
diff --git a/apps/sound_menu.c b/apps/sound_menu.c
index a6e6a1c..c10ba94 100644
--- a/apps/sound_menu.c
+++ b/apps/sound_menu.c
@@ -219,6 +219,15 @@
 
     return result;
 }
+
+static bool dithering_enable(void)
+{
+    return set_bool_options(str(LANG_DITHERING),
+                            &global_settings.dithering_enabled, 
+                            STR(LANG_SET_BOOL_YES), 
+                            STR(LANG_SET_BOOL_NO), 
+                            dsp_dither_enable);
+}
 #endif
 
 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
@@ -635,6 +644,7 @@
 #if CONFIG_CODEC == SWCODEC
         { ID2P(LANG_CROSSFEED), crossfeed_menu },
         { ID2P(LANG_EQUALIZER), eq_menu },
+        { ID2P(LANG_DITHERING), dithering_enable },
 #endif
 #ifdef HAVE_WM8758
         { ID2P(LANG_EQUALIZER_HARDWARE), eq_hw_menu },