Also fix Debug->View partitions when SECTOR_SIZE!=512
[kugel-rb.git] / apps / gui / pitchscreen.c
blob77b5739be12c1737da3e4a21228708ba4d82c13c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Björn Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdbool.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <math.h>
26 #include <stdlib.h> /* for ABS() */
27 #include "config.h"
28 #include "sprintf.h"
29 #include "action.h"
30 #include "dsp.h"
31 #include "sound.h"
32 #include "pcmbuf.h"
33 #include "lang.h"
34 #include "icons.h"
35 #include "screens.h"
36 #include "viewport.h"
37 #include "font.h"
38 #include "system.h"
39 #include "misc.h"
40 #include "pitchscreen.h"
41 #include "settings.h"
42 #if CONFIG_CODEC == SWCODEC
43 #include "tdspeed.h"
44 #endif
46 #define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
47 /* on both sides when drawing */
49 #define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
50 #define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
51 #define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
52 #define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
53 #define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
55 #define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
56 #define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
58 #define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
59 #define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
61 enum
63 PITCH_TOP = 0,
64 PITCH_MID,
65 PITCH_BOTTOM,
66 PITCH_ITEM_COUNT,
70 /* This is a table of semitone percentage values of the appropriate
71 precision (based on PITCH_SPEED_PRECISION). Note that these are
72 all constant expressions, which will be evaluated at compile time,
73 so no need to worry about how complex the expressions look.
74 That's just to get the precision right.
76 I calculated these values, starting from 50, as
78 x(n) = 50 * 2^(n/12)
80 All that math in each entry simply converts the float constant
81 to an integer equal to PITCH_SPEED_PRECISION times the float value,
82 with as little precision loss as possible (i.e. correctly rounding
83 the last digit).
85 #define TO_INT_WITH_PRECISION(x) \
86 ( (unsigned short)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) )
88 static const unsigned short semitone_table[] =
90 TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */
91 TO_INT_WITH_PRECISION(52.97315472),
92 TO_INT_WITH_PRECISION(56.12310242),
93 TO_INT_WITH_PRECISION(59.46035575),
94 TO_INT_WITH_PRECISION(62.99605249),
95 TO_INT_WITH_PRECISION(66.74199271),
96 TO_INT_WITH_PRECISION(70.71067812),
97 TO_INT_WITH_PRECISION(74.91535384),
98 TO_INT_WITH_PRECISION(79.37005260),
99 TO_INT_WITH_PRECISION(84.08964153),
100 TO_INT_WITH_PRECISION(89.08987181),
101 TO_INT_WITH_PRECISION(94.38743127),
102 TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */
103 TO_INT_WITH_PRECISION(105.9463094),
104 TO_INT_WITH_PRECISION(112.2462048),
105 TO_INT_WITH_PRECISION(118.9207115),
106 TO_INT_WITH_PRECISION(125.9921049),
107 TO_INT_WITH_PRECISION(133.4839854),
108 TO_INT_WITH_PRECISION(141.4213562),
109 TO_INT_WITH_PRECISION(149.8307077),
110 TO_INT_WITH_PRECISION(158.7401052),
111 TO_INT_WITH_PRECISION(168.1792831),
112 TO_INT_WITH_PRECISION(178.1797436),
113 TO_INT_WITH_PRECISION(188.7748625),
114 TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */
117 #define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0])))
118 #define SEMITONE_END (NUM_SEMITONES/2)
119 #define SEMITONE_START (-SEMITONE_END)
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.
125 These values were calculated as
126 x(n) = 100 * 2^(n * 20/1200)
129 static const unsigned short cent_interp[] =
131 TO_INT_WITH_PRECISION(100.0000000),
132 TO_INT_WITH_PRECISION(101.1619440),
133 TO_INT_WITH_PRECISION(102.3373892),
134 TO_INT_WITH_PRECISION(103.5264924),
135 TO_INT_WITH_PRECISION(104.7294123),
136 /* this one's the next semitone but we have it here for convenience */
137 TO_INT_WITH_PRECISION(105.9463094),
140 /* Number of cents between entries in the cent_interp table */
141 #define CENT_INTERP_INTERVAL 20
142 #define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0])))
144 /* This stores whether the pitch and speed are at their own limits */
145 /* or that of the timestretching algorithm */
146 static bool at_limit = false;
148 static void pitchscreen_fix_viewports(struct viewport *parent,
149 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
151 int i, font_height;
152 font_height = font_get(parent->font)->height;
153 for (i = 0; i < PITCH_ITEM_COUNT; i++)
155 pitch_viewports[i] = *parent;
156 pitch_viewports[i].height = font_height;
158 if (i == PITCH_TOP || i == PITCH_BOTTOM)
159 pitch_viewports[i].flags |= VP_FLAG_ALIGN_CENTER;
161 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
163 pitch_viewports[PITCH_MID].x += ICON_BORDER;
164 pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
165 pitch_viewports[PITCH_MID].height = parent->height - ICON_BORDER*2
166 - font_height * 2;
167 if(pitch_viewports[PITCH_MID].height < font_height * 2)
168 pitch_viewports[PITCH_MID].height = font_height * 2;
169 pitch_viewports[PITCH_MID].y += parent->height / 2 -
170 pitch_viewports[PITCH_MID].height / 2;
172 pitch_viewports[PITCH_BOTTOM].y += parent->height - font_height
173 - ICON_BORDER;
176 /* must be called before pitchscreen_draw, or within
177 * since it neither clears nor updates the display */
178 static void pitchscreen_draw_icons(struct screen *display,
179 struct viewport *parent)
181 display->set_viewport(parent);
182 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
183 parent->width/2 - 3,
184 2, 7, 8);
185 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
186 parent->width /2 - 3,
187 parent->height - 10, 7, 8);
188 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
189 parent->width - 10,
190 parent->height /2 - 4, 7, 8);
191 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
193 parent->height /2 - 4, 7, 8);
194 display->update_viewport();
197 static void pitchscreen_draw(struct screen *display, int max_lines,
198 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
199 int32_t pitch, int32_t semitone
200 #if CONFIG_CODEC == SWCODEC
201 ,int32_t speed
202 #endif
205 const char* ptr;
206 char buf[32];
207 int w, h;
208 bool show_lang_pitch;
210 /* "Pitch up/Pitch down" - hide for a small screen,
211 * the text is drawn centered automatically */
212 if (max_lines >= 5)
214 /* UP: Pitch Up */
215 display->set_viewport(&pitch_viewports[PITCH_TOP]);
216 if (global_settings.pitch_mode_semitone)
217 ptr = str(LANG_PITCH_UP_SEMITONE);
218 else
219 ptr = str(LANG_PITCH_UP);
220 display->clear_viewport();
221 /* draw text */
222 display->putsxy(0, 0, ptr);
223 display->update_viewport();
225 /* DOWN: Pitch Down */
226 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
227 if (global_settings.pitch_mode_semitone)
228 ptr = str(LANG_PITCH_DOWN_SEMITONE);
229 else
230 ptr = str(LANG_PITCH_DOWN);
231 display->clear_viewport();
232 /* draw text */
233 display->putsxy(0, 0, ptr);
234 display->update_viewport();
237 /* Middle section */
238 display->set_viewport(&pitch_viewports[PITCH_MID]);
239 display->clear_viewport();
240 int width_used = 0;
242 /* Middle section upper line - hide for a small screen */
243 if ((show_lang_pitch = (max_lines >= 3)))
245 #if CONFIG_CODEC == SWCODEC
246 if(global_settings.pitch_mode_timestretch)
248 /* Pitch:XXX.X% */
249 if(global_settings.pitch_mode_semitone)
251 snprintf(buf, sizeof(buf), "%s: %s%ld.%02ld", str(LANG_PITCH),
252 semitone >= 0 ? "+" : "-",
253 ABS(semitone / PITCH_SPEED_PRECISION),
254 ABS((semitone % PITCH_SPEED_PRECISION) /
255 (PITCH_SPEED_PRECISION / 100))
258 else
260 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
261 pitch / PITCH_SPEED_PRECISION,
262 (pitch % PITCH_SPEED_PRECISION) /
263 (PITCH_SPEED_PRECISION / 10));
266 else
267 #endif
269 /* Rate */
270 snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
272 display->getstringsize(buf, &w, &h);
273 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
274 (pitch_viewports[PITCH_MID].height / 2) - h, buf);
275 if (w > width_used)
276 width_used = w;
279 /* Middle section lower line */
280 /* "Speed:XXX%" */
281 #if CONFIG_CODEC == SWCODEC
282 if(global_settings.pitch_mode_timestretch)
284 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
285 speed / PITCH_SPEED_PRECISION,
286 (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
288 else
289 #endif
291 if(global_settings.pitch_mode_semitone)
293 snprintf(buf, sizeof(buf), "%s%ld.%02ld",
294 semitone >= 0 ? "+" : "-",
295 ABS(semitone / PITCH_SPEED_PRECISION),
296 ABS((semitone % PITCH_SPEED_PRECISION) /
297 (PITCH_SPEED_PRECISION / 100))
300 else
302 snprintf(buf, sizeof(buf), "%ld.%ld%%",
303 pitch / PITCH_SPEED_PRECISION,
304 (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
308 display->getstringsize(buf, &w, &h);
309 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
310 show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
311 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
312 buf);
313 if (w > width_used)
314 width_used = w;
316 /* "limit" and "timestretch" labels */
317 if (max_lines >= 7)
319 if(at_limit)
321 const char * const p = str(LANG_STRETCH_LIMIT);
322 display->getstringsize(p, &w, &h);
323 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
324 (pitch_viewports[PITCH_MID].height / 2) + h, p);
325 if (w > width_used)
326 width_used = w;
330 /* Middle section left/right labels */
331 const char *leftlabel = "-2%";
332 const char *rightlabel = "+2%";
333 #if CONFIG_CODEC == SWCODEC
334 if (global_settings.pitch_mode_timestretch)
336 leftlabel = "<<";
337 rightlabel = ">>";
339 #endif
341 /* Only display if they fit */
342 display->getstringsize(leftlabel, &w, &h);
343 width_used += w;
344 display->getstringsize(rightlabel, &w, &h);
345 width_used += w;
347 if (width_used <= pitch_viewports[PITCH_MID].width)
349 display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
350 leftlabel);
351 display->putsxy((pitch_viewports[PITCH_MID].width - w),
352 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
353 rightlabel);
355 display->update_viewport();
356 display->set_viewport(NULL);
359 static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
360 #if CONFIG_CODEC == SWCODEC
361 /* need this to maintain correct pitch/speed caps */
362 , int32_t speed
363 #endif
366 int32_t new_pitch;
367 #if CONFIG_CODEC == SWCODEC
368 int32_t new_stretch;
369 #endif
370 at_limit = false;
372 if (pitch_delta < 0)
374 /* for large jumps, snap up to whole numbers */
375 if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
376 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
378 pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
381 new_pitch = pitch + pitch_delta;
383 if (new_pitch < PITCH_MIN)
385 if (!allow_cutoff)
387 return pitch;
389 new_pitch = PITCH_MIN;
390 at_limit = true;
393 else if (pitch_delta > 0)
395 /* for large jumps, snap down to whole numbers */
396 if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
397 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
399 pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
402 new_pitch = pitch + pitch_delta;
404 if (new_pitch > PITCH_MAX)
406 if (!allow_cutoff)
407 return pitch;
408 new_pitch = PITCH_MAX;
409 at_limit = true;
412 else
414 /* pitch_delta == 0 -> no real change */
415 return pitch;
417 #if CONFIG_CODEC == SWCODEC
418 if (dsp_timestretch_available())
420 /* increase the multiple to increase precision of this calculation */
421 new_stretch = GET_STRETCH(new_pitch, speed);
422 if(new_stretch < STRETCH_MIN)
424 /* we have to ignore allow_cutoff, because we can't have the */
425 /* stretch go higher than STRETCH_MAX */
426 new_pitch = GET_PITCH(speed, STRETCH_MIN);
428 else if(new_stretch > STRETCH_MAX)
430 /* we have to ignore allow_cutoff, because we can't have the */
431 /* stretch go higher than STRETCH_MAX */
432 new_pitch = GET_PITCH(speed, STRETCH_MAX);
435 if(new_stretch >= STRETCH_MAX ||
436 new_stretch <= STRETCH_MIN)
438 at_limit = true;
441 #endif
443 sound_set_pitch(new_pitch);
445 return new_pitch;
448 static int32_t get_semitone_from_pitch(int32_t pitch)
450 int semitone = 0;
451 int32_t fractional_index = 0;
453 while(semitone < NUM_SEMITONES - 1 &&
454 pitch >= semitone_table[semitone + 1])
456 semitone++;
460 /* now find the fractional part */
461 while(pitch > (cent_interp[fractional_index + 1] *
462 semitone_table[semitone] / PITCH_SPEED_100))
464 /* Check to make sure fractional_index isn't too big */
465 /* This should never happen. */
466 if(fractional_index >= CENT_INTERP_NUM - 1)
468 break;
470 fractional_index++;
473 int32_t semitone_pitch_a = cent_interp[fractional_index] *
474 semitone_table[semitone] /
475 PITCH_SPEED_100;
476 int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
477 semitone_table[semitone] /
478 PITCH_SPEED_100;
479 /* this will be the integer offset from the cent_interp entry */
480 int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
481 (semitone_pitch_b - semitone_pitch_a);
482 semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
483 fractional_index * CENT_INTERP_INTERVAL +
484 semitone_frac_ofs;
486 return semitone;
489 static int32_t get_pitch_from_semitone(int32_t semitone)
491 int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
493 /* Find the index into the semitone table */
494 int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
496 /* set pitch to the semitone's integer part value */
497 int32_t pitch = semitone_table[semitone_index];
498 /* get the range of the cent modification for future calculation */
499 int32_t pitch_mod_a =
500 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
501 CENT_INTERP_INTERVAL];
502 int32_t pitch_mod_b =
503 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
504 CENT_INTERP_INTERVAL + 1];
505 /* figure out the cent mod amount based on the semitone fractional value */
506 int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
507 (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
509 /* modify pitch based on the mod amount we just calculated */
510 return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
513 static int32_t pitch_increase_semitone(int32_t pitch,
514 int32_t current_semitone,
515 int32_t semitone_delta
516 #if CONFIG_CODEC == SWCODEC
517 , int32_t speed
518 #endif
521 int32_t new_semitone = current_semitone;
523 /* snap to the delta interval */
524 if(current_semitone % semitone_delta != 0)
526 if(current_semitone > 0 && semitone_delta > 0)
527 new_semitone += semitone_delta;
528 else if(current_semitone < 0 && semitone_delta < 0)
529 new_semitone += semitone_delta;
531 new_semitone -= new_semitone % semitone_delta;
533 else
534 new_semitone += semitone_delta;
536 /* clamp the pitch so it doesn't go beyond the pitch limits */
537 if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
539 new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
540 at_limit = true;
542 else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
544 new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
545 at_limit = true;
548 int32_t new_pitch = get_pitch_from_semitone(new_semitone);
550 #if CONFIG_CODEC == SWCODEC
551 int32_t new_stretch = GET_STRETCH(new_pitch, speed);
553 /* clamp the pitch so it doesn't go beyond the stretch limits */
554 if( new_stretch > STRETCH_MAX)
556 new_pitch = GET_PITCH(speed, STRETCH_MAX);
557 new_semitone = get_semitone_from_pitch(new_pitch);
558 at_limit = true;
560 else if (new_stretch < STRETCH_MIN)
562 new_pitch = GET_PITCH(speed, STRETCH_MIN);
563 new_semitone = get_semitone_from_pitch(new_pitch);
564 at_limit = true;
566 #endif
568 pitch_increase(pitch, new_pitch - pitch, false
569 #if CONFIG_CODEC == SWCODEC
570 , speed
571 #endif
574 return new_semitone;
578 returns:
579 0 on exit
580 1 if USB was connected
583 int gui_syncpitchscreen_run(void)
585 int button, i;
586 int32_t pitch = sound_get_pitch();
587 int32_t semitone;
589 int32_t new_pitch;
590 int32_t pitch_delta;
591 bool nudged = false;
592 bool exit = false;
593 /* should maybe be passed per parameter later, not needed for now */
594 struct viewport parent[NB_SCREENS];
595 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
596 int max_lines[NB_SCREENS];
598 #if CONFIG_CODEC == SWCODEC
599 int32_t new_speed = 0, new_stretch;
601 /* the speed variable holds the apparent speed of the playback */
602 int32_t speed;
603 if (dsp_timestretch_available())
605 speed = GET_SPEED(pitch, dsp_get_timestretch());
607 else
609 speed = pitch;
612 /* Figure out whether to be in timestretch mode */
613 if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
615 global_settings.pitch_mode_timestretch = false;
616 settings_save();
618 #endif
620 /* set the semitone index based on the current pitch */
621 semitone = get_semitone_from_pitch(pitch);
623 /* initialize pitchscreen vps */
624 FOR_NB_SCREENS(i)
626 viewport_set_defaults(&parent[i], i);
627 max_lines[i] = viewport_get_nb_lines(&parent[i]);
628 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
629 screens[i].set_viewport(&parent[i]);
630 screens[i].clear_viewport();
632 /* also, draw the icons now, it's only needed once */
633 pitchscreen_draw_icons(&screens[i], &parent[i]);
635 #if CONFIG_CODEC == SWCODEC
636 pcmbuf_set_low_latency(true);
637 #endif
639 while (!exit)
641 FOR_NB_SCREENS(i)
642 pitchscreen_draw(&screens[i], max_lines[i],
643 pitch_viewports[i], pitch, semitone
644 #if CONFIG_CODEC == SWCODEC
645 , speed
646 #endif
648 pitch_delta = 0;
649 #if CONFIG_CODEC == SWCODEC
650 new_speed = 0;
651 #endif
652 button = get_action(CONTEXT_PITCHSCREEN, HZ);
653 switch (button)
655 case ACTION_PS_INC_SMALL:
656 if(global_settings.pitch_mode_semitone)
657 pitch_delta = SEMITONE_SMALL_DELTA;
658 else
659 pitch_delta = PITCH_SMALL_DELTA;
660 break;
662 case ACTION_PS_INC_BIG:
663 if(global_settings.pitch_mode_semitone)
664 pitch_delta = SEMITONE_BIG_DELTA;
665 else
666 pitch_delta = PITCH_BIG_DELTA;
667 break;
669 case ACTION_PS_DEC_SMALL:
670 if(global_settings.pitch_mode_semitone)
671 pitch_delta = -SEMITONE_SMALL_DELTA;
672 else
673 pitch_delta = -PITCH_SMALL_DELTA;
674 break;
676 case ACTION_PS_DEC_BIG:
677 if(global_settings.pitch_mode_semitone)
678 pitch_delta = -SEMITONE_BIG_DELTA;
679 else
680 pitch_delta = -PITCH_BIG_DELTA;
681 break;
683 case ACTION_PS_NUDGE_RIGHT:
684 #if CONFIG_CODEC == SWCODEC
685 if (!global_settings.pitch_mode_timestretch)
687 #endif
688 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
689 #if CONFIG_CODEC == SWCODEC
690 , speed
691 #endif
693 nudged = (new_pitch != pitch);
694 pitch = new_pitch;
695 semitone = get_semitone_from_pitch(pitch);
696 #if CONFIG_CODEC == SWCODEC
697 speed = pitch;
698 #endif
699 break;
700 #if CONFIG_CODEC == SWCODEC
702 else
704 new_speed = speed + SPEED_SMALL_DELTA;
705 at_limit = false;
707 break;
709 case ACTION_PS_FASTER:
710 if (global_settings.pitch_mode_timestretch)
712 new_speed = speed + SPEED_BIG_DELTA;
713 /* snap to whole numbers */
714 if(new_speed % PITCH_SPEED_PRECISION != 0)
715 new_speed -= new_speed % PITCH_SPEED_PRECISION;
716 at_limit = false;
718 break;
719 #endif
721 case ACTION_PS_NUDGE_RIGHTOFF:
722 if (nudged)
724 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
725 #if CONFIG_CODEC == SWCODEC
726 , speed
727 #endif
729 #if CONFIG_CODEC == SWCODEC
730 speed = pitch;
731 #endif
732 semitone = get_semitone_from_pitch(pitch);
733 nudged = false;
735 break;
737 case ACTION_PS_NUDGE_LEFT:
738 #if CONFIG_CODEC == SWCODEC
739 if (!global_settings.pitch_mode_timestretch)
741 #endif
742 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
743 #if CONFIG_CODEC == SWCODEC
744 , speed
745 #endif
747 nudged = (new_pitch != pitch);
748 pitch = new_pitch;
749 semitone = get_semitone_from_pitch(pitch);
750 #if CONFIG_CODEC == SWCODEC
751 speed = pitch;
752 #endif
753 break;
754 #if CONFIG_CODEC == SWCODEC
756 else
758 new_speed = speed - SPEED_SMALL_DELTA;
759 at_limit = false;
761 break;
763 case ACTION_PS_SLOWER:
764 if (global_settings.pitch_mode_timestretch)
766 new_speed = speed - SPEED_BIG_DELTA;
767 /* snap to whole numbers */
768 if(new_speed % PITCH_SPEED_PRECISION != 0)
769 new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
770 at_limit = false;
772 break;
773 #endif
775 case ACTION_PS_NUDGE_LEFTOFF:
776 if (nudged)
778 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
779 #if CONFIG_CODEC == SWCODEC
780 , speed
781 #endif
783 #if CONFIG_CODEC == SWCODEC
784 speed = pitch;
785 #endif
786 semitone = get_semitone_from_pitch(pitch);
787 nudged = false;
789 break;
791 case ACTION_PS_RESET:
792 pitch = PITCH_SPEED_100;
793 sound_set_pitch(pitch);
794 #if CONFIG_CODEC == SWCODEC
795 speed = PITCH_SPEED_100;
796 if (dsp_timestretch_available())
798 dsp_set_timestretch(PITCH_SPEED_100);
799 at_limit = false;
801 #endif
802 semitone = get_semitone_from_pitch(pitch);
803 break;
805 case ACTION_PS_TOGGLE_MODE:
806 global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
807 #if CONFIG_CODEC == SWCODEC
809 if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
811 global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
812 if(!global_settings.pitch_mode_timestretch)
814 /* no longer in timestretch mode. Reset speed */
815 speed = pitch;
816 dsp_set_timestretch(PITCH_SPEED_100);
819 settings_save();
820 #endif
821 break;
823 case ACTION_PS_EXIT:
824 exit = true;
825 break;
827 default:
828 if (default_event_handler(button) == SYS_USB_CONNECTED)
829 return 1;
830 break;
832 if (pitch_delta)
834 if (global_settings.pitch_mode_semitone)
836 semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
837 #if CONFIG_CODEC == SWCODEC
838 , speed
839 #endif
841 pitch = get_pitch_from_semitone(semitone);
843 else
845 pitch = pitch_increase(pitch, pitch_delta, true
846 #if CONFIG_CODEC == SWCODEC
847 , speed
848 #endif
850 semitone = get_semitone_from_pitch(pitch);
852 #if CONFIG_CODEC == SWCODEC
853 if (global_settings.pitch_mode_timestretch)
855 /* do this to make sure we properly obey the stretch limits */
856 new_speed = speed;
858 else
860 speed = pitch;
862 #endif
865 #if CONFIG_CODEC == SWCODEC
866 if(new_speed)
868 new_stretch = GET_STRETCH(pitch, new_speed);
870 /* limit the amount of stretch */
871 if(new_stretch > STRETCH_MAX)
873 new_stretch = STRETCH_MAX;
874 new_speed = GET_SPEED(pitch, new_stretch);
876 else if(new_stretch < STRETCH_MIN)
878 new_stretch = STRETCH_MIN;
879 new_speed = GET_SPEED(pitch, new_stretch);
882 new_stretch = GET_STRETCH(pitch, new_speed);
883 if(new_stretch >= STRETCH_MAX ||
884 new_stretch <= STRETCH_MIN)
886 at_limit = true;
889 /* set the amount of stretch */
890 dsp_set_timestretch(new_stretch);
892 /* update the speed variable with the new speed */
893 speed = new_speed;
895 /* Reset new_speed so we only call dsp_set_timestretch */
896 /* when needed */
897 new_speed = 0;
899 #endif
901 #if CONFIG_CODEC == SWCODEC
902 pcmbuf_set_low_latency(false);
903 #endif
904 return 0;