Add timestretching from FS#8894, as written by Stephane Doyon based on work by Nicolas Pitre.  Shouldn't affect playback unless it's explicitly enabled, but let me know if it does.  Currently has a dedicated setting, but maybe inclusion of the code will inspire someone to integrate this with the pitch screen...

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18446 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/SOURCES b/apps/SOURCES
index 419d24d..abf373e 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -201,3 +201,4 @@
 #elif CONFIG_KEYPAD == IAUDIO67_PAD
 keymaps/keymap-iaudio67.c
 #endif
+tdspeed.c
diff --git a/apps/dsp.c b/apps/dsp.c
index cbae49a..4c1dc1f 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -32,6 +32,7 @@
 #include "replaygain.h"
 #include "misc.h"
 #include "debug.h"
+#include "tdspeed.h"
 
 /* 16-bit samples are scaled based on these constants. The shift should be
  * no more than 15.
@@ -41,8 +42,14 @@
 
 #define NATIVE_DEPTH        16
 /* If the buffer sizes change, check the assembly code! */
-#define SAMPLE_BUF_COUNT    256
-#define RESAMPLE_BUF_COUNT  (256 * 4)   /* Enough for 11,025 Hz -> 44,100 Hz*/
+#define SMALL_SAMPLE_BUF_COUNT    256
+#define SMALL_RESAMPLE_BUF_COUNT  (256 * 4)   /* Enough for 11,025 Hz -> 44,100 Hz*/
+#define BIG_SAMPLE_BUF_COUNT    4096
+#define BIG_RESAMPLE_BUF_COUNT  (4096 * 4)   /* Enough for 11,025 Hz -> 44,100 Hz*/
+int sample_buf_count;
+int resample_buf_count;
+#define SAMPLE_BUF_COUNT sample_buf_count
+#define RESAMPLE_BUF_COUNT resample_buf_count
 #define DEFAULT_GAIN        0x01000000
 #define SAMPLE_BUF_LEFT_CHANNEL 0
 #define SAMPLE_BUF_RIGHT_CHANNEL (SAMPLE_BUF_COUNT/2)
@@ -163,6 +170,8 @@
     int  sample_depth;
     int  sample_bytes;
     int  stereo_mode;
+    bool tdspeed_active;
+    int tdspeed_factor; /* % */
     int  frac_bits;
 #ifdef HAVE_SW_TONE_CONTROLS
     /* Filter struct for software bass/treble controls */
@@ -226,8 +235,12 @@
  * of copying needed is minimized for that case.
  */
 
-int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR;
-static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR;
+int32_t small_sample_buf[SMALL_SAMPLE_BUF_COUNT] IBSS_ATTR;
+static int32_t small_resample_buf[SMALL_RESAMPLE_BUF_COUNT] IBSS_ATTR;
+int32_t big_sample_buf[BIG_SAMPLE_BUF_COUNT];
+static int32_t big_resample_buf[BIG_RESAMPLE_BUF_COUNT];
+int32_t *sample_buf;
+static int32_t *resample_buf;
 
 #if 0
 /* Clip sample to arbitrary limits where range > 0 and min + range = max */
@@ -264,6 +277,30 @@
                   audio_dsp.codec_frequency);
 }
 
+void tdspeed_setup(struct dsp_config *dspc)
+{
+    if(dspc == &dsp_conf[CODEC_IDX_AUDIO]) {
+#if 0
+        mylog("tdspeed_setup: CODEC_IDX_AUDIO, factor %d, %d, %d\n",
+              dspc->tdspeed_factor, dspc->codec_frequency,
+              dspc->stereo_mode != STEREO_MONO);
+#endif
+        if(dspc->tdspeed_factor == 0 || dspc->tdspeed_factor == 100)
+            dspc->tdspeed_active = false;
+        else dspc->tdspeed_active
+            = tdspeed_init(dspc->codec_frequency == 0 ? NATIVE_FREQUENCY
+                           : dspc->codec_frequency,
+                           dspc->stereo_mode != STEREO_MONO,
+                           dspc->tdspeed_factor);
+    }
+}
+
+void dsp_set_speed(int percent)
+{
+    dsp_conf[CODEC_IDX_AUDIO].tdspeed_factor = percent;
+    tdspeed_setup(&dsp_conf[CODEC_IDX_AUDIO]);
+}
+
 /* Convert count samples to the internal format, if needed.  Updates src
  * to point past the samples "consumed" and dst is set to point to the
  * samples to consume. Note that for mono, dst[0] equals dst[1], as there
@@ -1137,6 +1174,9 @@
 
         dsp->input_samples(samples, src, tmp);
 
+        if(dsp->tdspeed_active)
+            samples = tdspeed_doit(tmp, samples);
+
         if (dsp->apply_gain)
             dsp->apply_gain(samples, &dsp->data, tmp);
 
@@ -1188,6 +1228,19 @@
 /* dsp_input_size MUST be called afterwards */
 int dsp_output_count(struct dsp_config *dsp, int count)
 {
+    if(!dsp->tdspeed_active) {
+        sample_buf = small_sample_buf;
+        resample_buf = small_resample_buf;
+        sample_buf_count = SMALL_SAMPLE_BUF_COUNT;
+        resample_buf_count = SMALL_RESAMPLE_BUF_COUNT;
+    } else {
+        sample_buf = big_sample_buf;
+        resample_buf = big_resample_buf;
+        sample_buf_count = BIG_SAMPLE_BUF_COUNT;
+        resample_buf_count = BIG_RESAMPLE_BUF_COUNT;
+    }
+    if(dsp->tdspeed_active)
+        count = tdspeed_est_output_size(count);
     if (dsp->resample)
     {
         count = (int)(((unsigned long)count * NATIVE_FREQUENCY
@@ -1221,6 +1274,9 @@
                       dsp->data.resample_data.delta) >> 16);
     }
 
+    if(dsp->tdspeed_active)
+        count = tdspeed_est_input_size(count);
+
     return count;
 }
 
@@ -1268,6 +1324,7 @@
             dsp->frequency = dsp->codec_frequency;
 
         resampler_new_delta(dsp);
+        tdspeed_setup(dsp);
         break;
 
     case DSP_SET_SAMPLE_DEPTH:
@@ -1297,6 +1354,7 @@
         dsp->stereo_mode = value;
         dsp->data.num_channels = value == STEREO_MONO ? 1 : 2;
         dsp_update_functions(dsp);
+        tdspeed_setup(dsp);
         break;
 
     case DSP_RESET:
@@ -1321,6 +1379,7 @@
 
         dsp_update_functions(dsp);
         resampler_new_delta(dsp);
+        tdspeed_setup(dsp);
         break;
 
     case DSP_FLUSH:
@@ -1328,6 +1387,7 @@
                sizeof (dsp->data.resample_data));
         resampler_new_delta(dsp);
         dither_init(dsp);
+        tdspeed_setup(dsp);
         break;
 
     case DSP_SET_TRACK_GAIN:
diff --git a/apps/dsp.h b/apps/dsp.h
index 1746a5c..d12c877 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -165,5 +165,6 @@
 int sound_get_pitch(void);
 int dsp_callback(int msg, intptr_t param);
 void dsp_dither_enable(bool enable);
+void dsp_set_speed(int speed);
 
 #endif
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index ea232df..2d1c5f2 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12037,3 +12037,17 @@
     recording: ""
   </voice>
 </phrase>
+<phrase>
+  id: LANG_SPEED
+  desc: time-domain speed compress/stretch
+  user:
+  <source>
+    *: "Speed"
+  </source>
+  <dest>
+    *: "Speed"
+  </dest>
+  <voice>
+    *: "Speed"
+  </voice>
+</phrase>
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index d953db9..8d80e38 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -89,6 +89,8 @@
               
     MENUITEM_SETTING(dithering_enabled,
                      &global_settings.dithering_enabled, lowlatency_callback);
+    MENUITEM_SETTING(sound_speed, &global_settings.sound_speed,
+                     lowlatency_callback);
 #endif
 
 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
@@ -116,7 +118,7 @@
 #endif
           &balance,&channel_config,&stereo_width
 #if CONFIG_CODEC == SWCODEC
-         ,&crossfeed_menu, &equalizer_menu, &dithering_enabled
+          ,&crossfeed_menu, &equalizer_menu, &dithering_enabled, &sound_speed
 #endif
 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
          ,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength
diff --git a/apps/settings.c b/apps/settings.c
index b681471..b13037a 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -913,6 +913,7 @@
     }
 
     dsp_dither_enable(global_settings.dithering_enabled);
+    dsp_set_speed(global_settings.sound_speed);
 #endif
 
 #ifdef HAVE_SPDIF_POWER
diff --git a/apps/settings.h b/apps/settings.h
index 9ef8323..69d476f 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -633,6 +633,7 @@
     int eq_band4_gain;          /* +/- dB */
 
     bool dithering_enabled;
+    int sound_speed;
 #endif
 
 
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 0addf5c..b72be24 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1106,6 +1106,9 @@
     /* dithering */
     OFFON_SETTING(F_SOUNDSETTING, dithering_enabled, LANG_DITHERING, false,
                   "dithering enabled", dsp_dither_enable),
+    INT_SETTING(0, sound_speed, LANG_SPEED, 100, "speed",
+                UNIT_INT, 35, 250, 5,
+                NULL, NULL, dsp_set_speed),
 #endif
 #ifdef HAVE_WM8758
     SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF,
diff --git a/docs/CREDITS b/docs/CREDITS
index 8571aa3..a439689 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -418,6 +418,7 @@
 Volker Mische
 Vitja Makarov
 Francisco Vila
+Nicolas Pitre
 
 The libmad team
 The wavpack team