FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / apps / gui / pitchscreen.c
blob78d049be10b951e0a81b51cc033bc0d3151cc680
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 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
160 pitch_viewports[PITCH_MID].x += ICON_BORDER;
161 pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
162 pitch_viewports[PITCH_MID].height = parent->height - ICON_BORDER*2
163 - font_height * 2;
164 if(pitch_viewports[PITCH_MID].height < font_height * 2)
165 pitch_viewports[PITCH_MID].height = font_height * 2;
166 pitch_viewports[PITCH_MID].y += parent->height / 2 -
167 pitch_viewports[PITCH_MID].height / 2;
169 pitch_viewports[PITCH_BOTTOM].y += parent->height - font_height
170 - ICON_BORDER;
173 /* must be called before pitchscreen_draw, or within
174 * since it neither clears nor updates the display */
175 static void pitchscreen_draw_icons(struct screen *display,
176 struct viewport *parent)
178 display->set_viewport(parent);
179 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
180 parent->width/2 - 3,
181 2, 7, 8);
182 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
183 parent->width /2 - 3,
184 parent->height - 10, 7, 8);
185 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
186 parent->width - 10,
187 parent->height /2 - 4, 7, 8);
188 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
190 parent->height /2 - 4, 7, 8);
191 display->update_viewport();
194 static void pitchscreen_draw(struct screen *display, int max_lines,
195 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
196 int32_t pitch, int32_t semitone
197 #if CONFIG_CODEC == SWCODEC
198 ,int32_t speed
199 #endif
202 unsigned char* ptr;
203 char buf[32];
204 int w, h;
205 bool show_lang_pitch;
207 /* "Pitch up/Pitch down" - hide for a small screen */
208 if (max_lines >= 5)
210 /* UP: Pitch Up */
211 display->set_viewport(&pitch_viewports[PITCH_TOP]);
212 if (global_settings.pitch_mode_semitone)
213 ptr = str(LANG_PITCH_UP_SEMITONE);
214 else
215 ptr = str(LANG_PITCH_UP);
216 display->getstringsize(ptr, &w, &h);
217 display->clear_viewport();
218 /* draw text */
219 display->putsxy((pitch_viewports[PITCH_TOP].width / 2) -
220 (w / 2), 0, ptr);
221 display->update_viewport();
223 /* DOWN: Pitch Down */
224 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
225 if (global_settings.pitch_mode_semitone)
226 ptr = str(LANG_PITCH_DOWN_SEMITONE);
227 else
228 ptr = str(LANG_PITCH_DOWN);
229 display->getstringsize(ptr, &w, &h);
230 display->clear_viewport();
231 /* draw text */
232 display->putsxy((pitch_viewports[PITCH_BOTTOM].width / 2) -
233 (w / 2), 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 snprintf(buf, sizeof(buf), "%s", str(LANG_STRETCH_LIMIT));
322 display->getstringsize(buf, &w, &h);
323 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
324 (pitch_viewports[PITCH_MID].height / 2) + h, buf);
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 screens[i].clear_display();
627 viewport_set_defaults(&parent[i], i);
628 max_lines[i] = viewport_get_nb_lines(&parent[i]);
629 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
631 /* also, draw the icons now, it's only needed once */
632 pitchscreen_draw_icons(&screens[i], &parent[i]);
634 #if CONFIG_CODEC == SWCODEC
635 pcmbuf_set_low_latency(true);
636 #endif
638 while (!exit)
640 FOR_NB_SCREENS(i)
641 pitchscreen_draw(&screens[i], max_lines[i],
642 pitch_viewports[i], pitch, semitone
643 #if CONFIG_CODEC == SWCODEC
644 , speed
645 #endif
647 pitch_delta = 0;
648 #if CONFIG_CODEC == SWCODEC
649 new_speed = 0;
650 #endif
651 button = get_action(CONTEXT_PITCHSCREEN, HZ);
652 switch (button)
654 case ACTION_PS_INC_SMALL:
655 if(global_settings.pitch_mode_semitone)
656 pitch_delta = SEMITONE_SMALL_DELTA;
657 else
658 pitch_delta = PITCH_SMALL_DELTA;
659 break;
661 case ACTION_PS_INC_BIG:
662 if(global_settings.pitch_mode_semitone)
663 pitch_delta = SEMITONE_BIG_DELTA;
664 else
665 pitch_delta = PITCH_BIG_DELTA;
666 break;
668 case ACTION_PS_DEC_SMALL:
669 if(global_settings.pitch_mode_semitone)
670 pitch_delta = -SEMITONE_SMALL_DELTA;
671 else
672 pitch_delta = -PITCH_SMALL_DELTA;
673 break;
675 case ACTION_PS_DEC_BIG:
676 if(global_settings.pitch_mode_semitone)
677 pitch_delta = -SEMITONE_BIG_DELTA;
678 else
679 pitch_delta = -PITCH_BIG_DELTA;
680 break;
682 case ACTION_PS_NUDGE_RIGHT:
683 #if CONFIG_CODEC == SWCODEC
684 if (!global_settings.pitch_mode_timestretch)
686 #endif
687 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
688 #if CONFIG_CODEC == SWCODEC
689 , speed
690 #endif
692 nudged = (new_pitch != pitch);
693 pitch = new_pitch;
694 semitone = get_semitone_from_pitch(pitch);
695 #if CONFIG_CODEC == SWCODEC
696 speed = pitch;
697 #endif
698 break;
699 #if CONFIG_CODEC == SWCODEC
701 else
703 new_speed = speed + SPEED_SMALL_DELTA;
704 at_limit = false;
706 break;
708 case ACTION_PS_FASTER:
709 if (global_settings.pitch_mode_timestretch)
711 new_speed = speed + SPEED_BIG_DELTA;
712 /* snap to whole numbers */
713 if(new_speed % PITCH_SPEED_PRECISION != 0)
714 new_speed -= new_speed % PITCH_SPEED_PRECISION;
715 at_limit = false;
717 break;
718 #endif
720 case ACTION_PS_NUDGE_RIGHTOFF:
721 if (nudged)
723 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
724 #if CONFIG_CODEC == SWCODEC
725 , speed
726 #endif
728 #if CONFIG_CODEC == SWCODEC
729 speed = pitch;
730 #endif
731 semitone = get_semitone_from_pitch(pitch);
732 nudged = false;
734 break;
736 case ACTION_PS_NUDGE_LEFT:
737 #if CONFIG_CODEC == SWCODEC
738 if (!global_settings.pitch_mode_timestretch)
740 #endif
741 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
742 #if CONFIG_CODEC == SWCODEC
743 , speed
744 #endif
746 nudged = (new_pitch != pitch);
747 pitch = new_pitch;
748 semitone = get_semitone_from_pitch(pitch);
749 #if CONFIG_CODEC == SWCODEC
750 speed = pitch;
751 #endif
752 break;
753 #if CONFIG_CODEC == SWCODEC
755 else
757 new_speed = speed - SPEED_SMALL_DELTA;
758 at_limit = false;
760 break;
762 case ACTION_PS_SLOWER:
763 if (global_settings.pitch_mode_timestretch)
765 new_speed = speed - SPEED_BIG_DELTA;
766 /* snap to whole numbers */
767 if(new_speed % PITCH_SPEED_PRECISION != 0)
768 new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
769 at_limit = false;
771 break;
772 #endif
774 case ACTION_PS_NUDGE_LEFTOFF:
775 if (nudged)
777 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
778 #if CONFIG_CODEC == SWCODEC
779 , speed
780 #endif
782 #if CONFIG_CODEC == SWCODEC
783 speed = pitch;
784 #endif
785 semitone = get_semitone_from_pitch(pitch);
786 nudged = false;
788 break;
790 case ACTION_PS_RESET:
791 pitch = PITCH_SPEED_100;
792 sound_set_pitch(pitch);
793 #if CONFIG_CODEC == SWCODEC
794 speed = PITCH_SPEED_100;
795 if (dsp_timestretch_available())
797 dsp_set_timestretch(PITCH_SPEED_100);
798 at_limit = false;
800 #endif
801 semitone = get_semitone_from_pitch(pitch);
802 break;
804 case ACTION_PS_TOGGLE_MODE:
805 global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
806 #if CONFIG_CODEC == SWCODEC
808 if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
810 global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
811 if(!global_settings.pitch_mode_timestretch)
813 /* no longer in timestretch mode. Reset speed */
814 speed = pitch;
815 dsp_set_timestretch(PITCH_SPEED_100);
818 settings_save();
819 #endif
820 break;
822 case ACTION_PS_EXIT:
823 exit = true;
824 break;
826 default:
827 if (default_event_handler(button) == SYS_USB_CONNECTED)
828 return 1;
829 break;
831 if (pitch_delta)
833 if (global_settings.pitch_mode_semitone)
835 semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
836 #if CONFIG_CODEC == SWCODEC
837 , speed
838 #endif
840 pitch = get_pitch_from_semitone(semitone);
842 else
844 pitch = pitch_increase(pitch, pitch_delta, true
845 #if CONFIG_CODEC == SWCODEC
846 , speed
847 #endif
849 semitone = get_semitone_from_pitch(pitch);
851 #if CONFIG_CODEC == SWCODEC
852 if (global_settings.pitch_mode_timestretch)
854 /* do this to make sure we properly obey the stretch limits */
855 new_speed = speed;
857 else
859 speed = pitch;
861 #endif
864 #if CONFIG_CODEC == SWCODEC
865 if(new_speed)
867 new_stretch = GET_STRETCH(pitch, new_speed);
869 /* limit the amount of stretch */
870 if(new_stretch > STRETCH_MAX)
872 new_stretch = STRETCH_MAX;
873 new_speed = GET_SPEED(pitch, new_stretch);
875 else if(new_stretch < STRETCH_MIN)
877 new_stretch = STRETCH_MIN;
878 new_speed = GET_SPEED(pitch, new_stretch);
881 new_stretch = GET_STRETCH(pitch, new_speed);
882 if(new_stretch >= STRETCH_MAX ||
883 new_stretch <= STRETCH_MIN)
885 at_limit = true;
888 /* set the amount of stretch */
889 dsp_set_timestretch(new_stretch);
891 /* update the speed variable with the new speed */
892 speed = new_speed;
894 /* Reset new_speed so we only call dsp_set_timestretch */
895 /* when needed */
896 new_speed = 0;
898 #endif
900 #if CONFIG_CODEC == SWCODEC
901 pcmbuf_set_low_latency(false);
902 #endif
903 return 0;