summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/action.h2
-rw-r--r--apps/dsp.c300
-rw-r--r--apps/dsp.h4
-rw-r--r--apps/dsp_arm.S4
-rw-r--r--apps/dsp_asm.h8
-rw-r--r--apps/dsp_cf.S6
-rw-r--r--apps/gui/pitchscreen.c328
-rw-r--r--apps/keymaps/keymap-av300.c2
-rw-r--r--apps/keymaps/keymap-c100.c2
-rw-r--r--apps/keymaps/keymap-c200.c10
-rw-r--r--apps/keymaps/keymap-clip.c26
-rw-r--r--apps/keymaps/keymap-e200.c2
-rw-r--r--apps/keymaps/keymap-fuze.c2
-rw-r--r--apps/keymaps/keymap-gigabeat-s.c4
-rw-r--r--apps/keymaps/keymap-gigabeat.c2
-rw-r--r--apps/keymaps/keymap-h10.c4
-rw-r--r--apps/keymaps/keymap-h1x0_h3x0.c8
-rw-r--r--apps/keymaps/keymap-hdd1630.c6
-rw-r--r--apps/keymaps/keymap-iaudio67.c4
-rw-r--r--apps/keymaps/keymap-ifp7xx.c2
-rw-r--r--apps/keymaps/keymap-ipod.c2
-rw-r--r--apps/keymaps/keymap-logikdax.c3
-rw-r--r--apps/keymaps/keymap-m200.c2
-rw-r--r--apps/keymaps/keymap-m3.c4
-rw-r--r--apps/keymaps/keymap-meizu-m6sl.c2
-rw-r--r--apps/keymaps/keymap-mr100.c2
-rw-r--r--apps/keymaps/keymap-sa9200.c2
-rw-r--r--apps/keymaps/keymap-touchscreen.c2
-rw-r--r--apps/keymaps/keymap-x5.c4
-rw-r--r--apps/lang/english.lang34
-rw-r--r--apps/menus/sound_menu.c27
-rw-r--r--apps/settings.c1
-rw-r--r--apps/settings.h1
-rw-r--r--apps/settings_list.c4
-rw-r--r--apps/tdspeed.c319
-rw-r--r--apps/tdspeed.h36
-rw-r--r--docs/CREDITS2
-rw-r--r--manual/configure_rockbox/sound_settings.tex10
-rw-r--r--manual/rockbox_interface/wps.tex104
40 files changed, 1027 insertions, 261 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 0fe001b242..527b0b20a9 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -126,6 +126,7 @@ pcmbuf.c
126playback.c 126playback.c
127codecs.c 127codecs.c
128dsp.c 128dsp.c
129tdspeed.c
129#ifdef HAVE_RECORDING 130#ifdef HAVE_RECORDING
130enc_config.c 131enc_config.c
131#ifndef SIMULATOR 132#ifndef SIMULATOR
diff --git a/apps/action.h b/apps/action.h
index 61c214c609..3e53b6d7a8 100644
--- a/apps/action.h
+++ b/apps/action.h
@@ -211,6 +211,8 @@ enum {
211 ACTION_PS_TOGGLE_MODE, 211 ACTION_PS_TOGGLE_MODE,
212 ACTION_PS_RESET, 212 ACTION_PS_RESET,
213 ACTION_PS_EXIT, /* _STD_* isnt going to work here */ 213 ACTION_PS_EXIT, /* _STD_* isnt going to work here */
214 ACTION_PS_SLOWER,
215 ACTION_PS_FASTER,
214 216
215 /* yesno screen */ 217 /* yesno screen */
216 ACTION_YESNO_ACCEPT, 218 ACTION_YESNO_ACCEPT,
diff --git a/apps/dsp.c b/apps/dsp.c
index cbae49ab69..b32b641693 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -32,22 +32,19 @@
32#include "replaygain.h" 32#include "replaygain.h"
33#include "misc.h" 33#include "misc.h"
34#include "debug.h" 34#include "debug.h"
35#include "tdspeed.h"
36#include "buffer.h"
35 37
36/* 16-bit samples are scaled based on these constants. The shift should be 38/* 16-bit samples are scaled based on these constants. The shift should be
37 * no more than 15. 39 * no more than 15.
38 */ 40 */
39#define WORD_SHIFT 12 41#define WORD_SHIFT 12
40#define WORD_FRACBITS 27 42#define WORD_FRACBITS 27
41 43
42#define NATIVE_DEPTH 16 44#define NATIVE_DEPTH 16
43/* If the buffer sizes change, check the assembly code! */ 45/* If the small buffer size changes, check the assembly code! */
44#define SAMPLE_BUF_COUNT 256 46#define SMALL_SAMPLE_BUF_COUNT 256
45#define RESAMPLE_BUF_COUNT (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/ 47#define DEFAULT_GAIN 0x01000000
46#define DEFAULT_GAIN 0x01000000
47#define SAMPLE_BUF_LEFT_CHANNEL 0
48#define SAMPLE_BUF_RIGHT_CHANNEL (SAMPLE_BUF_COUNT/2)
49#define RESAMPLE_BUF_LEFT_CHANNEL 0
50#define RESAMPLE_BUF_RIGHT_CHANNEL (RESAMPLE_BUF_COUNT/2)
51 48
52/* enums to index conversion properly with stereo mode and other settings */ 49/* enums to index conversion properly with stereo mode and other settings */
53enum 50enum
@@ -101,7 +98,7 @@ struct dsp_data
101 struct resample_data resample_data; /* 08h */ 98 struct resample_data resample_data; /* 08h */
102 int32_t clip_min; /* 18h */ 99 int32_t clip_min; /* 18h */
103 int32_t clip_max; /* 1ch */ 100 int32_t clip_max; /* 1ch */
104 int32_t gain; /* 20h - Note that this is in S8.23 format. */ 101 int32_t gain; /* 20h - Note that this is in S8.23 format. */
105 /* 24h */ 102 /* 24h */
106}; 103};
107 104
@@ -140,11 +137,12 @@ struct eq_state
140 137
141/* Typedefs keep things much neater in this case */ 138/* Typedefs keep things much neater in this case */
142typedef void (*sample_input_fn_type)(int count, const char *src[], 139typedef void (*sample_input_fn_type)(int count, const char *src[],
143 int32_t *dst[]); 140 int32_t *dst[]);
144typedef int (*resample_fn_type)(int count, struct dsp_data *data, 141typedef int (*resample_fn_type)(int count, struct dsp_data *data,
145 int32_t *src[], int32_t *dst[]); 142 const int32_t *src[], int32_t *dst[]);
146typedef void (*sample_output_fn_type)(int count, struct dsp_data *data, 143typedef void (*sample_output_fn_type)(int count, struct dsp_data *data,
147 int32_t *src[], int16_t *dst); 144 const int32_t *src[], int16_t *dst);
145
148/* Single-DSP channel processing in place */ 146/* Single-DSP channel processing in place */
149typedef void (*channels_process_fn_type)(int count, int32_t *buf[]); 147typedef void (*channels_process_fn_type)(int count, int32_t *buf[]);
150/* DSP local channel processing in place */ 148/* DSP local channel processing in place */
@@ -163,6 +161,9 @@ struct dsp_config
163 int sample_depth; 161 int sample_depth;
164 int sample_bytes; 162 int sample_bytes;
165 int stereo_mode; 163 int stereo_mode;
164 bool tdspeed_enabled; /* User has enabled timestretch */
165 int tdspeed_percent; /* Speed % */
166 bool tdspeed_active; /* Timestretch is in use */
166 int frac_bits; 167 int frac_bits;
167#ifdef HAVE_SW_TONE_CONTROLS 168#ifdef HAVE_SW_TONE_CONTROLS
168 /* Filter struct for software bass/treble controls */ 169 /* Filter struct for software bass/treble controls */
@@ -218,16 +219,31 @@ static long album_peak;
218static long replaygain; 219static long replaygain;
219static bool crossfeed_enabled; 220static bool crossfeed_enabled;
220 221
221#define audio_dsp (dsp_conf[CODEC_IDX_AUDIO]) 222#define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO])
222#define voice_dsp (dsp_conf[CODEC_IDX_VOICE]) 223#define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE])
223 224
224/* The internal format is 32-bit samples, non-interleaved, stereo. This 225/* The internal format is 32-bit samples, non-interleaved, stereo. This
225 * format is similar to the raw output from several codecs, so the amount 226 * format is similar to the raw output from several codecs, so the amount
226 * of copying needed is minimized for that case. 227 * of copying needed is minimized for that case.
227 */ 228 */
228 229
229int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR; 230#define RESAMPLE_RATIO 4 /* Enough for 11,025 Hz -> 44,100 Hz */
230static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR; 231
232static int32_t small_sample_buf[SMALL_SAMPLE_BUF_COUNT] IBSS_ATTR;
233static int32_t small_resample_buf[SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO] IBSS_ATTR;
234
235static int32_t *big_sample_buf = NULL;
236static int32_t *big_resample_buf = NULL;
237static int big_sample_buf_count = -1; /* -1=unknown, 0=not available */
238
239static int sample_buf_count;
240static int32_t *sample_buf;
241static int32_t *resample_buf;
242
243#define SAMPLE_BUF_LEFT_CHANNEL 0
244#define SAMPLE_BUF_RIGHT_CHANNEL (sample_buf_count/2)
245#define RESAMPLE_BUF_LEFT_CHANNEL 0
246#define RESAMPLE_BUF_RIGHT_CHANNEL (sample_buf_count/2 * RESAMPLE_RATIO)
231 247
232#if 0 248#if 0
233/* Clip sample to arbitrary limits where range > 0 and min + range = max */ 249/* Clip sample to arbitrary limits where range > 0 and min + range = max */
@@ -260,8 +276,66 @@ int sound_get_pitch(void)
260void sound_set_pitch(int permille) 276void sound_set_pitch(int permille)
261{ 277{
262 pitch_ratio = permille; 278 pitch_ratio = permille;
263 dsp_configure(&audio_dsp, DSP_SWITCH_FREQUENCY, 279 dsp_configure(&AUDIO_DSP, DSP_SWITCH_FREQUENCY,
264 audio_dsp.codec_frequency); 280 AUDIO_DSP.codec_frequency);
281}
282
283void tdspeed_setup(struct dsp_config *dspc)
284{
285 if (dspc == &AUDIO_DSP)
286 {
287 dspc->tdspeed_active = false;
288 if (!dspc->tdspeed_enabled)
289 return;
290 if (dspc->tdspeed_percent == 0)
291 dspc->tdspeed_percent = 100;
292 if (!tdspeed_init(
293 dspc->codec_frequency == 0 ? NATIVE_FREQUENCY : dspc->codec_frequency,
294 dspc->stereo_mode != STEREO_MONO,
295 dspc->tdspeed_percent))
296 return;
297 if (dspc->tdspeed_percent == 100 || big_sample_buf_count <= 0)
298 return;
299 dspc->tdspeed_active = true;
300 }
301}
302
303void dsp_timestretch_enable(bool enable)
304{
305 if (enable)
306 {
307 /* Set up timestretch buffers on first enable */
308 if (big_sample_buf_count < 0)
309 {
310 big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO;
311 big_sample_buf = small_resample_buf;
312 big_resample_buf = (int32_t *) buffer_alloc(big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t));
313 }
314 }
315 else
316 {
317 /* If not enabled at startup, buffers will never be available */
318 if (big_sample_buf_count < 0)
319 big_sample_buf_count = 0;
320 }
321 AUDIO_DSP.tdspeed_enabled = enable;
322 tdspeed_setup(&AUDIO_DSP);
323}
324
325void dsp_set_timestretch(int percent)
326{
327 AUDIO_DSP.tdspeed_percent = percent;
328 tdspeed_setup(&AUDIO_DSP);
329}
330
331int dsp_get_timestretch()
332{
333 return AUDIO_DSP.tdspeed_percent;
334}
335
336bool dsp_timestretch_enabled()
337{
338 return (AUDIO_DSP.tdspeed_enabled && big_sample_buf_count > 0);
265} 339}
266 340
267/* Convert count samples to the internal format, if needed. Updates src 341/* Convert count samples to the internal format, if needed. Updates src
@@ -403,10 +477,11 @@ static void sample_input_new_format(struct dsp_config *dsp)
403 dsp->input_samples = sample_input_functions[convert]; 477 dsp->input_samples = sample_input_functions[convert];
404} 478}
405 479
480
406#ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO 481#ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
407/* write mono internal format to output format */ 482/* write mono internal format to output format */
408static void sample_output_mono(int count, struct dsp_data *data, 483static void sample_output_mono(int count, struct dsp_data *data,
409 int32_t *src[], int16_t *dst) 484 const int32_t *src[], int16_t *dst)
410{ 485{
411 const int32_t *s0 = src[0]; 486 const int32_t *s0 = src[0];
412 const int scale = data->output_scale; 487 const int scale = data->output_scale;
@@ -425,7 +500,7 @@ static void sample_output_mono(int count, struct dsp_data *data,
425/* write stereo internal format to output format */ 500/* write stereo internal format to output format */
426#ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO 501#ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
427static void sample_output_stereo(int count, struct dsp_data *data, 502static void sample_output_stereo(int count, struct dsp_data *data,
428 int32_t *src[], int16_t *dst) 503 const int32_t *src[], int16_t *dst)
429{ 504{
430 const int32_t *s0 = src[0]; 505 const int32_t *s0 = src[0];
431 const int32_t *s1 = src[1]; 506 const int32_t *s1 = src[1];
@@ -448,7 +523,7 @@ static void sample_output_stereo(int count, struct dsp_data *data,
448 * This function handles mono and stereo outputs. 523 * This function handles mono and stereo outputs.
449 */ 524 */
450static void sample_output_dithered(int count, struct dsp_data *data, 525static void sample_output_dithered(int count, struct dsp_data *data,
451 int32_t *src[], int16_t *dst) 526 const int32_t *src[], int16_t *dst)
452{ 527{
453 const int32_t mask = dither_mask; 528 const int32_t mask = dither_mask;
454 const int32_t bias = dither_bias; 529 const int32_t bias = dither_bias;
@@ -462,7 +537,7 @@ static void sample_output_dithered(int count, struct dsp_data *data,
462 for (ch = 0; ch < data->num_channels; ch++) 537 for (ch = 0; ch < data->num_channels; ch++)
463 { 538 {
464 struct dither_data * const dither = &dither_data[ch]; 539 struct dither_data * const dither = &dither_data[ch];
465 int32_t *s = src[ch]; 540 const int32_t *s = src[ch];
466 int i; 541 int i;
467 542
468 for (i = 0, d = &dst[ch]; i < count; i++, s++, d += 2) 543 for (i = 0, d = &dst[ch]; i < count; i++, s++, d += 2)
@@ -540,7 +615,7 @@ static void sample_output_new_format(struct dsp_config *dsp)
540 615
541 int out = dsp->data.num_channels - 1; 616 int out = dsp->data.num_channels - 1;
542 617
543 if (dsp == &audio_dsp && dither_enabled) 618 if (dsp == &AUDIO_DSP && dither_enabled)
544 out += 2; 619 out += 2;
545 620
546 dsp->output_samples = sample_output_functions[out]; 621 dsp->output_samples = sample_output_functions[out];
@@ -552,7 +627,7 @@ static void sample_output_new_format(struct dsp_config *dsp)
552 */ 627 */
553#ifndef DSP_HAVE_ASM_RESAMPLING 628#ifndef DSP_HAVE_ASM_RESAMPLING
554static int dsp_downsample(int count, struct dsp_data *data, 629static int dsp_downsample(int count, struct dsp_data *data,
555 int32_t *src[], int32_t *dst[]) 630 const int32_t *src[], int32_t *dst[])
556{ 631{
557 int ch = data->num_channels - 1; 632 int ch = data->num_channels - 1;
558 uint32_t delta = data->resample_data.delta; 633 uint32_t delta = data->resample_data.delta;
@@ -565,9 +640,9 @@ static int dsp_downsample(int count, struct dsp_data *data,
565 /* Just initialize things and not worry too much about the relatively 640 /* Just initialize things and not worry too much about the relatively
566 * uncommon case of not being able to spit out a sample for the frame. 641 * uncommon case of not being able to spit out a sample for the frame.
567 */ 642 */
568 int32_t *s = src[ch]; 643 const int32_t *s = src[ch];
569 int32_t last = data->resample_data.last_sample[ch]; 644 int32_t last = data->resample_data.last_sample[ch];
570 645
571 data->resample_data.last_sample[ch] = s[count - 1]; 646 data->resample_data.last_sample[ch] = s[count - 1];
572 d = dst[ch]; 647 d = dst[ch];
573 phase = data->resample_data.phase; 648 phase = data->resample_data.phase;
@@ -593,7 +668,7 @@ static int dsp_downsample(int count, struct dsp_data *data,
593} 668}
594 669
595static int dsp_upsample(int count, struct dsp_data *data, 670static int dsp_upsample(int count, struct dsp_data *data,
596 int32_t *src[], int32_t *dst[]) 671 const int32_t *src[], int32_t *dst[])
597{ 672{
598 int ch = data->num_channels - 1; 673 int ch = data->num_channels - 1;
599 uint32_t delta = data->resample_data.delta; 674 uint32_t delta = data->resample_data.delta;
@@ -603,11 +678,10 @@ static int dsp_upsample(int count, struct dsp_data *data,
603 /* Rolled channel loop actually showed slightly faster. */ 678 /* Rolled channel loop actually showed slightly faster. */
604 do 679 do
605 { 680 {
606 /* Should always be able to output a sample for a ratio up to 681 /* Should always be able to output a sample for a ratio up to RESAMPLE_RATIO */
607 RESAMPLE_BUF_COUNT / SAMPLE_BUF_COUNT. */ 682 const int32_t *s = src[ch];
608 int32_t *s = src[ch];
609 int32_t last = data->resample_data.last_sample[ch]; 683 int32_t last = data->resample_data.last_sample[ch];
610 684
611 data->resample_data.last_sample[ch] = s[count - 1]; 685 data->resample_data.last_sample[ch] = s[count - 1];
612 d = dst[ch]; 686 d = dst[ch];
613 phase = data->resample_data.phase; 687 phase = data->resample_data.phase;
@@ -638,7 +712,7 @@ static int dsp_upsample(int count, struct dsp_data *data,
638 712
639static void resampler_new_delta(struct dsp_config *dsp) 713static void resampler_new_delta(struct dsp_config *dsp)
640{ 714{
641 dsp->data.resample_data.delta = (unsigned long) 715 dsp->data.resample_data.delta = (unsigned long)
642 dsp->frequency * 65536LL / NATIVE_FREQUENCY; 716 dsp->frequency * 65536LL / NATIVE_FREQUENCY;
643 717
644 if (dsp->frequency == NATIVE_FREQUENCY) 718 if (dsp->frequency == NATIVE_FREQUENCY)
@@ -669,7 +743,7 @@ static inline int resample(struct dsp_config *dsp, int count, int32_t *src[])
669 &resample_buf[RESAMPLE_BUF_RIGHT_CHANNEL], 743 &resample_buf[RESAMPLE_BUF_RIGHT_CHANNEL],
670 }; 744 };
671 745
672 count = dsp->resample(count, &dsp->data, src, dst); 746 count = dsp->resample(count, &dsp->data, (const int32_t **)src, dst);
673 747
674 src[0] = dst[0]; 748 src[0] = dst[0];
675 src[1] = dst[dsp->data.num_channels - 1]; 749 src[1] = dst[dsp->data.num_channels - 1];
@@ -686,7 +760,7 @@ static void dither_init(struct dsp_config *dsp)
686 760
687void dsp_dither_enable(bool enable) 761void dsp_dither_enable(bool enable)
688{ 762{
689 struct dsp_config *dsp = &audio_dsp; 763 struct dsp_config *dsp = &AUDIO_DSP;
690 dither_enabled = enable; 764 dither_enabled = enable;
691 sample_output_new_format(dsp); 765 sample_output_new_format(dsp);
692} 766}
@@ -705,7 +779,7 @@ static void apply_crossfeed(int count, int32_t *buf[])
705 int32_t *coefs = &crossfeed_data.coefs[0]; 779 int32_t *coefs = &crossfeed_data.coefs[0];
706 int32_t gain = crossfeed_data.gain; 780 int32_t gain = crossfeed_data.gain;
707 int32_t *di = crossfeed_data.index; 781 int32_t *di = crossfeed_data.index;
708 782
709 int32_t acc; 783 int32_t acc;
710 int32_t left, right; 784 int32_t left, right;
711 int i; 785 int i;
@@ -734,7 +808,7 @@ static void apply_crossfeed(int count, int32_t *buf[])
734 /* Now add the attenuated direct sound and write to outputs */ 808 /* Now add the attenuated direct sound and write to outputs */
735 buf[0][i] = FRACMUL(left, gain) + hist_r[1]; 809 buf[0][i] = FRACMUL(left, gain) + hist_r[1];
736 buf[1][i] = FRACMUL(right, gain) + hist_l[1]; 810 buf[1][i] = FRACMUL(right, gain) + hist_l[1];
737 811
738 /* Wrap delay line index if bigger than delay line size */ 812 /* Wrap delay line index if bigger than delay line size */
739 if (di >= delay + 13*2) 813 if (di >= delay + 13*2)
740 di = delay; 814 di = delay;
@@ -754,7 +828,7 @@ static void apply_crossfeed(int count, int32_t *buf[])
754void dsp_set_crossfeed(bool enable) 828void dsp_set_crossfeed(bool enable)
755{ 829{
756 crossfeed_enabled = enable; 830 crossfeed_enabled = enable;
757 audio_dsp.apply_crossfeed = (enable && audio_dsp.data.num_channels > 1) 831 AUDIO_DSP.apply_crossfeed = (enable && AUDIO_DSP.data.num_channels > 1)
758 ? apply_crossfeed : NULL; 832 ? apply_crossfeed : NULL;
759} 833}
760 834
@@ -815,17 +889,17 @@ static void set_gain(struct dsp_config *dsp)
815 dsp->data.gain = DEFAULT_GAIN; 889 dsp->data.gain = DEFAULT_GAIN;
816 890
817 /* Replay gain not relevant to voice */ 891 /* Replay gain not relevant to voice */
818 if (dsp == &audio_dsp && replaygain) 892 if (dsp == &AUDIO_DSP && replaygain)
819 { 893 {
820 dsp->data.gain = replaygain; 894 dsp->data.gain = replaygain;
821 } 895 }
822 896
823 if (dsp->eq_process && eq_precut) 897 if (dsp->eq_process && eq_precut)
824 { 898 {
825 dsp->data.gain = 899 dsp->data.gain =
826 (long) (((int64_t) dsp->data.gain * eq_precut) >> 24); 900 (long) (((int64_t) dsp->data.gain * eq_precut) >> 24);
827 } 901 }
828 902
829 if (dsp->data.gain == DEFAULT_GAIN) 903 if (dsp->data.gain == DEFAULT_GAIN)
830 { 904 {
831 dsp->data.gain = 0; 905 dsp->data.gain = 0;
@@ -846,7 +920,7 @@ static void set_gain(struct dsp_config *dsp)
846void dsp_set_eq_precut(int precut) 920void dsp_set_eq_precut(int precut)
847{ 921{
848 eq_precut = get_replaygain_int(precut * -10); 922 eq_precut = get_replaygain_int(precut * -10);
849 set_gain(&audio_dsp); 923 set_gain(&AUDIO_DSP);
850} 924}
851 925
852/** 926/**
@@ -867,10 +941,10 @@ void dsp_set_eq_coefs(int band)
867 cutoff = 0xffffffff / NATIVE_FREQUENCY * (*setting++); 941 cutoff = 0xffffffff / NATIVE_FREQUENCY * (*setting++);
868 q = *setting++; 942 q = *setting++;
869 gain = *setting++; 943 gain = *setting++;
870 944
871 if (q == 0) 945 if (q == 0)
872 q = 1; 946 q = 1;
873 947
874 /* NOTE: The coef functions assume the EMAC unit is in fractional mode, 948 /* NOTE: The coef functions assume the EMAC unit is in fractional mode,
875 which it should be, since we're executed from the main thread. */ 949 which it should be, since we're executed from the main thread. */
876 950
@@ -903,7 +977,7 @@ static void eq_process(int count, int32_t *buf[])
903 EQ_PEAK_SHIFT, /* peaking */ 977 EQ_PEAK_SHIFT, /* peaking */
904 EQ_SHELF_SHIFT, /* high shelf */ 978 EQ_SHELF_SHIFT, /* high shelf */
905 }; 979 };
906 unsigned int channels = audio_dsp.data.num_channels; 980 unsigned int channels = AUDIO_DSP.data.num_channels;
907 int i; 981 int i;
908 982
909 /* filter configuration currently is 1 low shelf filter, 3 band peaking 983 /* filter configuration currently is 1 low shelf filter, 3 band peaking
@@ -925,14 +999,14 @@ static void eq_process(int count, int32_t *buf[])
925 */ 999 */
926void dsp_set_eq(bool enable) 1000void dsp_set_eq(bool enable)
927{ 1001{
928 audio_dsp.eq_process = enable ? eq_process : NULL; 1002 AUDIO_DSP.eq_process = enable ? eq_process : NULL;
929 set_gain(&audio_dsp); 1003 set_gain(&AUDIO_DSP);
930} 1004}
931 1005
932static void dsp_set_stereo_width(int value) 1006static void dsp_set_stereo_width(int value)
933{ 1007{
934 long width, straight, cross; 1008 long width, straight, cross;
935 1009
936 width = value * 0x7fffff / 100; 1010 width = value * 0x7fffff / 100;
937 1011
938 if (value <= 100) 1012 if (value <= 100)
@@ -1039,14 +1113,14 @@ static void dsp_set_channel_config(int value)
1039 }; 1113 };
1040 1114
1041 if ((unsigned)value >= ARRAYLEN(channels_process_functions) || 1115 if ((unsigned)value >= ARRAYLEN(channels_process_functions) ||
1042 audio_dsp.stereo_mode == STEREO_MONO) 1116 AUDIO_DSP.stereo_mode == STEREO_MONO)
1043 { 1117 {
1044 value = SOUND_CHAN_STEREO; 1118 value = SOUND_CHAN_STEREO;
1045 } 1119 }
1046 1120
1047 /* This doesn't apply to voice */ 1121 /* This doesn't apply to voice */
1048 channels_mode = value; 1122 channels_mode = value;
1049 audio_dsp.channels_process = channels_process_functions[value]; 1123 AUDIO_DSP.channels_process = channels_process_functions[value];
1050} 1124}
1051 1125
1052#if CONFIG_CODEC == SWCODEC 1126#if CONFIG_CODEC == SWCODEC
@@ -1057,10 +1131,10 @@ static void set_tone_controls(void)
1057 filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200, 1131 filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200,
1058 0xffffffff/NATIVE_FREQUENCY*3500, 1132 0xffffffff/NATIVE_FREQUENCY*3500,
1059 bass, treble, -prescale, 1133 bass, treble, -prescale,
1060 audio_dsp.tone_filter.coefs); 1134 AUDIO_DSP.tone_filter.coefs);
1061 /* Sync the voice dsp coefficients */ 1135 /* Sync the voice dsp coefficients */
1062 memcpy(&voice_dsp.tone_filter.coefs, audio_dsp.tone_filter.coefs, 1136 memcpy(&VOICE_DSP.tone_filter.coefs, AUDIO_DSP.tone_filter.coefs,
1063 sizeof (voice_dsp.tone_filter.coefs)); 1137 sizeof (VOICE_DSP.tone_filter.coefs));
1064} 1138}
1065#endif 1139#endif
1066 1140
@@ -1069,7 +1143,8 @@ static void set_tone_controls(void)
1069 */ 1143 */
1070int dsp_callback(int msg, intptr_t param) 1144int dsp_callback(int msg, intptr_t param)
1071{ 1145{
1072 switch (msg) { 1146 switch (msg)
1147 {
1073#ifdef HAVE_SW_TONE_CONTROLS 1148#ifdef HAVE_SW_TONE_CONTROLS
1074 case DSP_CALLBACK_SET_PRESCALE: 1149 case DSP_CALLBACK_SET_PRESCALE:
1075 prescale = param; 1150 prescale = param;
@@ -1112,7 +1187,6 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1112 static long last_yield; 1187 static long last_yield;
1113 long tick; 1188 long tick;
1114 int written = 0; 1189 int written = 0;
1115 int samples;
1116 1190
1117#if defined(CPU_COLDFIRE) 1191#if defined(CPU_COLDFIRE)
1118 /* set emac unit for dsp processing, and save old macsr, we're running in 1192 /* set emac unit for dsp processing, and save old macsr, we're running in
@@ -1132,43 +1206,58 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1132 will be preloaded to be used for the call if not. */ 1206 will be preloaded to be used for the call if not. */
1133 while (count > 0) 1207 while (count > 0)
1134 { 1208 {
1135 samples = MIN(SAMPLE_BUF_COUNT/2, count); 1209 int samples = MIN(sample_buf_count/2, count);
1136 count -= samples; 1210 count -= samples;
1137 1211
1138 dsp->input_samples(samples, src, tmp); 1212 dsp->input_samples(samples, src, tmp);
1139 1213
1140 if (dsp->apply_gain) 1214 if (dsp->tdspeed_active)
1141 dsp->apply_gain(samples, &dsp->data, tmp); 1215 samples = tdspeed_doit(tmp, samples);
1142 1216
1143 if (dsp->resample && (samples = resample(dsp, samples, tmp)) <= 0) 1217 int chunk_offset = 0;
1144 break; /* I'm pretty sure we're downsampling here */ 1218 while (samples > 0)
1219 {
1220 int32_t *t2[2];
1221 t2[0] = tmp[0]+chunk_offset;
1222 t2[1] = tmp[1]+chunk_offset;
1223
1224 int chunk = MIN(sample_buf_count/2, samples);
1225 chunk_offset += chunk;
1226 samples -= chunk;
1227
1228 if (dsp->apply_gain)
1229 dsp->apply_gain(chunk, &dsp->data, t2);
1230
1231 if (dsp->resample && (chunk = resample(dsp, chunk, t2)) <= 0)
1232 break; /* I'm pretty sure we're downsampling here */
1145 1233
1146 if (dsp->apply_crossfeed) 1234 if (dsp->apply_crossfeed)
1147 dsp->apply_crossfeed(samples, tmp); 1235 dsp->apply_crossfeed(chunk, t2);
1148 1236
1149 if (dsp->eq_process) 1237 if (dsp->eq_process)
1150 dsp->eq_process(samples, tmp); 1238 dsp->eq_process(chunk, t2);
1151 1239
1152#ifdef HAVE_SW_TONE_CONTROLS 1240#ifdef HAVE_SW_TONE_CONTROLS
1153 if ((bass | treble) != 0) 1241 if ((bass | treble) != 0)
1154 eq_filter(tmp, &dsp->tone_filter, samples, 1242 eq_filter(t2, &dsp->tone_filter, chunk,
1155 dsp->data.num_channels, FILTER_BISHELF_SHIFT); 1243 dsp->data.num_channels, FILTER_BISHELF_SHIFT);
1156#endif 1244#endif
1157 1245
1158 if (dsp->channels_process) 1246 if (dsp->channels_process)
1159 dsp->channels_process(samples, tmp); 1247 dsp->channels_process(chunk, t2);
1160 1248
1161 dsp->output_samples(samples, &dsp->data, tmp, (int16_t *)dst); 1249 dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst);
1162 1250
1163 written += samples; 1251 written += chunk;
1164 dst += samples * sizeof (int16_t) * 2; 1252 dst += chunk * sizeof (int16_t) * 2;
1165 1253
1166 /* yield at least once each tick */ 1254 /* yield at least once each tick */
1167 tick = current_tick; 1255 tick = current_tick;
1168 if (TIME_AFTER(tick, last_yield)) 1256 if (TIME_AFTER(tick, last_yield))
1169 { 1257 {
1170 last_yield = tick; 1258 last_yield = tick;
1171 yield(); 1259 yield();
1260 }
1172 } 1261 }
1173 } 1262 }
1174 1263
@@ -1188,6 +1277,20 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1188/* dsp_input_size MUST be called afterwards */ 1277/* dsp_input_size MUST be called afterwards */
1189int dsp_output_count(struct dsp_config *dsp, int count) 1278int dsp_output_count(struct dsp_config *dsp, int count)
1190{ 1279{
1280 if(!dsp->tdspeed_active)
1281 {
1282 sample_buf = small_sample_buf;
1283 resample_buf = small_resample_buf;
1284 sample_buf_count = SMALL_SAMPLE_BUF_COUNT;
1285 }
1286 else
1287 {
1288 sample_buf = big_sample_buf;
1289 sample_buf_count = big_sample_buf_count;
1290 resample_buf = big_resample_buf;
1291 }
1292 if(dsp->tdspeed_active)
1293 count = tdspeed_est_output_size();
1191 if (dsp->resample) 1294 if (dsp->resample)
1192 { 1295 {
1193 count = (int)(((unsigned long)count * NATIVE_FREQUENCY 1296 count = (int)(((unsigned long)count * NATIVE_FREQUENCY
@@ -1195,12 +1298,12 @@ int dsp_output_count(struct dsp_config *dsp, int count)
1195 } 1298 }
1196 1299
1197 /* Now we have the resampled sample count which must not exceed 1300 /* Now we have the resampled sample count which must not exceed
1198 * RESAMPLE_BUF_COUNT/2 to avoid resample buffer overflow. One 1301 * RESAMPLE_BUF_RIGHT_CHANNEL to avoid resample buffer overflow. One
1199 * must call dsp_input_count() to get the correct input sample 1302 * must call dsp_input_count() to get the correct input sample
1200 * count. 1303 * count.
1201 */ 1304 */
1202 if (count > RESAMPLE_BUF_COUNT/2) 1305 if (count > RESAMPLE_BUF_RIGHT_CHANNEL)
1203 count = RESAMPLE_BUF_COUNT/2; 1306 count = RESAMPLE_BUF_RIGHT_CHANNEL;
1204 1307
1205 return count; 1308 return count;
1206} 1309}
@@ -1221,6 +1324,9 @@ int dsp_input_count(struct dsp_config *dsp, int count)
1221 dsp->data.resample_data.delta) >> 16); 1324 dsp->data.resample_data.delta) >> 16);
1222 } 1325 }
1223 1326
1327 if(dsp->tdspeed_active)
1328 count = tdspeed_est_input_size(count);
1329
1224 return count; 1330 return count;
1225} 1331}
1226 1332
@@ -1234,7 +1340,7 @@ static void dsp_update_functions(struct dsp_config *dsp)
1234{ 1340{
1235 sample_input_new_format(dsp); 1341 sample_input_new_format(dsp);
1236 sample_output_new_format(dsp); 1342 sample_output_new_format(dsp);
1237 if (dsp == &audio_dsp) 1343 if (dsp == &AUDIO_DSP)
1238 dsp_set_crossfeed(crossfeed_enabled); 1344 dsp_set_crossfeed(crossfeed_enabled);
1239} 1345}
1240 1346
@@ -1246,9 +1352,9 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1246 switch (value) 1352 switch (value)
1247 { 1353 {
1248 case CODEC_IDX_AUDIO: 1354 case CODEC_IDX_AUDIO:
1249 return (intptr_t)&audio_dsp; 1355 return (intptr_t)&AUDIO_DSP;
1250 case CODEC_IDX_VOICE: 1356 case CODEC_IDX_VOICE:
1251 return (intptr_t)&voice_dsp; 1357 return (intptr_t)&VOICE_DSP;
1252 default: 1358 default:
1253 return (intptr_t)NULL; 1359 return (intptr_t)NULL;
1254 } 1360 }
@@ -1262,12 +1368,13 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1262 if we're called from the main audio thread. Voice UI thread should 1368 if we're called from the main audio thread. Voice UI thread should
1263 not need this feature. 1369 not need this feature.
1264 */ 1370 */
1265 if (dsp == &audio_dsp) 1371 if (dsp == &AUDIO_DSP)
1266 dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000; 1372 dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000;
1267 else 1373 else
1268 dsp->frequency = dsp->codec_frequency; 1374 dsp->frequency = dsp->codec_frequency;
1269 1375
1270 resampler_new_delta(dsp); 1376 resampler_new_delta(dsp);
1377 tdspeed_setup(dsp);
1271 break; 1378 break;
1272 1379
1273 case DSP_SET_SAMPLE_DEPTH: 1380 case DSP_SET_SAMPLE_DEPTH:
@@ -1290,13 +1397,14 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1290 1397
1291 dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH; 1398 dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH;
1292 sample_input_new_format(dsp); 1399 sample_input_new_format(dsp);
1293 dither_init(dsp); 1400 dither_init(dsp);
1294 break; 1401 break;
1295 1402
1296 case DSP_SET_STEREO_MODE: 1403 case DSP_SET_STEREO_MODE:
1297 dsp->stereo_mode = value; 1404 dsp->stereo_mode = value;
1298 dsp->data.num_channels = value == STEREO_MONO ? 1 : 2; 1405 dsp->data.num_channels = value == STEREO_MONO ? 1 : 2;
1299 dsp_update_functions(dsp); 1406 dsp_update_functions(dsp);
1407 tdspeed_setup(dsp);
1300 break; 1408 break;
1301 1409
1302 case DSP_RESET: 1410 case DSP_RESET:
@@ -1310,7 +1418,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1310 dsp->data.clip_min = -((1 << WORD_FRACBITS)); 1418 dsp->data.clip_min = -((1 << WORD_FRACBITS));
1311 dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY; 1419 dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY;
1312 1420
1313 if (dsp == &audio_dsp) 1421 if (dsp == &AUDIO_DSP)
1314 { 1422 {
1315 track_gain = 0; 1423 track_gain = 0;
1316 album_gain = 0; 1424 album_gain = 0;
@@ -1321,6 +1429,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1321 1429
1322 dsp_update_functions(dsp); 1430 dsp_update_functions(dsp);
1323 resampler_new_delta(dsp); 1431 resampler_new_delta(dsp);
1432 tdspeed_setup(dsp);
1324 break; 1433 break;
1325 1434
1326 case DSP_FLUSH: 1435 case DSP_FLUSH:
@@ -1328,25 +1437,26 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1328 sizeof (dsp->data.resample_data)); 1437 sizeof (dsp->data.resample_data));
1329 resampler_new_delta(dsp); 1438 resampler_new_delta(dsp);
1330 dither_init(dsp); 1439 dither_init(dsp);
1440 tdspeed_setup(dsp);
1331 break; 1441 break;
1332 1442
1333 case DSP_SET_TRACK_GAIN: 1443 case DSP_SET_TRACK_GAIN:
1334 if (dsp == &audio_dsp) 1444 if (dsp == &AUDIO_DSP)
1335 dsp_set_gain_var(&track_gain, value); 1445 dsp_set_gain_var(&track_gain, value);
1336 break; 1446 break;
1337 1447
1338 case DSP_SET_ALBUM_GAIN: 1448 case DSP_SET_ALBUM_GAIN:
1339 if (dsp == &audio_dsp) 1449 if (dsp == &AUDIO_DSP)
1340 dsp_set_gain_var(&album_gain, value); 1450 dsp_set_gain_var(&album_gain, value);
1341 break; 1451 break;
1342 1452
1343 case DSP_SET_TRACK_PEAK: 1453 case DSP_SET_TRACK_PEAK:
1344 if (dsp == &audio_dsp) 1454 if (dsp == &AUDIO_DSP)
1345 dsp_set_gain_var(&track_peak, value); 1455 dsp_set_gain_var(&track_peak, value);
1346 break; 1456 break;
1347 1457
1348 case DSP_SET_ALBUM_PEAK: 1458 case DSP_SET_ALBUM_PEAK:
1349 if (dsp == &audio_dsp) 1459 if (dsp == &AUDIO_DSP)
1350 dsp_set_gain_var(&album_peak, value); 1460 dsp_set_gain_var(&album_peak, value);
1351 break; 1461 break;
1352 1462
@@ -1403,5 +1513,5 @@ void dsp_set_replaygain(void)
1403 1513
1404 /* Store in S8.23 format to simplify calculations. */ 1514 /* Store in S8.23 format to simplify calculations. */
1405 replaygain = gain; 1515 replaygain = gain;
1406 set_gain(&audio_dsp); 1516 set_gain(&AUDIO_DSP);
1407} 1517}
diff --git a/apps/dsp.h b/apps/dsp.h
index c3239360b0..5c4211f251 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -164,5 +164,9 @@ void sound_set_pitch(int r);
164int sound_get_pitch(void); 164int sound_get_pitch(void);
165int dsp_callback(int msg, intptr_t param); 165int dsp_callback(int msg, intptr_t param);
166void dsp_dither_enable(bool enable); 166void dsp_dither_enable(bool enable);
167void dsp_timestretch_enable(bool enable);
168void dsp_set_timestretch(int percent);
169bool dsp_timestretch_enabled(void);
170int dsp_get_timestretch(void);
167 171
168#endif 172#endif
diff --git a/apps/dsp_arm.S b/apps/dsp_arm.S
index 5b43c3bb5b..11b7ba7141 100644
--- a/apps/dsp_arm.S
+++ b/apps/dsp_arm.S
@@ -86,7 +86,7 @@ channels_process_sound_chan_karaoke:
86 86
87/**************************************************************************** 87/****************************************************************************
88 * void sample_output_mono(int count, struct dsp_data *data, 88 * void sample_output_mono(int count, struct dsp_data *data,
89 int32_t *src[], int16_t *dst) 89 * const int32_t *src[], int16_t *dst)
90 * NOTE: The following code processes two samples at once. When count is odd, 90 * NOTE: The following code processes two samples at once. When count is odd,
91 * there is an additional obsolete sample processed, which will not be 91 * there is an additional obsolete sample processed, which will not be
92 * used by the calling functions. 92 * used by the calling functions.
@@ -136,7 +136,7 @@ sample_output_mono:
136 136
137/**************************************************************************** 137/****************************************************************************
138 * void sample_output_stereo(int count, struct dsp_data *data, 138 * void sample_output_stereo(int count, struct dsp_data *data,
139 int32_t *src[], int16_t *dst) 139 * const int32_t *src[], int16_t *dst)
140 * NOTE: The following code processes two samples at once. When count is odd, 140 * NOTE: The following code processes two samples at once. When count is odd,
141 * there is an additional obsolete sample processed, which will not be 141 * there is an additional obsolete sample processed, which will not be
142 * used by the calling functions. 142 * used by the calling functions.
diff --git a/apps/dsp_asm.h b/apps/dsp_asm.h
index 64373d3eea..85db57b6ef 100644
--- a/apps/dsp_asm.h
+++ b/apps/dsp_asm.h
@@ -54,9 +54,9 @@ void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]);
54 54
55#ifdef DSP_HAVE_ASM_RESAMPLING 55#ifdef DSP_HAVE_ASM_RESAMPLING
56int dsp_upsample(int count, struct dsp_data *data, 56int dsp_upsample(int count, struct dsp_data *data,
57 int32_t *src[], int32_t *dst[]); 57 const int32_t *src[], int32_t *dst[]);
58int dsp_downsample(int count, struct dsp_data *data, 58int dsp_downsample(int count, struct dsp_data *data,
59 int32_t *src[], int32_t *dst[]); 59 const int32_t *src[], int32_t *dst[]);
60#endif /* DSP_HAVE_ASM_RESAMPLING */ 60#endif /* DSP_HAVE_ASM_RESAMPLING */
61 61
62#ifdef DSP_HAVE_ASM_SOUND_CHAN_MONO 62#ifdef DSP_HAVE_ASM_SOUND_CHAN_MONO
@@ -73,12 +73,12 @@ void channels_process_sound_chan_karaoke(int count, int32_t *buf[]);
73 73
74#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO 74#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
75void sample_output_stereo(int count, struct dsp_data *data, 75void sample_output_stereo(int count, struct dsp_data *data,
76 int32_t *src[], int16_t *dst); 76 const int32_t *src[], int16_t *dst);
77#endif 77#endif
78 78
79#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO 79#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
80void sample_output_mono(int count, struct dsp_data *data, 80void sample_output_mono(int count, struct dsp_data *data,
81 int32_t *src[], int16_t *dst); 81 const int32_t *src[], int16_t *dst);
82#endif 82#endif
83 83
84#endif /* _DSP_ASM_H */ 84#endif /* _DSP_ASM_H */
diff --git a/apps/dsp_cf.S b/apps/dsp_cf.S
index d8869fcb09..cda811a7d5 100644
--- a/apps/dsp_cf.S
+++ b/apps/dsp_cf.S
@@ -191,7 +191,7 @@ dsp_downsample:
191 191
192/**************************************************************************** 192/****************************************************************************
193 * int dsp_upsample(int count, struct dsp_data *dsp, 193 * int dsp_upsample(int count, struct dsp_data *dsp,
194 * int32_t *src[], int32_t *dst[]) 194 * const int32_t *src[], int32_t *dst[])
195 */ 195 */
196 .section .text 196 .section .text
197 .align 2 197 .align 2
@@ -395,7 +395,7 @@ channels_process_sound_chan_karaoke:
395 395
396/**************************************************************************** 396/****************************************************************************
397 * void sample_output_stereo(int count, struct dsp_data *data, 397 * void sample_output_stereo(int count, struct dsp_data *data,
398 * int32_t *src[], int16_t *dst) 398 * const int32_t *src[], int16_t *dst)
399 * 399 *
400 * Framework based on the ubiquitous Rockbox line transfer logic for 400 * Framework based on the ubiquitous Rockbox line transfer logic for
401 * Coldfire CPUs. 401 * Coldfire CPUs.
@@ -517,7 +517,7 @@ sample_output_stereo:
517 517
518/**************************************************************************** 518/****************************************************************************
519 * void sample_output_mono(int count, struct dsp_data *data, 519 * void sample_output_mono(int count, struct dsp_data *data,
520 * int32_t *src[], int16_t *dst) 520 * const int32_t *src[], int16_t *dst)
521 * 521 *
522 * Same treatment as sample_output_stereo but for one channel. 522 * Same treatment as sample_output_stereo but for one channel.
523 */ 523 */
diff --git a/apps/gui/pitchscreen.c b/apps/gui/pitchscreen.c
index 485eb7861c..5072031652 100644
--- a/apps/gui/pitchscreen.c
+++ b/apps/gui/pitchscreen.c
@@ -36,12 +36,13 @@
36#include "system.h" 36#include "system.h"
37#include "misc.h" 37#include "misc.h"
38#include "pitchscreen.h" 38#include "pitchscreen.h"
39#if CONFIG_CODEC == SWCODEC
40#include "tdspeed.h"
41#endif
42
39 43
40#define PITCH_MODE_ABSOLUTE 1
41#define PITCH_MODE_SEMITONE -PITCH_MODE_ABSOLUTE
42#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */ 44#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
43 /* on both sides when drawing */ 45 /* on both sides when drawing */
44
45 46
46#define PITCH_MAX 2000 47#define PITCH_MAX 2000
47#define PITCH_MIN 500 48#define PITCH_MIN 500
@@ -49,8 +50,14 @@
49#define PITCH_BIG_DELTA 10 50#define PITCH_BIG_DELTA 10
50#define PITCH_NUDGE_DELTA 20 51#define PITCH_NUDGE_DELTA 20
51 52
52 53static enum
53static int pitch_mode = PITCH_MODE_ABSOLUTE; /* 1 - absolute, -1 - semitone */ 54{
55 PITCH_MODE_ABSOLUTE,
56 PITCH_MODE_SEMITONE,
57#if CONFIG_CODEC == SWCODEC
58 PITCH_MODE_TIMESTRETCH,
59#endif
60} pitch_mode = PITCH_MODE_ABSOLUTE;
54 61
55enum 62enum
56{ 63{
@@ -83,8 +90,8 @@ static void pitchscreen_fix_viewports(struct viewport *parent,
83 90
84/* must be called before pitchscreen_draw, or within 91/* must be called before pitchscreen_draw, or within
85 * since it neither clears nor updates the display */ 92 * since it neither clears nor updates the display */
86static void pitchscreen_draw_icons (struct screen *display, 93static void pitchscreen_draw_icons(struct screen *display,
87 struct viewport *parent) 94 struct viewport *parent)
88{ 95{
89 display->set_viewport(parent); 96 display->set_viewport(parent);
90 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow], 97 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
@@ -102,25 +109,29 @@ static void pitchscreen_draw_icons (struct screen *display,
102 display->update_viewport(); 109 display->update_viewport();
103} 110}
104 111
105static void pitchscreen_draw (struct screen *display, int max_lines, 112static void pitchscreen_draw(struct screen *display, int max_lines,
106 struct viewport pitch_viewports[PITCH_ITEM_COUNT], int pitch) 113 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
114 int pitch
115#if CONFIG_CODEC == SWCODEC
116 ,int speed
117#endif
118 )
107{ 119{
108 unsigned char* ptr; 120 unsigned char* ptr;
109 unsigned char buf[32]; 121 char buf[32];
110 int width_val, w, h; 122 int w, h;
111 bool show_lang_pitch; 123 bool show_lang_pitch;
112 124
113 /* Hide "Pitch up/Pitch down" for a small screen */ 125 /* "Pitch up/Pitch down" - hide for a small screen */
114 if (max_lines >= 5) 126 if (max_lines >= 5)
115 { 127 {
116 /* UP: Pitch Up */ 128 /* UP: Pitch Up */
117 display->set_viewport(&pitch_viewports[PITCH_TOP]); 129 display->set_viewport(&pitch_viewports[PITCH_TOP]);
118 if (pitch_mode == PITCH_MODE_ABSOLUTE) { 130 if (pitch_mode == PITCH_MODE_SEMITONE)
119 ptr = str(LANG_PITCH_UP);
120 } else {
121 ptr = str(LANG_PITCH_UP_SEMITONE); 131 ptr = str(LANG_PITCH_UP_SEMITONE);
122 } 132 else
123 display->getstringsize(ptr,&w,&h); 133 ptr = str(LANG_PITCH_UP);
134 display->getstringsize(ptr, &w, &h);
124 display->clear_viewport(); 135 display->clear_viewport();
125 /* draw text */ 136 /* draw text */
126 display->putsxy((pitch_viewports[PITCH_TOP].width / 2) - 137 display->putsxy((pitch_viewports[PITCH_TOP].width / 2) -
@@ -129,81 +140,125 @@ static void pitchscreen_draw (struct screen *display, int max_lines,
129 140
130 /* DOWN: Pitch Down */ 141 /* DOWN: Pitch Down */
131 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]); 142 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
132 if (pitch_mode == PITCH_MODE_ABSOLUTE) { 143 if (pitch_mode == PITCH_MODE_SEMITONE)
133 ptr = str(LANG_PITCH_DOWN);
134 } else {
135 ptr = str(LANG_PITCH_DOWN_SEMITONE); 144 ptr = str(LANG_PITCH_DOWN_SEMITONE);
136 } 145 else
137 display->getstringsize(ptr,&w,&h); 146 ptr = str(LANG_PITCH_DOWN);
147 display->getstringsize(ptr, &w, &h);
138 display->clear_viewport(); 148 display->clear_viewport();
139 /* draw text */ 149 /* draw text */
140 display->putsxy((pitch_viewports[PITCH_BOTTOM].width / 2) - 150 display->putsxy((pitch_viewports[PITCH_BOTTOM].width / 2) -
141 (w / 2), 0, ptr); 151 (w / 2), 0, ptr);
142 display->update_viewport(); 152 display->update_viewport();
143 } 153 }
144 display->set_viewport(&pitch_viewports[PITCH_MID]);
145 154
146 snprintf((char *)buf, sizeof(buf), "%s", str(LANG_PITCH)); 155 /* Middle section */
147 display->getstringsize(buf,&w,&h); 156 display->set_viewport(&pitch_viewports[PITCH_MID]);
148 /* lets hide LANG_PITCH for smaller screens */
149 display->clear_viewport(); 157 display->clear_viewport();
158 int width_used = 0;
159
160 /* Middle section upper line - hide for a small screen */
150 if ((show_lang_pitch = (max_lines >= 3))) 161 if ((show_lang_pitch = (max_lines >= 3)))
162 {
163#if CONFIG_CODEC == SWCODEC
164 if (pitch_mode != PITCH_MODE_TIMESTRETCH)
165 {
166#endif
167 /* LANG_PITCH */
168 snprintf(buf, sizeof(buf), "%s", str(LANG_PITCH));
169#if CONFIG_CODEC == SWCODEC
170 }
171 else
172 {
173 /* Pitch:XXX.X% */
174 snprintf(buf, sizeof(buf), "%s:%d.%d%%", str(LANG_PITCH),
175 pitch / 10, pitch % 10);
176 }
177#endif
178 display->getstringsize(buf, &w, &h);
151 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), 179 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
152 0, buf); 180 0, buf);
153 181 if (w > width_used)
154 /* "XXX.X%" */ 182 width_used = w;
155 snprintf((char *)buf, sizeof(buf), "%d.%d%%", 183 }
156 pitch / 10, pitch % 10 ); 184
157 display->getstringsize(buf,&width_val,&h); 185 /* Middle section lower line */
158 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (width_val / 2), 186#if CONFIG_CODEC == SWCODEC
159 (show_lang_pitch? h : h/2), buf); 187 if (pitch_mode != PITCH_MODE_TIMESTRETCH)
160 188 {
161 /* What's wider? LANG_PITCH or the value? 189#endif
162 * Only interesting if LANG_PITCH is actually drawn */ 190 /* "XXX.X%" */
163 if (show_lang_pitch && width_val > w) 191 snprintf(buf, sizeof(buf), "%d.%d%%",
164 w = width_val; 192 pitch / 10, pitch % 10);
165 193#if CONFIG_CODEC == SWCODEC
166 /* Let's treat '+' and '-' as equally wide 194 }
167 * This saves a getstringsize call 195 else
168 * Also, it wouldn't look nice if -2% shows up, but +2% not */ 196 {
169 display->getstringsize("+2%",&width_val,&h); 197 /* "Speed:XXX%" */
170 w += width_val*2; 198 snprintf(buf, sizeof(buf), "%s:%d%%", str(LANG_SPEED), speed);
171 /* hide +2%/-2% for a narrow screens */ 199 }
172 if (w <= pitch_viewports[PITCH_MID].width) 200#endif
201 display->getstringsize(buf, &w, &h);
202 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
203 (show_lang_pitch ? h : h/2), buf);
204 if (w > width_used)
205 width_used = w;
206
207 /* Middle section left/right labels */
208 const char *leftlabel = "-2%";
209 const char *rightlabel = "+2%";
210#if CONFIG_CODEC == SWCODEC
211 if (pitch_mode == PITCH_MODE_TIMESTRETCH)
212 {
213 leftlabel = "<<";
214 rightlabel = ">>";
215 }
216#endif
217
218 /* Only display if they fit */
219 display->getstringsize(leftlabel, &w, &h);
220 width_used += w;
221 display->getstringsize(rightlabel, &w, &h);
222 width_used += w;
223
224 if (width_used <= pitch_viewports[PITCH_MID].width)
173 { 225 {
174 /* RIGHT: +2% */ 226 display->putsxy(0, h / 2, leftlabel);
175 display->putsxy(pitch_viewports[PITCH_MID].width - width_val, h /2, "+2%"); 227 display->putsxy(pitch_viewports[PITCH_MID].width - w, h /2, rightlabel);
176 /* LEFT: -2% */
177 display->putsxy(0, h / 2, "-2%");
178 } 228 }
179 display->update_viewport(); 229 display->update_viewport();
180 display->set_viewport(NULL); 230 display->set_viewport(NULL);
181} 231}
182 232
183static int pitch_increase(int pitch, int delta, bool allow_cutoff) 233static int pitch_increase(int pitch, int pitch_delta, bool allow_cutoff)
184{ 234{
185 int new_pitch; 235 int new_pitch;
186 236
187 if (delta < 0) { 237 if (pitch_delta < 0)
188 if (pitch + delta >= PITCH_MIN) { 238 {
189 new_pitch = pitch + delta; 239 if (pitch + pitch_delta >= PITCH_MIN)
190 } else { 240 new_pitch = pitch + pitch_delta;
191 if (!allow_cutoff) { 241 else
242 {
243 if (!allow_cutoff)
192 return pitch; 244 return pitch;
193 }
194 new_pitch = PITCH_MIN; 245 new_pitch = PITCH_MIN;
195 } 246 }
196 } else if (delta > 0) { 247 }
197 if (pitch + delta <= PITCH_MAX) { 248 else if (pitch_delta > 0)
198 new_pitch = pitch + delta; 249 {
199 } else { 250 if (pitch + pitch_delta <= PITCH_MAX)
200 if (!allow_cutoff) { 251 new_pitch = pitch + pitch_delta;
252 else
253 {
254 if (!allow_cutoff)
201 return pitch; 255 return pitch;
202 }
203 new_pitch = PITCH_MAX; 256 new_pitch = PITCH_MAX;
204 } 257 }
205 } else { 258 }
206 /* delta == 0 -> no real change */ 259 else
260 {
261 /* pitch_delta == 0 -> no real change */
207 return pitch; 262 return pitch;
208 } 263 }
209 sound_set_pitch(new_pitch); 264 sound_set_pitch(new_pitch);
@@ -234,10 +289,13 @@ static int pitch_increase_semitone(int pitch, bool up)
234 uint32_t tmp; 289 uint32_t tmp;
235 uint32_t round_fct; /* How much to scale down at the end */ 290 uint32_t round_fct; /* How much to scale down at the end */
236 tmp = pitch; 291 tmp = pitch;
237 if (up) { 292 if (up)
293 {
238 tmp = tmp * PITCH_SEMITONE_FACTOR; 294 tmp = tmp * PITCH_SEMITONE_FACTOR;
239 round_fct = PITCH_K_FCT; 295 round_fct = PITCH_K_FCT;
240 } else { 296 }
297 else
298 {
241 tmp = (tmp * PITCH_KN_FCT) / PITCH_SEMITONE_FACTOR; 299 tmp = (tmp * PITCH_KN_FCT) / PITCH_SEMITONE_FACTOR;
242 round_fct = PITCH_N_FCT; 300 round_fct = PITCH_N_FCT;
243 } 301 }
@@ -256,7 +314,12 @@ int gui_syncpitchscreen_run(void)
256{ 314{
257 int button, i; 315 int button, i;
258 int pitch = sound_get_pitch(); 316 int pitch = sound_get_pitch();
259 int new_pitch, delta = 0; 317#if CONFIG_CODEC == SWCODEC
318 int speed = dsp_get_timestretch();
319 int maintain_speed_pitch = speed * pitch; /* speed * pitch to maintain */
320#endif
321 int new_pitch;
322 int pitch_delta = 0;
260 bool nudged = false; 323 bool nudged = false;
261 bool exit = false; 324 bool exit = false;
262 /* should maybe be passed per parameter later, not needed for now */ 325 /* should maybe be passed per parameter later, not needed for now */
@@ -283,58 +346,118 @@ int gui_syncpitchscreen_run(void)
283 { 346 {
284 FOR_NB_SCREENS(i) 347 FOR_NB_SCREENS(i)
285 pitchscreen_draw(&screens[i], max_lines[i], 348 pitchscreen_draw(&screens[i], max_lines[i],
286 pitch_viewports[i], pitch); 349 pitch_viewports[i], pitch
287 button = get_action(CONTEXT_PITCHSCREEN,HZ); 350#if CONFIG_CODEC == SWCODEC
288 switch (button) { 351 , speed
352#endif
353 );
354 button = get_action(CONTEXT_PITCHSCREEN, HZ);
355 switch (button)
356 {
289 case ACTION_PS_INC_SMALL: 357 case ACTION_PS_INC_SMALL:
290 delta = PITCH_SMALL_DELTA; 358 pitch_delta = PITCH_SMALL_DELTA;
291 break; 359 break;
292 360
293 case ACTION_PS_INC_BIG: 361 case ACTION_PS_INC_BIG:
294 delta = PITCH_BIG_DELTA; 362 pitch_delta = PITCH_BIG_DELTA;
295 break; 363 break;
296 364
297 case ACTION_PS_DEC_SMALL: 365 case ACTION_PS_DEC_SMALL:
298 delta = -PITCH_SMALL_DELTA; 366 pitch_delta = -PITCH_SMALL_DELTA;
299 break; 367 break;
300 368
301 case ACTION_PS_DEC_BIG: 369 case ACTION_PS_DEC_BIG:
302 delta = -PITCH_BIG_DELTA; 370 pitch_delta = -PITCH_BIG_DELTA;
303 break; 371 break;
304 372
305 case ACTION_PS_NUDGE_RIGHT: 373 case ACTION_PS_NUDGE_RIGHT:
306 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false); 374#if CONFIG_CODEC == SWCODEC
307 nudged = (new_pitch != pitch); 375 if (pitch_mode != PITCH_MODE_TIMESTRETCH)
308 pitch = new_pitch; 376 {
377#endif
378 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
379 nudged = (new_pitch != pitch);
380 pitch = new_pitch;
381 break;
382#if CONFIG_CODEC == SWCODEC
383 }
384
385 case ACTION_PS_FASTER:
386 if (pitch_mode == PITCH_MODE_TIMESTRETCH)
387 {
388 if (speed < SPEED_MAX)
389 {
390 speed++;
391 dsp_set_timestretch(speed);
392 maintain_speed_pitch = speed * pitch;
393 }
394 }
309 break; 395 break;
396#endif
310 397
311 case ACTION_PS_NUDGE_RIGHTOFF: 398 case ACTION_PS_NUDGE_RIGHTOFF:
312 if (nudged) { 399 if (nudged)
400 {
313 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false); 401 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
402 nudged = false;
314 } 403 }
315 nudged = false;
316 break; 404 break;
317 405
318 case ACTION_PS_NUDGE_LEFT: 406 case ACTION_PS_NUDGE_LEFT:
319 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false); 407#if CONFIG_CODEC == SWCODEC
320 nudged = (new_pitch != pitch); 408 if (pitch_mode != PITCH_MODE_TIMESTRETCH)
321 pitch = new_pitch; 409 {
410#endif
411 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
412 nudged = (new_pitch != pitch);
413 pitch = new_pitch;
414 break;
415#if CONFIG_CODEC == SWCODEC
416 }
417
418 case ACTION_PS_SLOWER:
419 if (pitch_mode == PITCH_MODE_TIMESTRETCH)
420 {
421 if (speed > SPEED_MIN)
422 {
423 speed--;
424 dsp_set_timestretch(speed);
425 maintain_speed_pitch = speed * pitch;
426 }
427 }
322 break; 428 break;
429#endif
323 430
324 case ACTION_PS_NUDGE_LEFTOFF: 431 case ACTION_PS_NUDGE_LEFTOFF:
325 if (nudged) { 432 if (nudged)
433 {
326 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false); 434 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
435 nudged = false;
327 } 436 }
328 nudged = false;
329 break; 437 break;
330 438
331 case ACTION_PS_RESET: 439 case ACTION_PS_RESET:
332 pitch = 1000; 440 pitch = 1000;
333 sound_set_pitch( pitch ); 441 sound_set_pitch(pitch);
442#if CONFIG_CODEC == SWCODEC
443 speed = 100;
444 dsp_set_timestretch(speed);
445 maintain_speed_pitch = speed * pitch;
446#endif
334 break; 447 break;
335 448
336 case ACTION_PS_TOGGLE_MODE: 449 case ACTION_PS_TOGGLE_MODE:
337 pitch_mode = -pitch_mode; 450 ++pitch_mode;
451#if CONFIG_CODEC == SWCODEC
452 if (dsp_timestretch_enabled())
453 {
454 if (pitch_mode > PITCH_MODE_TIMESTRETCH)
455 pitch_mode = PITCH_MODE_ABSOLUTE;
456 break;
457 }
458#endif
459 if (pitch_mode > PITCH_MODE_SEMITONE)
460 pitch_mode = PITCH_MODE_ABSOLUTE;
338 break; 461 break;
339 462
340 case ACTION_PS_EXIT: 463 case ACTION_PS_EXIT:
@@ -342,19 +465,32 @@ int gui_syncpitchscreen_run(void)
342 break; 465 break;
343 466
344 default: 467 default:
345 if(default_event_handler(button) == SYS_USB_CONNECTED) 468 if (default_event_handler(button) == SYS_USB_CONNECTED)
346 return 1; 469 return 1;
347 break; 470 break;
348 } 471 }
349 if(delta) 472 if (pitch_delta)
350 { 473 {
351 if (pitch_mode == PITCH_MODE_ABSOLUTE) { 474 if (pitch_mode == PITCH_MODE_SEMITONE)
352 pitch = pitch_increase(pitch, delta, true); 475 pitch = pitch_increase_semitone(pitch, pitch_delta > 0);
353 } else { 476 else
354 pitch = pitch_increase_semitone(pitch, delta > 0); 477 pitch = pitch_increase(pitch, pitch_delta, true);
478#if CONFIG_CODEC == SWCODEC
479 if (pitch_mode == PITCH_MODE_TIMESTRETCH)
480 {
481 /* Set speed to maintain time dimension */
482 /* i.e. increase pitch, slow down speed */
483 int new_speed = maintain_speed_pitch / pitch;
484 if (new_speed >= SPEED_MIN && new_speed <= SPEED_MAX)
485 {
486 speed = new_speed;
487 dsp_set_timestretch(speed);
488 }
355 } 489 }
356 490 else
357 delta = 0; 491 maintain_speed_pitch = speed * pitch;
492#endif
493 pitch_delta = 0;
358 } 494 }
359 } 495 }
360#if CONFIG_CODEC == SWCODEC 496#if CONFIG_CODEC == SWCODEC
diff --git a/apps/keymaps/keymap-av300.c b/apps/keymaps/keymap-av300.c
index 6c91499a50..1cca2c2a8b 100644
--- a/apps/keymaps/keymap-av300.c
+++ b/apps/keymaps/keymap-av300.c
@@ -159,6 +159,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
159 { ACTION_PS_TOGGLE_MODE, BUTTON_F1, BUTTON_NONE }, 159 { ACTION_PS_TOGGLE_MODE, BUTTON_F1, BUTTON_NONE },
160 { ACTION_PS_RESET, BUTTON_ON, BUTTON_NONE }, 160 { ACTION_PS_RESET, BUTTON_ON, BUTTON_NONE },
161 { ACTION_PS_EXIT, BUTTON_OFF, BUTTON_NONE }, 161 { ACTION_PS_EXIT, BUTTON_OFF, BUTTON_NONE },
162 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
163 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
162 164
163 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 165 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
164}; /* button_context_pitchcreen */ 166}; /* button_context_pitchcreen */
diff --git a/apps/keymaps/keymap-c100.c b/apps/keymaps/keymap-c100.c
index c8ba16cdfb..80948c0e19 100644
--- a/apps/keymaps/keymap-c100.c
+++ b/apps/keymaps/keymap-c100.c
@@ -174,6 +174,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
174 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE }, 174 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
175 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, 175 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
176 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, 176 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
177 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
178 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
177 179
178 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 180 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
179}; /* button_context_pitchscreen */ 181}; /* button_context_pitchscreen */
diff --git a/apps/keymaps/keymap-c200.c b/apps/keymaps/keymap-c200.c
index 56504584f8..c9e5d4394e 100644
--- a/apps/keymaps/keymap-c200.c
+++ b/apps/keymaps/keymap-c200.c
@@ -194,19 +194,17 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
194static const struct button_mapping button_context_pitchscreen[] = { 194static const struct button_mapping button_context_pitchscreen[] = {
195 { ACTION_PS_INC_SMALL, BUTTON_UP, BUTTON_NONE }, 195 { ACTION_PS_INC_SMALL, BUTTON_UP, BUTTON_NONE },
196 { ACTION_PS_INC_BIG, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, 196 { ACTION_PS_INC_BIG, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
197
198 { ACTION_PS_DEC_SMALL, BUTTON_DOWN, BUTTON_NONE }, 197 { ACTION_PS_DEC_SMALL, BUTTON_DOWN, BUTTON_NONE },
199 { ACTION_PS_DEC_BIG, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, 198 { ACTION_PS_DEC_BIG, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
200
201 { ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE }, 199 { ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE },
202 { ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE }, 200 { ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
203
204 { ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE }, 201 { ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
205 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE }, 202 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
206 203 { ACTION_PS_TOGGLE_MODE, BUTTON_REC, BUTTON_NONE },
207 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, 204 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
208 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, 205 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
209 206 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
207 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
210 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 208 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
211}; /* button_context_pitchscreen */ 209}; /* button_context_pitchscreen */
212 210
diff --git a/apps/keymaps/keymap-clip.c b/apps/keymaps/keymap-clip.c
index 4b778beeb7..002cc3c36f 100644
--- a/apps/keymaps/keymap-clip.c
+++ b/apps/keymaps/keymap-clip.c
@@ -182,19 +182,19 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
182}; /* button_context_settings_right_is_inc */ 182}; /* button_context_settings_right_is_inc */
183 183
184static const struct button_mapping button_context_pitchscreen[] = { 184static const struct button_mapping button_context_pitchscreen[] = {
185 { ACTION_PS_INC_SMALL, BUTTON_RIGHT, BUTTON_NONE }, 185 { ACTION_PS_INC_SMALL, BUTTON_UP, BUTTON_NONE },
186 { ACTION_PS_INC_BIG, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, 186 { ACTION_PS_INC_BIG, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
187 { ACTION_PS_DEC_SMALL, BUTTON_LEFT, BUTTON_NONE }, 187 { ACTION_PS_DEC_SMALL, BUTTON_DOWN, BUTTON_NONE },
188 { ACTION_PS_DEC_BIG, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, 188 { ACTION_PS_DEC_BIG, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
189 { ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE }, 189 { ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE },
190 { ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE }, 190 { ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
191 { ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE }, 191 { ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
192 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE }, 192 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
193 { ACTION_PS_TOGGLE_MODE, BUTTON_HOME, BUTTON_NONE }, 193 { ACTION_PS_TOGGLE_MODE, BUTTON_HOME, BUTTON_NONE },
194 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, 194 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
195 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, 195 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
196 { ACTION_PS_EXIT, BUTTON_UP, BUTTON_NONE }, 196 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
197 197 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
198 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 198 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
199}; /* button_context_pitchscreen */ 199}; /* button_context_pitchscreen */
200 200
diff --git a/apps/keymaps/keymap-e200.c b/apps/keymaps/keymap-e200.c
index a2017de6f3..569862827d 100644
--- a/apps/keymaps/keymap-e200.c
+++ b/apps/keymaps/keymap-e200.c
@@ -203,6 +203,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
203 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, 203 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
204 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, 204 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
205 { ACTION_PS_EXIT, BUTTON_UP, BUTTON_NONE }, 205 { ACTION_PS_EXIT, BUTTON_UP, BUTTON_NONE },
206 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
207 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT,BUTTON_NONE },
206 208
207 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 209 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
208}; /* button_context_pitchscreen */ 210}; /* button_context_pitchscreen */
diff --git a/apps/keymaps/keymap-fuze.c b/apps/keymaps/keymap-fuze.c
index 697598843b..783446429f 100644
--- a/apps/keymaps/keymap-fuze.c
+++ b/apps/keymaps/keymap-fuze.c
@@ -203,6 +203,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
203 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, 203 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
204 { ACTION_PS_EXIT, BUTTON_HOME|BUTTON_REPEAT, BUTTON_NONE }, 204 { ACTION_PS_EXIT, BUTTON_HOME|BUTTON_REPEAT, BUTTON_NONE },
205 { ACTION_PS_EXIT, BUTTON_UP, BUTTON_NONE }, 205 { ACTION_PS_EXIT, BUTTON_UP, BUTTON_NONE },
206 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
207 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
206 208
207 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 209 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
208}; /* button_context_pitchscreen */ 210}; /* button_context_pitchscreen */
diff --git a/apps/keymaps/keymap-gigabeat-s.c b/apps/keymaps/keymap-gigabeat-s.c
index 66b58615c5..da7baa29cb 100644
--- a/apps/keymaps/keymap-gigabeat-s.c
+++ b/apps/keymaps/keymap-gigabeat-s.c
@@ -252,6 +252,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
252 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE }, 252 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
253 { ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE }, 253 { ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE },
254 { ACTION_PS_EXIT, BUTTON_BACK, BUTTON_NONE }, 254 { ACTION_PS_EXIT, BUTTON_BACK, BUTTON_NONE },
255 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
256 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT,BUTTON_NONE },
255 257
256 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 258 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
257}; /* button_context_pitchcreen */ 259}; /* button_context_pitchcreen */
@@ -445,6 +447,8 @@ static const struct button_mapping remote_button_context_pitchscreen[] = {
445 { ACTION_PS_RESET, BUTTON_RC_PLAY|BUTTON_REL, BUTTON_RC_PLAY }, 447 { ACTION_PS_RESET, BUTTON_RC_PLAY|BUTTON_REL, BUTTON_RC_PLAY },
446 { ACTION_PS_TOGGLE_MODE, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_RC_PLAY }, 448 { ACTION_PS_TOGGLE_MODE, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_RC_PLAY },
447 { ACTION_PS_EXIT, BUTTON_RC_DSP|BUTTON_REL, BUTTON_RC_DSP }, 449 { ACTION_PS_EXIT, BUTTON_RC_DSP|BUTTON_REL, BUTTON_RC_DSP },
450 { ACTION_PS_SLOWER, BUTTON_RC_RW|BUTTON_REPEAT, BUTTON_NONE },
451 { ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
448 452
449 LAST_ITEM_IN_LIST 453 LAST_ITEM_IN_LIST
450}; /* remote_button_context_pitchscreen */ 454}; /* remote_button_context_pitchscreen */
diff --git a/apps/keymaps/keymap-gigabeat.c b/apps/keymaps/keymap-gigabeat.c
index 31abce5bb2..3e5f404c71 100644
--- a/apps/keymaps/keymap-gigabeat.c
+++ b/apps/keymaps/keymap-gigabeat.c
@@ -240,6 +240,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
240 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE }, 240 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
241 { ACTION_PS_RESET, BUTTON_A, BUTTON_NONE }, 241 { ACTION_PS_RESET, BUTTON_A, BUTTON_NONE },
242 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, 242 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
243 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
244 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
243 245
244 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 246 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
245}; /* button_context_pitchcreen */ 247}; /* button_context_pitchcreen */
diff --git a/apps/keymaps/keymap-h10.c b/apps/keymaps/keymap-h10.c
index aed55dbfa2..0c1d4b2331 100644
--- a/apps/keymaps/keymap-h10.c
+++ b/apps/keymaps/keymap-h10.c
@@ -260,6 +260,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
260 { ACTION_PS_TOGGLE_MODE, BUTTON_POWER, BUTTON_NONE }, 260 { ACTION_PS_TOGGLE_MODE, BUTTON_POWER, BUTTON_NONE },
261 { ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE }, 261 { ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE },
262 { ACTION_PS_EXIT, BUTTON_LEFT, BUTTON_NONE }, 262 { ACTION_PS_EXIT, BUTTON_LEFT, BUTTON_NONE },
263 { ACTION_PS_SLOWER, BUTTON_REW|BUTTON_REPEAT, BUTTON_NONE },
264 { ACTION_PS_FASTER, BUTTON_FF|BUTTON_REPEAT, BUTTON_NONE },
263 265
264 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 266 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
265}; /* button_context_pitchscreen */ 267}; /* button_context_pitchscreen */
@@ -274,6 +276,8 @@ static const struct button_mapping remote_button_context_pitchscreen[] = {
274 { ACTION_PS_NUDGE_RIGHT, BUTTON_RC_FF, BUTTON_NONE }, 276 { ACTION_PS_NUDGE_RIGHT, BUTTON_RC_FF, BUTTON_NONE },
275 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RC_FF|BUTTON_REL, BUTTON_NONE }, 277 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RC_FF|BUTTON_REL, BUTTON_NONE },
276 { ACTION_PS_RESET, BUTTON_RC_PLAY, BUTTON_NONE }, 278 { ACTION_PS_RESET, BUTTON_RC_PLAY, BUTTON_NONE },
279 { ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE },
280 { ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
277 281
278 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 282 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
279}; /* button_context_pitchscreen */ 283}; /* button_context_pitchscreen */
diff --git a/apps/keymaps/keymap-h1x0_h3x0.c b/apps/keymaps/keymap-h1x0_h3x0.c
index e4eb6c8168..cd18bf0041 100644
--- a/apps/keymaps/keymap-h1x0_h3x0.c
+++ b/apps/keymaps/keymap-h1x0_h3x0.c
@@ -230,6 +230,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
230 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, 230 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
231 { ACTION_PS_EXIT, BUTTON_ON, BUTTON_NONE }, 231 { ACTION_PS_EXIT, BUTTON_ON, BUTTON_NONE },
232 { ACTION_PS_EXIT, BUTTON_OFF, BUTTON_NONE }, 232 { ACTION_PS_EXIT, BUTTON_OFF, BUTTON_NONE },
233 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
234 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
233 235
234 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 236 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
235}; /* button_context_pitchcreen */ 237}; /* button_context_pitchcreen */
@@ -561,6 +563,8 @@ static const struct button_mapping button_context_pitchscreen_nonlcdremote[] =
561 { ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE }, 563 { ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE },
562 { ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE }, 564 { ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE },
563 { ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE }, 565 { ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE },
566 { ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT,BUTTON_NONE },
567 { ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
564 568
565 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 569 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
566}; /* button_context_pitchcreen */ 570}; /* button_context_pitchcreen */
@@ -577,6 +581,8 @@ static const struct button_mapping button_context_pitchscreen_h100lcdremote[] =
577 { ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE }, 581 { ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE },
578 { ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE }, 582 { ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE },
579 { ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE }, 583 { ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE },
584 { ACTION_PS_SLOWER, BUTTON_RC_SOURCE|BUTTON_REPEAT, BUTTON_NONE },
585 { ACTION_PS_FASTER, BUTTON_RC_BITRATE|BUTTON_REPEAT, BUTTON_NONE },
580 586
581 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 587 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
582}; 588};
@@ -593,6 +599,8 @@ static const struct button_mapping button_context_pitchscreen_h300lcdremote[] =
593 { ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE }, 599 { ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE },
594 { ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE }, 600 { ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE },
595 { ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE }, 601 { ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE },
602 { ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT,BUTTON_NONE },
603 { ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
596 604
597 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 605 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
598}; 606};
diff --git a/apps/keymaps/keymap-hdd1630.c b/apps/keymaps/keymap-hdd1630.c
index deb77307b8..86a6a58797 100644
--- a/apps/keymaps/keymap-hdd1630.c
+++ b/apps/keymaps/keymap-hdd1630.c
@@ -238,8 +238,10 @@ static const struct button_mapping button_context_pitchscreen[] = {
238 { ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE }, 238 { ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
239 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE }, 239 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
240 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE }, 240 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
241 { ACTION_PS_RESET, BUTTON_VIEW, BUTTON_NONE }, 241 { ACTION_PS_RESET, BUTTON_VIEW, BUTTON_NONE },
242 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, 242 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
243 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
244 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
243 245
244 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 246 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
245}; /* button_context_pitchcreen */ 247}; /* button_context_pitchcreen */
diff --git a/apps/keymaps/keymap-iaudio67.c b/apps/keymaps/keymap-iaudio67.c
index b0dac93386..a1eeb08886 100644
--- a/apps/keymaps/keymap-iaudio67.c
+++ b/apps/keymaps/keymap-iaudio67.c
@@ -197,6 +197,10 @@ static const struct button_mapping button_context_pitchscreen[] = {
197 197
198 { ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE }, 198 { ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE },
199 { ACTION_PS_EXIT, BUTTON_MENU, BUTTON_NONE }, 199 { ACTION_PS_EXIT, BUTTON_MENU, BUTTON_NONE },
200
201 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
202 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
203
200#endif 204#endif
201 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 205 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
202}; /* button_context_pitchscreen */ 206}; /* button_context_pitchscreen */
diff --git a/apps/keymaps/keymap-ifp7xx.c b/apps/keymaps/keymap-ifp7xx.c
index 06631438bc..3b09df49fc 100644
--- a/apps/keymaps/keymap-ifp7xx.c
+++ b/apps/keymaps/keymap-ifp7xx.c
@@ -132,6 +132,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
132 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE }, 132 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
133 { ACTION_PS_RESET, BUTTON_MODE, BUTTON_NONE }, 133 { ACTION_PS_RESET, BUTTON_MODE, BUTTON_NONE },
134 { ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE }, 134 { ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE },
135 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
136 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
135 137
136 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 138 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
137}; /* button_context_pitchcreen */ 139}; /* button_context_pitchcreen */
diff --git a/apps/keymaps/keymap-ipod.c b/apps/keymaps/keymap-ipod.c
index 65c5703451..8170e8c7e1 100644
--- a/apps/keymaps/keymap-ipod.c
+++ b/apps/keymaps/keymap-ipod.c
@@ -146,6 +146,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
146 { ACTION_PS_TOGGLE_MODE, BUTTON_PLAY, BUTTON_NONE }, 146 { ACTION_PS_TOGGLE_MODE, BUTTON_PLAY, BUTTON_NONE },
147 { ACTION_PS_RESET, BUTTON_MENU, BUTTON_NONE }, 147 { ACTION_PS_RESET, BUTTON_MENU, BUTTON_NONE },
148 { ACTION_PS_EXIT, BUTTON_SELECT, BUTTON_NONE }, 148 { ACTION_PS_EXIT, BUTTON_SELECT, BUTTON_NONE },
149 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
150 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
149 151
150 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 152 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
151}; /* button_context_pitchscreen */ 153}; /* button_context_pitchscreen */
diff --git a/apps/keymaps/keymap-logikdax.c b/apps/keymaps/keymap-logikdax.c
index fdf51e1652..cda6107b45 100644
--- a/apps/keymaps/keymap-logikdax.c
+++ b/apps/keymaps/keymap-logikdax.c
@@ -190,6 +190,9 @@ static const struct button_mapping button_context_pitchscreen[] = {
190 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, 190 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
191 { ACTION_PS_EXIT, BUTTON_MODE, BUTTON_NONE }, 191 { ACTION_PS_EXIT, BUTTON_MODE, BUTTON_NONE },
192 192
193 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
194 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
195
193 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 196 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
194}; /* button_context_pitchscreen */ 197}; /* button_context_pitchscreen */
195 198
diff --git a/apps/keymaps/keymap-m200.c b/apps/keymaps/keymap-m200.c
index 86144c0b0c..8318bd42e7 100644
--- a/apps/keymaps/keymap-m200.c
+++ b/apps/keymaps/keymap-m200.c
@@ -207,6 +207,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
207 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, 207 { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
208 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, 208 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
209 209
210 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
211 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
210 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), 212 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
211}; /* button_context_pitchscreen */ 213}; /* button_context_pitchscreen */
212 214
diff --git a/apps/keymaps/keymap-m3.c b/apps/keymaps/keymap-m3.c
index 4da6855644..6b68f1f881 100644
--- a/apps/keymaps/keymap-m3.c
+++ b/apps/keymaps/keymap-m3.c
@@ -162,6 +162,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
162 { ACTION_PS_TOGGLE_MODE, BUTTON_MODE, BUTTON_NONE }, 162 { ACTION_PS_TOGGLE_MODE, BUTTON_MODE, BUTTON_NONE },
163 { ACTION_PS_RESET, BUTTON_REC, BUTTON_NONE }, 163 { ACTION_PS_RESET, BUTTON_REC, BUTTON_NONE },
164 { ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE }, 164 { ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE },
165 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
166 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
165 167
166 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 168 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
167}; /* button_context_pitchscreen */ 169}; /* button_context_pitchscreen */
@@ -178,6 +180,8 @@ static const struct button_mapping remote_button_context_pitchscreen[] = {
178 { ACTION_PS_TOGGLE_MODE, BUTTON_RC_MODE, BUTTON_NONE }, 180 { ACTION_PS_TOGGLE_MODE, BUTTON_RC_MODE, BUTTON_NONE },
179 { ACTION_PS_RESET, BUTTON_RC_REC, BUTTON_NONE }, 181 { ACTION_PS_RESET, BUTTON_RC_REC, BUTTON_NONE },
180 { ACTION_PS_EXIT, BUTTON_RC_PLAY, BUTTON_NONE }, 182 { ACTION_PS_EXIT, BUTTON_RC_PLAY, BUTTON_NONE },
183 { ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE },
184 { ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
181 185
182 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 186 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
183}; /* remote_button_context_pitchscreen */ 187}; /* remote_button_context_pitchscreen */
diff --git a/apps/keymaps/keymap-meizu-m6sl.c b/apps/keymaps/keymap-meizu-m6sl.c
index ec19dd63a8..0741f632b7 100644
--- a/apps/keymaps/keymap-meizu-m6sl.c
+++ b/apps/keymaps/keymap-meizu-m6sl.c
@@ -227,6 +227,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
227 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE }, 227 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
228 { ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE }, 228 { ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE },
229 { ACTION_PS_EXIT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE }, 229 { ACTION_PS_EXIT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE },
230 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
231 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
230 232
231 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 233 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
232}; /* button_context_pitchcreen */ 234}; /* button_context_pitchcreen */
diff --git a/apps/keymaps/keymap-mr100.c b/apps/keymaps/keymap-mr100.c
index f9bb8c2419..4f178b7793 100644
--- a/apps/keymaps/keymap-mr100.c
+++ b/apps/keymaps/keymap-mr100.c
@@ -237,6 +237,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
237 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE }, 237 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
238 { ACTION_PS_RESET, BUTTON_DISPLAY, BUTTON_NONE }, 238 { ACTION_PS_RESET, BUTTON_DISPLAY, BUTTON_NONE },
239 { ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE }, 239 { ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE },
240 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
241 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
240 242
241 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 243 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
242}; /* button_context_pitchcreen */ 244}; /* button_context_pitchcreen */
diff --git a/apps/keymaps/keymap-sa9200.c b/apps/keymaps/keymap-sa9200.c
index 3091ec6156..c77f3e9bb4 100644
--- a/apps/keymaps/keymap-sa9200.c
+++ b/apps/keymaps/keymap-sa9200.c
@@ -240,6 +240,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
240 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE }, 240 { ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
241 { ACTION_PS_RESET, BUTTON_RIGHT, BUTTON_NONE }, 241 { ACTION_PS_RESET, BUTTON_RIGHT, BUTTON_NONE },
242 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, 242 { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
243 { ACTION_PS_SLOWER, BUTTON_REW|BUTTON_REPEAT, BUTTON_NONE },
244 { ACTION_PS_FASTER, BUTTON_FFWD|BUTTON_REPEAT, BUTTON_NONE },
243 245
244 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 246 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
245}; /* button_context_pitchcreen */ 247}; /* button_context_pitchcreen */
diff --git a/apps/keymaps/keymap-touchscreen.c b/apps/keymaps/keymap-touchscreen.c
index 70009794a5..b285446546 100644
--- a/apps/keymaps/keymap-touchscreen.c
+++ b/apps/keymaps/keymap-touchscreen.c
@@ -206,6 +206,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
206 { ACTION_PS_TOGGLE_MODE, BUTTON_BOTTOMRIGHT, BUTTON_NONE }, 206 { ACTION_PS_TOGGLE_MODE, BUTTON_BOTTOMRIGHT, BUTTON_NONE },
207 { ACTION_PS_RESET, BUTTON_CENTER, BUTTON_NONE }, 207 { ACTION_PS_RESET, BUTTON_CENTER, BUTTON_NONE },
208 { ACTION_PS_EXIT, BUTTON_TOPLEFT, BUTTON_NONE }, 208 { ACTION_PS_EXIT, BUTTON_TOPLEFT, BUTTON_NONE },
209 { ACTION_PS_SLOWER, BUTTON_MIDLEFT|BUTTON_REPEAT, BUTTON_NONE },
210 { ACTION_PS_FASTER, BUTTON_MIDRIGHT|BUTTON_REPEAT, BUTTON_NONE },
209 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM2|CONTEXT_PITCHSCREEN) 211 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM2|CONTEXT_PITCHSCREEN)
210}; /* button_context_pitchcreen */ 212}; /* button_context_pitchcreen */
211 213
diff --git a/apps/keymaps/keymap-x5.c b/apps/keymaps/keymap-x5.c
index 9495bf3306..4401f790d4 100644
--- a/apps/keymaps/keymap-x5.c
+++ b/apps/keymaps/keymap-x5.c
@@ -151,6 +151,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
151 { ACTION_PS_TOGGLE_MODE, BUTTON_SELECT, BUTTON_NONE }, 151 { ACTION_PS_TOGGLE_MODE, BUTTON_SELECT, BUTTON_NONE },
152 { ACTION_PS_RESET, BUTTON_POWER, BUTTON_NONE }, 152 { ACTION_PS_RESET, BUTTON_POWER, BUTTON_NONE },
153 { ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE }, 153 { ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE },
154 { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
155 { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
154 156
155 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 157 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
156}; /* button_context_pitchscreen */ 158}; /* button_context_pitchscreen */
@@ -166,6 +168,8 @@ static const struct button_mapping remote_button_context_pitchscreen[] = {
166 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RC_FF|BUTTON_REL, BUTTON_NONE }, 168 { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RC_FF|BUTTON_REL, BUTTON_NONE },
167 { ACTION_PS_RESET, BUTTON_RC_MODE, BUTTON_NONE }, 169 { ACTION_PS_RESET, BUTTON_RC_MODE, BUTTON_NONE },
168 { ACTION_PS_EXIT, BUTTON_RC_PLAY, BUTTON_NONE }, 170 { ACTION_PS_EXIT, BUTTON_RC_PLAY, BUTTON_NONE },
171 { ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE },
172 { ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
169 173
170 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 174 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
171}; /* remote_button_context_pitchscreen */ 175}; /* remote_button_context_pitchscreen */
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index ddddce328a..09cbdbfc09 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12468,3 +12468,37 @@
12468 *: "Prevent Track Skipping" 12468 *: "Prevent Track Skipping"
12469 </voice> 12469 </voice>
12470</phrase> 12470</phrase>
12471<phrase>
12472 id: LANG_TIMESTRETCH
12473 desc: timestretch enable
12474 user: core
12475 <source>
12476 *: none
12477 swcodec: "Timestretch"
12478 </source>
12479 <dest>
12480 *: none
12481 swcodec: "Timestretch"
12482 </dest>
12483 <voice>
12484 *: none
12485 swcodec: "Timestretch"
12486 </voice>
12487</phrase>
12488<phrase>
12489 id: LANG_SPEED
12490 desc: timestretch speed
12491 user: core
12492 <source>
12493 *: none
12494 swcodec: "Speed"
12495 </source>
12496 <dest>
12497 *: none
12498 swcodec: "Speed"
12499 </dest>
12500 <voice>
12501 *: none
12502 swcodec: "Speed"
12503 </voice>
12504</phrase>
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index 70730a1e5a..bfba8171da 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -1,4 +1,3 @@
1
2/*************************************************************************** 1/***************************************************************************
3 * __________ __ ___. 2 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
@@ -32,6 +31,9 @@
32#include "eq_menu.h" 31#include "eq_menu.h"
33#include "exported_menus.h" 32#include "exported_menus.h"
34#include "menu_common.h" 33#include "menu_common.h"
34#include "splash.h"
35#include "kernel.h"
36#include "dsp.h"
35 37
36/***********************************/ 38/***********************************/
37/* SOUND MENU */ 39/* SOUND MENU */
@@ -57,14 +59,14 @@ MENUITEM_SETTING(treble, &global_settings.treble,
57MENUITEM_SETTING(treble_cutoff, &global_settings.treble_cutoff, NULL); 59MENUITEM_SETTING(treble_cutoff, &global_settings.treble_cutoff, NULL);
58#endif 60#endif
59MENUITEM_SETTING(balance, &global_settings.balance, NULL); 61MENUITEM_SETTING(balance, &global_settings.balance, NULL);
60MENUITEM_SETTING(channel_config, &global_settings.channel_config, 62MENUITEM_SETTING(channel_config, &global_settings.channel_config,
61#if CONFIG_CODEC == SWCODEC 63#if CONFIG_CODEC == SWCODEC
62 lowlatency_callback 64 lowlatency_callback
63#else 65#else
64 NULL 66 NULL
65#endif 67#endif
66); 68);
67MENUITEM_SETTING(stereo_width, &global_settings.stereo_width, 69MENUITEM_SETTING(stereo_width, &global_settings.stereo_width,
68#if CONFIG_CODEC == SWCODEC 70#if CONFIG_CODEC == SWCODEC
69 lowlatency_callback 71 lowlatency_callback
70#else 72#else
@@ -86,7 +88,21 @@ MENUITEM_SETTING(stereo_width, &global_settings.stereo_width,
86 MAKE_MENU(crossfeed_menu,ID2P(LANG_CROSSFEED), NULL, Icon_NOICON, 88 MAKE_MENU(crossfeed_menu,ID2P(LANG_CROSSFEED), NULL, Icon_NOICON,
87 &crossfeed, &crossfeed_direct_gain, &crossfeed_cross_gain, 89 &crossfeed, &crossfeed_direct_gain, &crossfeed_cross_gain,
88 &crossfeed_hf_attenuation, &crossfeed_hf_cutoff); 90 &crossfeed_hf_attenuation, &crossfeed_hf_cutoff);
89 91
92static int timestretch_callback(int action,const struct menu_item_ex *this_item)
93{
94 switch (action)
95 {
96 case ACTION_EXIT_MENUITEM: /* on exit */
97 if (global_settings.timestretch_enabled && !dsp_timestretch_enabled())
98 splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
99 break;
100 }
101 lowlatency_callback(action, this_item);
102 return action;
103}
104 MENUITEM_SETTING(timestretch_enabled,
105 &global_settings.timestretch_enabled, timestretch_callback);
90 MENUITEM_SETTING(dithering_enabled, 106 MENUITEM_SETTING(dithering_enabled,
91 &global_settings.dithering_enabled, lowlatency_callback); 107 &global_settings.dithering_enabled, lowlatency_callback);
92#endif 108#endif
@@ -120,7 +136,8 @@ MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, Icon_Audio,
120#endif 136#endif
121 &balance,&channel_config,&stereo_width 137 &balance,&channel_config,&stereo_width
122#if CONFIG_CODEC == SWCODEC 138#if CONFIG_CODEC == SWCODEC
123 ,&crossfeed_menu, &equalizer_menu, &dithering_enabled 139 ,&crossfeed_menu, &equalizer_menu, &dithering_enabled
140 ,&timestretch_enabled
124#endif 141#endif
125#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 142#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
126 ,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength 143 ,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength
diff --git a/apps/settings.c b/apps/settings.c
index 6652141f9c..9594bd164e 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -936,6 +936,7 @@ void settings_apply(bool read_disk)
936 } 936 }
937 937
938 dsp_dither_enable(global_settings.dithering_enabled); 938 dsp_dither_enable(global_settings.dithering_enabled);
939 dsp_timestretch_enable(global_settings.timestretch_enabled);
939#endif 940#endif
940 941
941#ifdef HAVE_SPDIF_POWER 942#ifdef HAVE_SPDIF_POWER
diff --git a/apps/settings.h b/apps/settings.h
index 3b13ff8e6b..6ccaeed92e 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -374,6 +374,7 @@ struct user_settings
374 int keyclick; /* keyclick volume */ 374 int keyclick; /* keyclick volume */
375 int keyclick_repeats; /* keyclick on repeats */ 375 int keyclick_repeats; /* keyclick on repeats */
376 bool dithering_enabled; 376 bool dithering_enabled;
377 bool timestretch_enabled;
377#endif /* CONFIG_CODEC == SWCODEC */ 378#endif /* CONFIG_CODEC == SWCODEC */
378 379
379#ifdef HAVE_RECORDING 380#ifdef HAVE_RECORDING
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 0485ec4ab3..74e2cab3cd 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1181,6 +1181,10 @@ const struct settings_list settings[] = {
1181 /* dithering */ 1181 /* dithering */
1182 OFFON_SETTING(F_SOUNDSETTING, dithering_enabled, LANG_DITHERING, false, 1182 OFFON_SETTING(F_SOUNDSETTING, dithering_enabled, LANG_DITHERING, false,
1183 "dithering enabled", dsp_dither_enable), 1183 "dithering enabled", dsp_dither_enable),
1184
1185 /* timestretch */
1186 OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false,
1187 "timestretch enabled", dsp_timestretch_enable),
1184#endif 1188#endif
1185#ifdef HAVE_WM8758 1189#ifdef HAVE_WM8758
1186 SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF, 1190 SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF,
diff --git a/apps/tdspeed.c b/apps/tdspeed.c
new file mode 100644
index 0000000000..67f749f6c3
--- /dev/null
+++ b/apps/tdspeed.c
@@ -0,0 +1,319 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Nicolas Pitre <nico@cam.org>
11 * Copyright (C) 2006-2007 by Stéphane Doyon <s.doyon@videotron.ca>
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22
23#include <inttypes.h>
24#include <stddef.h>
25#include <stdio.h>
26#include <string.h>
27#include "buffer.h"
28#include "debug.h"
29#include "system.h"
30#include "tdspeed.h"
31
32#define assert(cond)
33
34#define MIN_RATE 8000
35#define MAX_RATE 48000 /* double buffer for double rate */
36#define MINFREQ 100
37
38#define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */
39
40struct tdspeed_state_s
41{
42 bool stereo;
43 int32_t shift_max; /* maximum displacement on a frame */
44 int32_t src_step; /* source window pace */
45 int32_t dst_step; /* destination window pace */
46 int32_t dst_order; /* power of two for dst_step */
47 int32_t ovl_shift; /* overlap buffer frame shift */
48 int32_t ovl_size; /* overlap buffer used size */
49 int32_t ovl_space; /* overlap buffer size */
50 int32_t *ovl_buff[2]; /* overlap buffer */
51};
52static struct tdspeed_state_s tdspeed_state;
53
54static int32_t *overlap_buffer[2] = { NULL, NULL };
55static int32_t *outbuf[2] = { NULL, NULL };
56
57bool tdspeed_init(int samplerate, bool stereo, int factor)
58{
59 struct tdspeed_state_s *st = &tdspeed_state;
60 int src_frame_sz;
61
62 /* Allocate buffers */
63 if (overlap_buffer[0] == NULL)
64 overlap_buffer[0] = (int32_t *) buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t));
65 if (overlap_buffer[1] == NULL)
66 overlap_buffer[1] = (int32_t *) buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t));
67 if (outbuf[0] == NULL)
68 outbuf[0] = (int32_t *) buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t));
69 if (outbuf[1] == NULL)
70 outbuf[1] = (int32_t *) buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t));
71
72 /* Check parameters */
73 if (factor == 100)
74 return false;
75 if (samplerate < MIN_RATE || samplerate > MAX_RATE)
76 return false;
77 if (factor < SPEED_MIN || factor > SPEED_MAX)
78 return false;
79
80 st->stereo = stereo;
81 st->dst_step = samplerate / MINFREQ;
82
83 if (factor > 100)
84 st->dst_step = st->dst_step * 100 / factor;
85 st->dst_order = 1;
86
87 while (st->dst_step >>= 1)
88 st->dst_order++;
89 st->dst_step = (1 << st->dst_order);
90 st->src_step = st->dst_step * factor / 100;
91 st->shift_max = (st->dst_step > st->src_step) ? st->dst_step : st->src_step;
92
93 src_frame_sz = st->shift_max + st->dst_step;
94 if (st->dst_step > st->src_step)
95 src_frame_sz += st->dst_step - st->src_step;
96 st->ovl_space = ((src_frame_sz - 2)/st->src_step) * st->src_step
97 + src_frame_sz;
98 if (st->src_step > st->dst_step)
99 st->ovl_space += 2*st->src_step - st->dst_step;
100
101 if (st->ovl_space > FIXED_BUFSIZE)
102 st->ovl_space = FIXED_BUFSIZE;
103
104 st->ovl_size = 0;
105 st->ovl_shift = 0;
106
107 st->ovl_buff[0] = overlap_buffer[0];
108 if (stereo)
109 st->ovl_buff[1] = overlap_buffer[1];
110 else
111 st->ovl_buff[1] = st->ovl_buff[0];
112
113 return true;
114}
115
116static int tdspeed_apply(int32_t *buf_out[2], int32_t *buf_in[2],
117 int data_len, int last, int out_size)
118/* data_len in samples */
119{
120 struct tdspeed_state_s *st = &tdspeed_state;
121 int32_t *curr, *prev, *dest[2], *d;
122 int32_t i, j, next_frame, prev_frame, shift, src_frame_sz;
123 bool stereo = buf_in[0] != buf_in[1];
124 assert(stereo == st->stereo);
125
126 src_frame_sz = st->shift_max + st->dst_step;
127 if (st->dst_step > st->src_step)
128 src_frame_sz += st->dst_step - st->src_step;
129
130 /* deal with overlap data first, if any */
131 if (st->ovl_size)
132 {
133 int32_t have, copy, steps;
134 have = st->ovl_size;
135 if (st->ovl_shift > 0)
136 have -= st->ovl_shift;
137 /* append just enough data to have all of the overlap buffer consumed */
138 steps = (have - 1) / st->src_step;
139 copy = steps * st->src_step + src_frame_sz - have;
140 if (copy < src_frame_sz - st->dst_step)
141 copy += st->src_step; /* one more step to allow for pregap data */
142 if (copy > data_len) copy = data_len;
143 assert(st->ovl_size +copy <= FIXED_BUFSIZE);
144 memcpy(st->ovl_buff[0] + st->ovl_size, buf_in[0],
145 copy * sizeof(int32_t));
146 if (stereo)
147 memcpy(st->ovl_buff[1] + st->ovl_size, buf_in[1],
148 copy * sizeof(int32_t));
149 if (!last && have + copy < src_frame_sz)
150 {
151 /* still not enough to process at least one frame */
152 st->ovl_size += copy;
153 return 0;
154 }
155
156 /* recursively call ourselves to process the overlap buffer */
157 have = st->ovl_size;
158 st->ovl_size = 0;
159 if (copy == data_len)
160 {
161 assert( (have+copy) <= FIXED_BUFSIZE);
162 return tdspeed_apply(buf_out, st->ovl_buff, have+copy, last,
163 out_size);
164 }
165 assert( (have+copy) <= FIXED_BUFSIZE);
166 i = tdspeed_apply(buf_out, st->ovl_buff, have+copy, -1, out_size);
167 dest[0] = buf_out[0] + i;
168 dest[1] = buf_out[1] + i;
169
170 /* readjust pointers to account for data already consumed */
171 next_frame = copy - src_frame_sz + st->src_step;
172 prev_frame = next_frame - st->ovl_shift;
173 }
174 else
175 {
176 dest[0] = buf_out[0];
177 dest[1] = buf_out[1];
178 next_frame = prev_frame = 0;
179 if (st->ovl_shift > 0)
180 next_frame += st->ovl_shift;
181 else
182 prev_frame += -st->ovl_shift;
183 }
184 st->ovl_shift = 0;
185
186 /* process all complete frames */
187 while (data_len - next_frame >= src_frame_sz)
188 {
189 /* find frame overlap by autocorelation */
190 int64_t min_delta = ~(1ll << 63); /* most positive */
191 shift = 0;
192#define INC1 8
193#define INC2 32
194 /* Power of 2 of a 28bit number requires 56bits, can accumulate
195 256times in a 64bit variable. */
196 assert(st->dst_step / INC2 <= 256);
197 assert(next_frame + st->shift_max - 1 + st->dst_step-1 < data_len);
198 assert(prev_frame + st->dst_step - 1 < data_len);
199 for (i = 0; i < st->shift_max; i += INC1)
200 {
201 int64_t delta = 0;
202 curr = buf_in[0] + next_frame + i;
203 prev = buf_in[0] + prev_frame;
204 for (j = 0; j < st->dst_step; j += INC2, curr += INC2, prev += INC2)
205 {
206 int32_t diff = *curr - *prev;
207 delta += (int64_t)diff * diff;
208 if (delta >= min_delta)
209 goto skip;
210 }
211 if (stereo)
212 {
213 curr = buf_in[1] +next_frame + i;
214 prev = buf_in[1] +prev_frame;
215 for (j = 0; j < st->dst_step; j += INC2, curr += INC2, prev += INC2)
216 {
217 int32_t diff = *curr - *prev;
218 delta += (int64_t)diff * diff;
219 if (delta >= min_delta)
220 goto skip;
221 }
222 }
223 min_delta = delta;
224 shift = i;
225skip:;
226 }
227
228 /* overlap fading-out previous frame with fading-in current frame */
229 curr = buf_in[0] + next_frame + shift;
230 prev = buf_in[0] + prev_frame;
231 d = dest[0];
232 assert(next_frame + shift + st->dst_step - 1 < data_len);
233 assert(prev_frame + st->dst_step - 1 < data_len);
234 assert(dest[0] - buf_out[0] + st->dst_step - 1 < out_size);
235 for (i = 0, j = st->dst_step; j; i++, j--)
236 {
237 *d++ = (*curr++ * (int64_t)i
238 + *prev++ * (int64_t)j) >> st->dst_order;
239 }
240 dest[0] = d;
241 if (stereo)
242 {
243 curr = buf_in[1] +next_frame + shift;
244 prev = buf_in[1] +prev_frame;
245 d = dest[1];
246 for (i = 0, j = st->dst_step; j; i++, j--)
247 {
248 assert(d < buf_out[1] +out_size);
249 *d++ = (*curr++ * (int64_t) i
250 + *prev++ * (int64_t) j) >> st->dst_order;
251 }
252 dest[1] = d;
253 }
254
255 /* adjust pointers for next frame */
256 prev_frame = next_frame + shift + st->dst_step;
257 next_frame += st->src_step;
258
259 /* here next_frame - prev_frame = src_step - dst_step - shift */
260 assert(next_frame - prev_frame == st->src_step - st->dst_step - shift);
261 }
262
263 /* now deal with remaining partial frames */
264 if (last == -1)
265 {
266 /* special overlap buffer processing: remember frame shift only */
267 st->ovl_shift = next_frame - prev_frame;
268 }
269 else if (last != 0)
270 {
271 /* last call: purge all remaining data to output buffer */
272 i = data_len -prev_frame;
273 assert(dest[0] +i <= buf_out[0] +out_size);
274 memcpy(dest[0], buf_in[0] +prev_frame, i * sizeof(int32_t));
275 dest[0] += i;
276 if (stereo)
277 {
278 assert(dest[1] +i <= buf_out[1] +out_size);
279 memcpy(dest[1], buf_in[1] +prev_frame, i * sizeof(int32_t));
280 dest[1] += i;
281 }
282 }
283 else
284 {
285 /* preserve remaining data + needed overlap data for next call */
286 st->ovl_shift = next_frame - prev_frame;
287 i = (st->ovl_shift < 0) ? next_frame : prev_frame;
288 st->ovl_size = data_len - i;
289 assert(st->ovl_size <= FIXED_BUFSIZE);
290 memcpy(st->ovl_buff[0], buf_in[0]+i, st->ovl_size * sizeof(int32_t));
291 if (stereo)
292 memcpy(st->ovl_buff[1], buf_in[1]+i, st->ovl_size * sizeof(int32_t));
293 }
294
295 return dest[0] - buf_out[0];
296}
297
298long tdspeed_est_output_size()
299{
300 return TDSPEED_OUTBUFSIZE;
301}
302
303long tdspeed_est_input_size(long size)
304{
305 struct tdspeed_state_s *st = &tdspeed_state;
306 size = (size -st->ovl_size) *st->src_step / st->dst_step;
307 if (size < 0)
308 size = 0;
309 return size;
310}
311
312int tdspeed_doit(int32_t *src[], int count)
313{
314 count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] },
315 src, count, 0, TDSPEED_OUTBUFSIZE);
316 src[0] = outbuf[0];
317 src[1] = outbuf[1];
318 return count;
319}
diff --git a/apps/tdspeed.h b/apps/tdspeed.h
new file mode 100644
index 0000000000..6d7cecdcdf
--- /dev/null
+++ b/apps/tdspeed.h
@@ -0,0 +1,36 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Nicolas Pitre <nico@cam.org>
11 * Copyright (C) 2006-2007 by Stéphane Doyon <s.doyon@videotron.ca>
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22
23#ifndef _TDSPEED_H
24#define _TDSPEED_H
25
26#define TDSPEED_OUTBUFSIZE 4096
27
28bool tdspeed_init(int samplerate, bool stereo, int factor);
29long tdspeed_est_output_size(void);
30long tdspeed_est_input_size(long size);
31int tdspeed_doit(int32_t *src[], int count);
32
33#define SPEED_MAX 250
34#define SPEED_MIN 35
35
36#endif
diff --git a/docs/CREDITS b/docs/CREDITS
index 99ddc3a763..cb491f1ab4 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -468,6 +468,8 @@ Michael Carr
468Eric Clayton 468Eric Clayton
469Marko Pahlke 469Marko Pahlke
470Vytenis Sabelka 470Vytenis Sabelka
471Nicolas Pitre
472Benedikt Goos
471 473
472The libmad team 474The libmad team
473The wavpack team 475The wavpack team
diff --git a/manual/configure_rockbox/sound_settings.tex b/manual/configure_rockbox/sound_settings.tex
index b699eeb821..ef0a0f31a8 100644
--- a/manual/configure_rockbox/sound_settings.tex
+++ b/manual/configure_rockbox/sound_settings.tex
@@ -421,3 +421,13 @@ and not easily noticable.
421Rockbox uses highpass triangular distribution noise as the dithering noise 421Rockbox uses highpass triangular distribution noise as the dithering noise
422source, and a third order noise shaper. 422source, and a third order noise shaper.
423} 423}
424
425\opt{swcodec}{
426\section{Timestretch}
427Enabling \setting{Timestretch} allows you to change the playback speed without it
428affecting the pitch of the recording.
429
430After enabling this feature and rebooting, you can access this via the \setting{Pitch Screen}.
431This function is intended for speech playback and may significantly dilute your listening
432experience with more complex audio.
433}
diff --git a/manual/rockbox_interface/wps.tex b/manual/rockbox_interface/wps.tex
index 6807fd886b..6780999a7d 100644
--- a/manual/rockbox_interface/wps.tex
+++ b/manual/rockbox_interface/wps.tex
@@ -239,44 +239,78 @@ Delete the currently playing file.
239\nopt{player}{ 239\nopt{player}{
240 \subsubsection{\label{sec:pitchscreen}Pitch} 240 \subsubsection{\label{sec:pitchscreen}Pitch}
241 241
242 The \setting{Pitch Screen} allows you to change the pitch and (at the same 242 The \setting{Pitch Screen} allows you to change the pitch and the playback
243 time) the playback speed of your \dap. The pitch value can be adjusted 243 speed of your \dap. The pitch value can be adjusted between 50\% and 200\%.
244 between 50\% and 200\%. 50\% means half the normal playback speed and the 244 50\% means half the normal playback speed and the pitch that is an octave lower
245 pitch that is an octave lower than the normal pitch. 200\% means double 245 than the normal pitch. 200\% means double playback speed and the pitch that
246 playback speed and the pitch that is an octave higher than the normal pitch. 246 is an octave higher than the normal pitch.
247 It is not possible to change the pitch without changing the playback speed and 247
248 vice versa. Changing the pitch can be done in two modes: procentual and 248 \opt{masf}{
249 semitone. Initially (after the \dap{} is switched on), procentual mode 249 Changing the pitch can be done in two modes: procentual and semitone.
250 is active. 250 Initially (after the \dap{} is switched on), procentual mode is active.
251 251
252 \begin{table} 252 \begin{table}
253 \begin{btnmap}{}{} 253 \begin{btnmap}{}{}
254 \ActionPsToggleMode 254 \ActionPsToggleMode
255 & Toggle pitch changing mode \\ 255 & Toggle pitch changing mode \\
256 % 256 %
257 \ActionPsIncSmall{} / \ActionPsDecSmall 257 \ActionPsIncSmall{} / \ActionPsDecSmall
258 & Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone 258 & Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone
259 (in semitone mode)\\ 259 (in semitone mode)\\
260 % 260 %
261 \ActionPsIncBig{} / \ActionPsDecBig 261 \ActionPsIncBig{} / \ActionPsDecBig
262 & Increase / Decrease pitch by 1\% (in procentual mode) or a semitone 262 & Increase / Decrease pitch by 1\% (in procentual mode) or a semitone
263 (in semitone mode)\\ 263 (in semitone mode)\\
264 % 264 %
265 \ActionPsNudgeRight{} / \ActionPsNudgeLeft 265 \ActionPsNudgeLeft{} / \ActionPsNudgeRight
266 & Temporarily increase / decrease pitch by 2.0\% \\ 266 & Temporarily change pitch by 2.0\% (beatmatch) \\
267 % 267 %
268 \ActionPsReset 268 \ActionPsReset
269 & Reset pitch to 100\% \\ 269 & Reset pitch to 100\% \\
270 % 270 %
271 \ActionPsExit 271 \ActionPsExit
272 & Leave the Pitch Screen \\ 272 & Leave the Pitch Screen \\
273 % 273 %
274 \end{btnmap} 274 \end{btnmap}
275 \end{table} 275 \end{table}
276 276
277 \opt{MASCODEC}{
278 \warn{Changing the pitch can cause audible 'Artifacts' or 'Dropouts'.} 277 \warn{Changing the pitch can cause audible 'Artifacts' or 'Dropouts'.}
279 } 278 }
279
280 \opt{swcodec}{
281 Changing the pitch can be done in three modes: procentual, semitone and
282 timestretch. Initially (after the \dap{} is switched on), procentual mode is active.
283
284 Timestretch mode allows you to change the playback speed of your recording without
285 affecting the pitch, and vice versa. To access this you must enable the \setting{Timestretch}
286 option in \setting{Sound Settings} and reboot.
287
288 \begin{table}
289 \begin{btnmap}{}{}
290 \ActionPsToggleMode
291 & Toggle pitch changing mode \\
292 %
293 \ActionPsIncSmall{} / \ActionPsDecSmall
294 & Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone
295 (in semitone mode)\\
296 %
297 \ActionPsIncBig{} / \ActionPsDecBig
298 & Increase / Decrease pitch by 1\% (in procentual mode) or a semitone
299 (in semitone mode)\\
300 %
301 \ActionPsNudgeLeft{} / \ActionPsNudgeRight
302 & Temporarily change pitch by 2.0\% (beatmatch), or modify speed (in timestretch mode) \\
303 %
304 \ActionPsReset
305 & Reset pitch and speed to 100\% \\
306 %
307 \ActionPsExit
308 & Leave the Pitch Screen \\
309 %
310 \end{btnmap}
311 \end{table}
312 }
313
280} 314}
281 315
282%********************QUICKSCREENS*********************************************** 316%********************QUICKSCREENS***********************************************