summaryrefslogtreecommitdiff
path: root/apps/gui/pitchscreen.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/pitchscreen.c')
-rw-r--r--apps/gui/pitchscreen.c681
1 files changed, 552 insertions, 129 deletions
diff --git a/apps/gui/pitchscreen.c b/apps/gui/pitchscreen.c
index 16fac0c3b5..a699d4a7b4 100644
--- a/apps/gui/pitchscreen.c
+++ b/apps/gui/pitchscreen.c
@@ -22,6 +22,7 @@
22#include <stdbool.h> 22#include <stdbool.h>
23#include <string.h> 23#include <string.h>
24#include <stdio.h> 24#include <stdio.h>
25#include <math.h>
25#include "config.h" 26#include "config.h"
26#include "sprintf.h" 27#include "sprintf.h"
27#include "action.h" 28#include "action.h"
@@ -36,24 +37,27 @@
36#include "system.h" 37#include "system.h"
37#include "misc.h" 38#include "misc.h"
38#include "pitchscreen.h" 39#include "pitchscreen.h"
40#include "settings.h"
39#if CONFIG_CODEC == SWCODEC 41#if CONFIG_CODEC == SWCODEC
40#include "tdspeed.h" 42#include "tdspeed.h"
41#endif 43#endif
42 44
45#define ABS(x) ((x) > 0 ? (x) : -(x))
43 46
44#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */ 47#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
45 /* on both sides when drawing */ 48 /* on both sides when drawing */
46 49
47#define PITCH_MAX 2000 50#define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
48#define PITCH_MIN 500 51#define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
49#define PITCH_SMALL_DELTA 1 52#define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
50#define PITCH_BIG_DELTA 10 53#define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
51#define PITCH_NUDGE_DELTA 20 54#define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
52 55
53static bool pitch_mode_semitone = false; 56#define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
54#if CONFIG_CODEC == SWCODEC 57#define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
55static bool pitch_mode_timestretch = false; 58
56#endif 59#define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
60#define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
57 61
58enum 62enum
59{ 63{
@@ -63,25 +67,111 @@ enum
63 PITCH_ITEM_COUNT, 67 PITCH_ITEM_COUNT,
64}; 68};
65 69
70
71/* This is a table of semitone percentage values of the appropriate
72 precision (based on PITCH_SPEED_PRECISION). Note that these are
73 all constant expressions, which will be evaluated at compile time,
74 so no need to worry about how complex the expressions look.
75 That's just to get the precision right.
76
77 I calculated these values, starting from 50, as
78
79 x(n) = 50 * 2^(n/12)
80
81 All that math in each entry simply converts the float constant
82 to an integer equal to PITCH_SPEED_PRECISION times the float value,
83 with as little precision loss as possible.
84*/
85#define SEMITONE_VALUE(x) \
86 ( (int)(((x) + 0.5 / PITCH_SPEED_PRECISION) * PITCH_SPEED_PRECISION) )
87
88static const int semitone_table[] =
89{
90 SEMITONE_VALUE(50),
91 SEMITONE_VALUE(52.97315472),
92 SEMITONE_VALUE(56.12310242),
93 SEMITONE_VALUE(59.46035575),
94 SEMITONE_VALUE(62.99605249),
95 SEMITONE_VALUE(66.74199271),
96 SEMITONE_VALUE(70.71067812),
97 SEMITONE_VALUE(74.91535384),
98 SEMITONE_VALUE(79.3700526 ),
99 SEMITONE_VALUE(84.08964153),
100 SEMITONE_VALUE(89.08987181),
101 SEMITONE_VALUE(94.38743127),
102 SEMITONE_VALUE(100 ),
103 SEMITONE_VALUE(105.9463094),
104 SEMITONE_VALUE(112.2462048),
105 SEMITONE_VALUE(118.9207115),
106 SEMITONE_VALUE(125.992105 ),
107 SEMITONE_VALUE(133.4839854),
108 SEMITONE_VALUE(141.4213562),
109 SEMITONE_VALUE(149.8307077),
110 SEMITONE_VALUE(158.7401052),
111 SEMITONE_VALUE(168.1792831),
112 SEMITONE_VALUE(178.1797436),
113 SEMITONE_VALUE(188.7748625),
114 SEMITONE_VALUE(200 )
115};
116
117#define NUM_SEMITONES ((int)(sizeof(semitone_table) / sizeof(int)))
118#define SEMITONE_START -12
119#define SEMITONE_END 12
120
121/* A table of values for approximating the cent curve with
122 linear interpolation. Multipy the next lowest semitone
123 by this much to find the corresponding cent percentage.
124
125 These values were calculated as
126 x(n) = 100 * 2^(n * 20/1200)
127*/
128
129#define CENT_INTERP(x) \
130 ( (int)(((x) + 0.5 / PITCH_SPEED_PRECISION) * PITCH_SPEED_PRECISION) )
131
132
133static const int cent_interp[] =
134{
135 PITCH_SPEED_100,
136 CENT_INTERP(101.1619440),
137 CENT_INTERP(102.3373892),
138 CENT_INTERP(103.5264924),
139 CENT_INTERP(104.7294123),
140 /* this one's the next semitone but we have it here for convenience */
141 CENT_INTERP(105.9463094),
142};
143
144/* Number of cents between entries in the cent_interp table */
145#define CENT_INTERP_INTERVAL 20
146#define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(int)))
147
148/* This stores whether the pitch and speed are at their own limits */
149/* or that of the timestretching algorithm */
150static bool at_limit = false;
151
66static void pitchscreen_fix_viewports(struct viewport *parent, 152static void pitchscreen_fix_viewports(struct viewport *parent,
67 struct viewport pitch_viewports[PITCH_ITEM_COUNT]) 153 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
68{ 154{
69 int i, height; 155 int i, font_height;
70 height = font_get(parent->font)->height; 156 font_height = font_get(parent->font)->height;
71 for (i = 0; i < PITCH_ITEM_COUNT; i++) 157 for (i = 0; i < PITCH_ITEM_COUNT; i++)
72 { 158 {
73 pitch_viewports[i] = *parent; 159 pitch_viewports[i] = *parent;
74 pitch_viewports[i].height = height; 160 pitch_viewports[i].height = font_height;
75 } 161 }
76 pitch_viewports[PITCH_TOP].y += ICON_BORDER; 162 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
77 163
78 pitch_viewports[PITCH_MID].x += ICON_BORDER; 164 pitch_viewports[PITCH_MID].x += ICON_BORDER;
79 pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2; 165 pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
80 pitch_viewports[PITCH_MID].height = height * 2; 166 pitch_viewports[PITCH_MID].height = parent->height - ICON_BORDER*2
167 - font_height * 2;
168 if(pitch_viewports[PITCH_MID].height < font_height * 2)
169 pitch_viewports[PITCH_MID].height = font_height * 2;
81 pitch_viewports[PITCH_MID].y += parent->height / 2 - 170 pitch_viewports[PITCH_MID].y += parent->height / 2 -
82 pitch_viewports[PITCH_MID].height / 2; 171 pitch_viewports[PITCH_MID].height / 2;
83 172
84 pitch_viewports[PITCH_BOTTOM].y += parent->height - height - ICON_BORDER; 173 pitch_viewports[PITCH_BOTTOM].y += parent->height - font_height
174 - ICON_BORDER;
85} 175}
86 176
87/* must be called before pitchscreen_draw, or within 177/* must be called before pitchscreen_draw, or within
@@ -107,9 +197,9 @@ static void pitchscreen_draw_icons(struct screen *display,
107 197
108static void pitchscreen_draw(struct screen *display, int max_lines, 198static void pitchscreen_draw(struct screen *display, int max_lines,
109 struct viewport pitch_viewports[PITCH_ITEM_COUNT], 199 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
110 int pitch 200 int32_t pitch, int32_t semitone
111#if CONFIG_CODEC == SWCODEC 201#if CONFIG_CODEC == SWCODEC
112 ,int speed 202 ,int32_t speed
113#endif 203#endif
114 ) 204 )
115{ 205{
@@ -123,7 +213,7 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
123 { 213 {
124 /* UP: Pitch Up */ 214 /* UP: Pitch Up */
125 display->set_viewport(&pitch_viewports[PITCH_TOP]); 215 display->set_viewport(&pitch_viewports[PITCH_TOP]);
126 if (pitch_mode_semitone) 216 if (global_settings.pitch_mode_semitone)
127 ptr = str(LANG_PITCH_UP_SEMITONE); 217 ptr = str(LANG_PITCH_UP_SEMITONE);
128 else 218 else
129 ptr = str(LANG_PITCH_UP); 219 ptr = str(LANG_PITCH_UP);
@@ -136,7 +226,7 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
136 226
137 /* DOWN: Pitch Down */ 227 /* DOWN: Pitch Down */
138 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]); 228 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
139 if (pitch_mode_semitone) 229 if (global_settings.pitch_mode_semitone)
140 ptr = str(LANG_PITCH_DOWN_SEMITONE); 230 ptr = str(LANG_PITCH_DOWN_SEMITONE);
141 else 231 else
142 ptr = str(LANG_PITCH_DOWN); 232 ptr = str(LANG_PITCH_DOWN);
@@ -157,55 +247,95 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
157 if ((show_lang_pitch = (max_lines >= 3))) 247 if ((show_lang_pitch = (max_lines >= 3)))
158 { 248 {
159#if CONFIG_CODEC == SWCODEC 249#if CONFIG_CODEC == SWCODEC
160 if (!pitch_mode_timestretch) 250 if(global_settings.pitch_mode_timestretch)
161 { 251 {
162#endif 252 /* Pitch:XXX.X% */
163 /* LANG_PITCH */ 253 if(global_settings.pitch_mode_semitone)
164 snprintf(buf, sizeof(buf), "%s", str(LANG_PITCH)); 254 {
165#if CONFIG_CODEC == SWCODEC 255 snprintf(buf, sizeof(buf), "%s: %s%ld.%02ld", str(LANG_PITCH),
256 semitone >= 0 ? "+" : "-",
257 ABS(semitone / PITCH_SPEED_PRECISION),
258 ABS((semitone % PITCH_SPEED_PRECISION) /
259 (PITCH_SPEED_PRECISION / 100))
260 );
261 }
262 else
263 {
264 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
265 pitch / PITCH_SPEED_PRECISION,
266 (pitch % PITCH_SPEED_PRECISION) /
267 (PITCH_SPEED_PRECISION / 10));
268 }
166 } 269 }
167 else 270 else
271#endif
168 { 272 {
169 /* Pitch:XXX.X% */ 273 /* Rate */
170 snprintf(buf, sizeof(buf), "%s:%d.%d%%", str(LANG_PITCH), 274 snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
171 pitch / 10, pitch % 10);
172 } 275 }
173#endif
174 display->getstringsize(buf, &w, &h); 276 display->getstringsize(buf, &w, &h);
175 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), 277 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
176 0, buf); 278 (pitch_viewports[PITCH_MID].height / 2) - h, buf);
177 if (w > width_used) 279 if (w > width_used)
178 width_used = w; 280 width_used = w;
179 } 281 }
180 282
181 /* Middle section lower line */ 283 /* Middle section lower line */
284 /* "Speed:XXX%" */
182#if CONFIG_CODEC == SWCODEC 285#if CONFIG_CODEC == SWCODEC
183 if (!pitch_mode_timestretch) 286 if(global_settings.pitch_mode_timestretch)
184 { 287 {
185#endif 288 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
186 /* "XXX.X%" */ 289 speed / PITCH_SPEED_PRECISION,
187 snprintf(buf, sizeof(buf), "%d.%d%%", 290 (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
188 pitch / 10, pitch % 10);
189#if CONFIG_CODEC == SWCODEC
190 } 291 }
191 else 292 else
293#endif
192 { 294 {
193 /* "Speed:XXX%" */ 295 if(global_settings.pitch_mode_semitone)
194 snprintf(buf, sizeof(buf), "%s:%d%%", str(LANG_SPEED), 296 {
195 speed / 1000); 297 snprintf(buf, sizeof(buf), "%s%ld.%02ld",
298 semitone >= 0 ? "+" : "-",
299 ABS(semitone / PITCH_SPEED_PRECISION),
300 ABS((semitone % PITCH_SPEED_PRECISION) /
301 (PITCH_SPEED_PRECISION / 100))
302 );
303 }
304 else
305 {
306 snprintf(buf, sizeof(buf), "%ld.%ld%%",
307 pitch / PITCH_SPEED_PRECISION,
308 (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
309 }
196 } 310 }
197#endif 311
198 display->getstringsize(buf, &w, &h); 312 display->getstringsize(buf, &w, &h);
199 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), 313 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
200 (show_lang_pitch ? h : h/2), buf); 314 show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
315 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
316 buf);
201 if (w > width_used) 317 if (w > width_used)
202 width_used = w; 318 width_used = w;
203 319
320 /* "limit" and "timestretch" labels */
321 if (max_lines >= 7)
322 {
323 if(at_limit)
324 {
325 snprintf(buf, sizeof(buf), "%s", str(LANG_STRETCH_LIMIT));
326 display->getstringsize(buf, &w, &h);
327 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
328 (pitch_viewports[PITCH_MID].height / 2) + h, buf);
329 if (w > width_used)
330 width_used = w;
331 }
332 }
333
204 /* Middle section left/right labels */ 334 /* Middle section left/right labels */
205 const char *leftlabel = "-2%"; 335 const char *leftlabel = "-2%";
206 const char *rightlabel = "+2%"; 336 const char *rightlabel = "+2%";
207#if CONFIG_CODEC == SWCODEC 337#if CONFIG_CODEC == SWCODEC
208 if (pitch_mode_timestretch) 338 if (global_settings.pitch_mode_timestretch)
209 { 339 {
210 leftlabel = "<<"; 340 leftlabel = "<<";
211 rightlabel = ">>"; 341 rightlabel = ">>";
@@ -220,37 +350,67 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
220 350
221 if (width_used <= pitch_viewports[PITCH_MID].width) 351 if (width_used <= pitch_viewports[PITCH_MID].width)
222 { 352 {
223 display->putsxy(0, h / 2, leftlabel); 353 display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
224 display->putsxy(pitch_viewports[PITCH_MID].width - w, h /2, rightlabel); 354 leftlabel);
355 display->putsxy((pitch_viewports[PITCH_MID].width - w),
356 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
357 rightlabel);
225 } 358 }
226 display->update_viewport(); 359 display->update_viewport();
227 display->set_viewport(NULL); 360 display->set_viewport(NULL);
228} 361}
229 362
230static int pitch_increase(int pitch, int pitch_delta, bool allow_cutoff) 363static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
364#if CONFIG_CODEC == SWCODEC
365 /* need this to maintain correct pitch/speed caps */
366 , int32_t speed
367#endif
368 )
231{ 369{
232 int new_pitch; 370 int32_t new_pitch;
371#if CONFIG_CODEC == SWCODEC
372 int32_t new_stretch;
373#endif
374 at_limit = false;
233 375
234 if (pitch_delta < 0) 376 if (pitch_delta < 0)
235 { 377 {
236 if (pitch + pitch_delta >= PITCH_MIN) 378 /* for large jumps, snap up to whole numbers */
237 new_pitch = pitch + pitch_delta; 379 if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
238 else 380 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
381 {
382 pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
383 }
384
385 new_pitch = pitch + pitch_delta;
386
387 if (new_pitch < PITCH_MIN)
239 { 388 {
240 if (!allow_cutoff) 389 if (!allow_cutoff)
390 {
241 return pitch; 391 return pitch;
392 }
242 new_pitch = PITCH_MIN; 393 new_pitch = PITCH_MIN;
394 at_limit = true;
243 } 395 }
244 } 396 }
245 else if (pitch_delta > 0) 397 else if (pitch_delta > 0)
246 { 398 {
247 if (pitch + pitch_delta <= PITCH_MAX) 399 /* for large jumps, snap down to whole numbers */
248 new_pitch = pitch + pitch_delta; 400 if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
249 else 401 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
402 {
403 pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
404 }
405
406 new_pitch = pitch + pitch_delta;
407
408 if (new_pitch > PITCH_MAX)
250 { 409 {
251 if (!allow_cutoff) 410 if (!allow_cutoff)
252 return pitch; 411 return pitch;
253 new_pitch = PITCH_MAX; 412 new_pitch = PITCH_MAX;
413 at_limit = true;
254 } 414 }
255 } 415 }
256 else 416 else
@@ -258,47 +418,164 @@ static int pitch_increase(int pitch, int pitch_delta, bool allow_cutoff)
258 /* pitch_delta == 0 -> no real change */ 418 /* pitch_delta == 0 -> no real change */
259 return pitch; 419 return pitch;
260 } 420 }
421#if CONFIG_CODEC == SWCODEC
422 if (dsp_timestretch_available())
423 {
424 /* increase the multiple to increase precision of this calculation */
425 new_stretch = GET_STRETCH(new_pitch, speed);
426 if(new_stretch < STRETCH_MIN)
427 {
428 /* we have to ignore allow_cutoff, because we can't have the */
429 /* stretch go higher than STRETCH_MAX */
430 new_pitch = GET_PITCH(speed, STRETCH_MIN);
431 }
432 else if(new_stretch > STRETCH_MAX)
433 {
434 /* we have to ignore allow_cutoff, because we can't have the */
435 /* stretch go higher than STRETCH_MAX */
436 new_pitch = GET_PITCH(speed, STRETCH_MAX);
437 }
438
439 if(new_stretch >= STRETCH_MAX ||
440 new_stretch <= STRETCH_MIN)
441 {
442 at_limit = true;
443 }
444 }
445#endif
446
261 sound_set_pitch(new_pitch); 447 sound_set_pitch(new_pitch);
262 448
263 return new_pitch; 449 return new_pitch;
264} 450}
265 451
266/* Factor for changing the pitch one half tone up. 452static int32_t get_semitone_from_pitch(int32_t pitch)
267 The exact value is 2^(1/12) = 1.05946309436
268 But we use only integer arithmetics, so take
269 rounded factor multiplied by 10^5=100,000. This is
270 enough to get the same promille values as if we
271 had used floating point (checked with a spread
272 sheet).
273 */
274#define PITCH_SEMITONE_FACTOR 105946L
275
276/* Some helpful constants. K is the scaling factor for SEMITONE.
277 N is for more accurate rounding
278 KN is K * N
279 */
280#define PITCH_K_FCT 100000UL
281#define PITCH_N_FCT 10
282#define PITCH_KN_FCT 1000000UL
283
284static int pitch_increase_semitone(int pitch, bool up)
285{ 453{
286 uint32_t tmp; 454 int semitone = 0;
287 uint32_t round_fct; /* How much to scale down at the end */ 455 int32_t fractional_index = 0;
288 tmp = pitch; 456
289 if (up) 457 while(semitone < NUM_SEMITONES - 1 &&
458 pitch >= semitone_table[semitone + 1])
459 {
460 semitone++;
461 }
462
463
464 /* now find the fractional part */
465 while(pitch > (cent_interp[fractional_index + 1] *
466 semitone_table[semitone] / PITCH_SPEED_100))
290 { 467 {
291 tmp = tmp * PITCH_SEMITONE_FACTOR; 468 /* Check to make sure fractional_index isn't too big */
292 round_fct = PITCH_K_FCT; 469 /* This should never happen. */
470 if(fractional_index >= CENT_INTERP_NUM - 1)
471 {
472 break;
473 }
474 fractional_index++;
475 }
476
477 int32_t semitone_pitch_a = cent_interp[fractional_index] *
478 semitone_table[semitone] /
479 PITCH_SPEED_100;
480 int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
481 semitone_table[semitone] /
482 PITCH_SPEED_100;
483 /* this will be the integer offset from the cent_interp entry */
484 int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
485 (semitone_pitch_b - semitone_pitch_a);
486 semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
487 fractional_index * CENT_INTERP_INTERVAL +
488 semitone_frac_ofs;
489
490 return semitone;
491}
492
493static int32_t get_pitch_from_semitone(int32_t semitone)
494{
495 int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
496
497 /* Find the index into the semitone table */
498 int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
499
500 /* set pitch to the semitone's integer part value */
501 int32_t pitch = semitone_table[semitone_index];
502 /* get the range of the cent modification for future calculation */
503 int32_t pitch_mod_a =
504 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
505 CENT_INTERP_INTERVAL];
506 int32_t pitch_mod_b =
507 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
508 CENT_INTERP_INTERVAL + 1];
509 /* figure out the cent mod amount based on the semitone fractional value */
510 int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
511 (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
512
513 /* modify pitch based on the mod amount we just calculated */
514 return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
515}
516
517static int32_t pitch_increase_semitone(int32_t pitch,
518 int32_t current_semitone,
519 int32_t semitone_delta
520#if CONFIG_CODEC == SWCODEC
521 , int32_t speed
522#endif
523 )
524{
525 int32_t new_semitone = current_semitone;
526
527 /* snap to the delta interval */
528 if(current_semitone % semitone_delta != 0)
529 {
530 if(current_semitone > 0 && semitone_delta > 0)
531 new_semitone += semitone_delta;
532 else if(current_semitone < 0 && semitone_delta < 0)
533 new_semitone += semitone_delta;
534
535 new_semitone -= new_semitone % semitone_delta;
293 } 536 }
294 else 537 else
538 new_semitone += semitone_delta;
539
540 /* clamp the pitch so it doesn't go beyond the pitch limits */
541 if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
542 {
543 new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
544 at_limit = true;
545 }
546 else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
295 { 547 {
296 tmp = (tmp * PITCH_KN_FCT) / PITCH_SEMITONE_FACTOR; 548 new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
297 round_fct = PITCH_N_FCT; 549 at_limit = true;
298 } 550 }
299 /* Scaling down with rounding */ 551
300 tmp = (tmp + round_fct / 2) / round_fct; 552 int32_t new_pitch = get_pitch_from_semitone(new_semitone);
301 return pitch_increase(pitch, tmp - pitch, false); 553
554#if CONFIG_CODEC == SWCODEC
555 int32_t new_stretch = GET_STRETCH(new_pitch, speed);
556
557 /* clamp the pitch so it doesn't go beyond the stretch limits */
558 if( new_stretch > STRETCH_MAX)
559 {
560 new_pitch = GET_PITCH(speed, STRETCH_MAX);
561 new_semitone = get_semitone_from_pitch(new_pitch);
562 at_limit = true;
563 }
564 else if (new_stretch < STRETCH_MIN)
565 {
566 new_pitch = GET_PITCH(speed, STRETCH_MIN);
567 new_semitone = get_semitone_from_pitch(new_pitch);
568 at_limit = true;
569 }
570#endif
571
572 pitch_increase(pitch, new_pitch - pitch, false
573#if CONFIG_CODEC == SWCODEC
574 , speed
575#endif
576 );
577
578 return new_semitone;
302} 579}
303 580
304/* 581/*
@@ -310,13 +587,11 @@ static int pitch_increase_semitone(int pitch, bool up)
310int gui_syncpitchscreen_run(void) 587int gui_syncpitchscreen_run(void)
311{ 588{
312 int button, i; 589 int button, i;
313 int pitch = sound_get_pitch(); 590 int32_t pitch = sound_get_pitch();
314#if CONFIG_CODEC == SWCODEC 591 int32_t semitone;
315 int stretch = dsp_get_timestretch(); 592
316 int speed = stretch * pitch; /* speed to maintain */ 593 int32_t new_pitch;
317#endif 594 int32_t pitch_delta;
318 int new_pitch;
319 int pitch_delta;
320 bool nudged = false; 595 bool nudged = false;
321 bool exit = false; 596 bool exit = false;
322 /* should maybe be passed per parameter later, not needed for now */ 597 /* should maybe be passed per parameter later, not needed for now */
@@ -324,6 +599,31 @@ int gui_syncpitchscreen_run(void)
324 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT]; 599 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
325 int max_lines[NB_SCREENS]; 600 int max_lines[NB_SCREENS];
326 601
602#if CONFIG_CODEC == SWCODEC
603 int32_t new_speed = 0, new_stretch;
604
605 /* the speed variable holds the apparent speed of the playback */
606 int32_t speed;
607 if (dsp_timestretch_available())
608 {
609 speed = GET_SPEED(pitch, dsp_get_timestretch());
610 }
611 else
612 {
613 speed = pitch;
614 }
615
616 /* Figure out whether to be in timestretch mode */
617 if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
618 {
619 global_settings.pitch_mode_timestretch = false;
620 settings_save();
621 }
622#endif
623
624 /* set the semitone index based on the current pitch */
625 semitone = get_semitone_from_pitch(pitch);
626
327 /* initialize pitchscreen vps */ 627 /* initialize pitchscreen vps */
328 FOR_NB_SCREENS(i) 628 FOR_NB_SCREENS(i)
329 { 629 {
@@ -343,49 +643,80 @@ int gui_syncpitchscreen_run(void)
343 { 643 {
344 FOR_NB_SCREENS(i) 644 FOR_NB_SCREENS(i)
345 pitchscreen_draw(&screens[i], max_lines[i], 645 pitchscreen_draw(&screens[i], max_lines[i],
346 pitch_viewports[i], pitch 646 pitch_viewports[i], pitch, semitone
347#if CONFIG_CODEC == SWCODEC 647#if CONFIG_CODEC == SWCODEC
348 , speed 648 , speed
349#endif 649#endif
350 ); 650 );
351 pitch_delta = 0; 651 pitch_delta = 0;
652#if CONFIG_CODEC == SWCODEC
653 new_speed = 0;
654#endif
352 button = get_action(CONTEXT_PITCHSCREEN, HZ); 655 button = get_action(CONTEXT_PITCHSCREEN, HZ);
353 switch (button) 656 switch (button)
354 { 657 {
355 case ACTION_PS_INC_SMALL: 658 case ACTION_PS_INC_SMALL:
356 pitch_delta = PITCH_SMALL_DELTA; 659 if(global_settings.pitch_mode_semitone)
660 pitch_delta = SEMITONE_SMALL_DELTA;
661 else
662 pitch_delta = PITCH_SMALL_DELTA;
357 break; 663 break;
358 664
359 case ACTION_PS_INC_BIG: 665 case ACTION_PS_INC_BIG:
360 pitch_delta = PITCH_BIG_DELTA; 666 if(global_settings.pitch_mode_semitone)
667 pitch_delta = SEMITONE_BIG_DELTA;
668 else
669 pitch_delta = PITCH_BIG_DELTA;
361 break; 670 break;
362 671
363 case ACTION_PS_DEC_SMALL: 672 case ACTION_PS_DEC_SMALL:
364 pitch_delta = -PITCH_SMALL_DELTA; 673 if(global_settings.pitch_mode_semitone)
674 pitch_delta = -SEMITONE_SMALL_DELTA;
675 else
676 pitch_delta = -PITCH_SMALL_DELTA;
365 break; 677 break;
366 678
367 case ACTION_PS_DEC_BIG: 679 case ACTION_PS_DEC_BIG:
368 pitch_delta = -PITCH_BIG_DELTA; 680 if(global_settings.pitch_mode_semitone)
681 pitch_delta = -SEMITONE_BIG_DELTA;
682 else
683 pitch_delta = -PITCH_BIG_DELTA;
369 break; 684 break;
370 685
371 case ACTION_PS_NUDGE_RIGHT: 686 case ACTION_PS_NUDGE_RIGHT:
372#if CONFIG_CODEC == SWCODEC 687#if CONFIG_CODEC == SWCODEC
373 if (!pitch_mode_timestretch) 688 if (!global_settings.pitch_mode_timestretch)
374 { 689 {
375#endif 690#endif
376 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false); 691 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
692#if CONFIG_CODEC == SWCODEC
693 , speed
694#endif
695 );
377 nudged = (new_pitch != pitch); 696 nudged = (new_pitch != pitch);
378 pitch = new_pitch; 697 pitch = new_pitch;
698 semitone = get_semitone_from_pitch(pitch);
699#if CONFIG_CODEC == SWCODEC
700 speed = pitch;
701#endif
379 break; 702 break;
380#if CONFIG_CODEC == SWCODEC 703#if CONFIG_CODEC == SWCODEC
381 } 704 }
705 else
706 {
707 new_speed = speed + SPEED_SMALL_DELTA;
708 at_limit = false;
709 }
710 break;
382 711
383 case ACTION_PS_FASTER: 712 case ACTION_PS_FASTER:
384 if (pitch_mode_timestretch && stretch < STRETCH_MAX) 713 if (global_settings.pitch_mode_timestretch)
385 { 714 {
386 stretch++; 715 new_speed = speed + SPEED_BIG_DELTA;
387 dsp_set_timestretch(stretch); 716 /* snap to whole numbers */
388 speed = stretch * pitch; 717 if(new_speed % PITCH_SPEED_PRECISION != 0)
718 new_speed -= new_speed % PITCH_SPEED_PRECISION;
719 at_limit = false;
389 } 720 }
390 break; 721 break;
391#endif 722#endif
@@ -393,29 +724,53 @@ int gui_syncpitchscreen_run(void)
393 case ACTION_PS_NUDGE_RIGHTOFF: 724 case ACTION_PS_NUDGE_RIGHTOFF:
394 if (nudged) 725 if (nudged)
395 { 726 {
396 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false); 727 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
728#if CONFIG_CODEC == SWCODEC
729 , speed
730#endif
731 );
732#if CONFIG_CODEC == SWCODEC
733 speed = pitch;
734#endif
735 semitone = get_semitone_from_pitch(pitch);
397 nudged = false; 736 nudged = false;
398 } 737 }
399 break; 738 break;
400 739
401 case ACTION_PS_NUDGE_LEFT: 740 case ACTION_PS_NUDGE_LEFT:
402#if CONFIG_CODEC == SWCODEC 741#if CONFIG_CODEC == SWCODEC
403 if (!pitch_mode_timestretch) 742 if (!global_settings.pitch_mode_timestretch)
404 { 743 {
405#endif 744#endif
406 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false); 745 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
746#if CONFIG_CODEC == SWCODEC
747 , speed
748#endif
749 );
407 nudged = (new_pitch != pitch); 750 nudged = (new_pitch != pitch);
408 pitch = new_pitch; 751 pitch = new_pitch;
752 semitone = get_semitone_from_pitch(pitch);
753#if CONFIG_CODEC == SWCODEC
754 speed = pitch;
755#endif
409 break; 756 break;
410#if CONFIG_CODEC == SWCODEC 757#if CONFIG_CODEC == SWCODEC
411 } 758 }
759 else
760 {
761 new_speed = speed - SPEED_SMALL_DELTA;
762 at_limit = false;
763 }
764 break;
412 765
413 case ACTION_PS_SLOWER: 766 case ACTION_PS_SLOWER:
414 if (pitch_mode_timestretch && stretch > STRETCH_MIN) 767 if (global_settings.pitch_mode_timestretch)
415 { 768 {
416 stretch--; 769 new_speed = speed - SPEED_BIG_DELTA;
417 dsp_set_timestretch(stretch); 770 /* snap to whole numbers */
418 speed = stretch * pitch; 771 if(new_speed % PITCH_SPEED_PRECISION != 0)
772 new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
773 at_limit = false;
419 } 774 }
420 break; 775 break;
421#endif 776#endif
@@ -423,27 +778,49 @@ int gui_syncpitchscreen_run(void)
423 case ACTION_PS_NUDGE_LEFTOFF: 778 case ACTION_PS_NUDGE_LEFTOFF:
424 if (nudged) 779 if (nudged)
425 { 780 {
426 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false); 781 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
782#if CONFIG_CODEC == SWCODEC
783 , speed
784#endif
785 );
786#if CONFIG_CODEC == SWCODEC
787 speed = pitch;
788#endif
789 semitone = get_semitone_from_pitch(pitch);
427 nudged = false; 790 nudged = false;
428 } 791 }
429 break; 792 break;
430 793
431 case ACTION_PS_RESET: 794 case ACTION_PS_RESET:
432 pitch = 1000; 795 pitch = PITCH_SPEED_100;
433 sound_set_pitch(pitch); 796 sound_set_pitch(pitch);
434#if CONFIG_CODEC == SWCODEC 797#if CONFIG_CODEC == SWCODEC
435 stretch = 100; 798 speed = PITCH_SPEED_100;
436 dsp_set_timestretch(stretch); 799 if (dsp_timestretch_available())
437 speed = stretch * pitch; 800 {
801 dsp_set_timestretch(PITCH_SPEED_100);
802 at_limit = false;
803 }
438#endif 804#endif
805 semitone = get_semitone_from_pitch(pitch);
439 break; 806 break;
440 807
441 case ACTION_PS_TOGGLE_MODE: 808 case ACTION_PS_TOGGLE_MODE:
809 global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
442#if CONFIG_CODEC == SWCODEC 810#if CONFIG_CODEC == SWCODEC
443 if (dsp_timestretch_available() && pitch_mode_semitone) 811
444 pitch_mode_timestretch = !pitch_mode_timestretch; 812 if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
813 {
814 global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
815 if(!global_settings.pitch_mode_timestretch)
816 {
817 /* no longer in timestretch mode. Reset speed */
818 speed = pitch;
819 dsp_set_timestretch(PITCH_SPEED_100);
820 }
821 }
822 settings_save();
445#endif 823#endif
446 pitch_mode_semitone = !pitch_mode_semitone;
447 break; 824 break;
448 825
449 case ACTION_PS_EXIT: 826 case ACTION_PS_EXIT:
@@ -457,27 +834,73 @@ int gui_syncpitchscreen_run(void)
457 } 834 }
458 if (pitch_delta) 835 if (pitch_delta)
459 { 836 {
460 if (pitch_mode_semitone) 837 if (global_settings.pitch_mode_semitone)
461 pitch = pitch_increase_semitone(pitch, pitch_delta > 0); 838 {
839 semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
840#if CONFIG_CODEC == SWCODEC
841 , speed
842#endif
843 );
844 pitch = get_pitch_from_semitone(semitone);
845 }
462 else 846 else
463 pitch = pitch_increase(pitch, pitch_delta, true); 847 {
848 pitch = pitch_increase(pitch, pitch_delta, true
464#if CONFIG_CODEC == SWCODEC 849#if CONFIG_CODEC == SWCODEC
465 if (pitch_mode_timestretch) 850 , speed
851#endif
852 );
853 semitone = get_semitone_from_pitch(pitch);
854 }
855#if CONFIG_CODEC == SWCODEC
856 if (global_settings.pitch_mode_timestretch)
466 { 857 {
467 /* Set stretch to maintain speed */ 858 /* do this to make sure we properly obey the stretch limits */
468 /* i.e. increase pitch, reduce stretch */ 859 new_speed = speed;
469 int new_stretch = speed / pitch;
470 if (new_stretch >= STRETCH_MIN && new_stretch <= STRETCH_MAX)
471 {
472 stretch = new_stretch;
473 dsp_set_timestretch(stretch);
474 }
475 } 860 }
476 else 861 else
477 speed = stretch * pitch; 862 {
478#endif 863 speed = pitch;
864 }
865#endif
479 } 866 }
480 } 867
868#if CONFIG_CODEC == SWCODEC
869 if(new_speed)
870 {
871 new_stretch = GET_STRETCH(pitch, new_speed);
872
873 /* limit the amount of stretch */
874 if(new_stretch > STRETCH_MAX)
875 {
876 new_stretch = STRETCH_MAX;
877 new_speed = GET_SPEED(pitch, new_stretch);
878 }
879 else if(new_stretch < STRETCH_MIN)
880 {
881 new_stretch = STRETCH_MIN;
882 new_speed = GET_SPEED(pitch, new_stretch);
883 }
884
885 new_stretch = GET_STRETCH(pitch, new_speed);
886 if(new_stretch >= STRETCH_MAX ||
887 new_stretch <= STRETCH_MIN)
888 {
889 at_limit = true;
890 }
891
892 /* set the amount of stretch */
893 dsp_set_timestretch(new_stretch);
894
895 /* update the speed variable with the new speed */
896 speed = new_speed;
897
898 /* Reset new_speed so we only call dsp_set_timestretch */
899 /* when needed */
900 new_speed = 0;
901 }
902#endif
903}
481#if CONFIG_CODEC == SWCODEC 904#if CONFIG_CODEC == SWCODEC
482 pcmbuf_set_low_latency(false); 905 pcmbuf_set_low_latency(false);
483#endif 906#endif