Correctly compute the array size regardless of the element type
[kugel-rb.git] / apps / gui / pitchscreen.c
blob204a1cd35fc3a3e4752924ff08f43aa67e2f77dc
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 "config.h"
27 #include "sprintf.h"
28 #include "action.h"
29 #include "dsp.h"
30 #include "sound.h"
31 #include "pcmbuf.h"
32 #include "lang.h"
33 #include "icons.h"
34 #include "screens.h"
35 #include "viewport.h"
36 #include "font.h"
37 #include "system.h"
38 #include "misc.h"
39 #include "pitchscreen.h"
40 #include "settings.h"
41 #if CONFIG_CODEC == SWCODEC
42 #include "tdspeed.h"
43 #endif
45 #define ABS(x) ((x) > 0 ? (x) : -(x))
47 #define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
48 /* on both sides when drawing */
50 #define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
51 #define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
52 #define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
53 #define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
54 #define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
56 #define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
57 #define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
59 #define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
60 #define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
62 enum
64 PITCH_TOP = 0,
65 PITCH_MID,
66 PITCH_BOTTOM,
67 PITCH_ITEM_COUNT,
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.
77 I calculated these values, starting from 50, as
79 x(n) = 50 * 2^(n/12)
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 (i.e. correctly rounding
84 the last digit).
86 #define TO_INT_WITH_PRECISION(x) \
87 ( (int)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) )
89 static const int semitone_table[] =
91 TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */
92 TO_INT_WITH_PRECISION(52.97315472),
93 TO_INT_WITH_PRECISION(56.12310242),
94 TO_INT_WITH_PRECISION(59.46035575),
95 TO_INT_WITH_PRECISION(62.99605249),
96 TO_INT_WITH_PRECISION(66.74199271),
97 TO_INT_WITH_PRECISION(70.71067812),
98 TO_INT_WITH_PRECISION(74.91535384),
99 TO_INT_WITH_PRECISION(79.37005260),
100 TO_INT_WITH_PRECISION(84.08964153),
101 TO_INT_WITH_PRECISION(89.08987181),
102 TO_INT_WITH_PRECISION(94.38743127),
103 TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */
104 TO_INT_WITH_PRECISION(105.9463094),
105 TO_INT_WITH_PRECISION(112.2462048),
106 TO_INT_WITH_PRECISION(118.9207115),
107 TO_INT_WITH_PRECISION(125.9921049),
108 TO_INT_WITH_PRECISION(133.4839854),
109 TO_INT_WITH_PRECISION(141.4213562),
110 TO_INT_WITH_PRECISION(149.8307077),
111 TO_INT_WITH_PRECISION(158.7401052),
112 TO_INT_WITH_PRECISION(168.1792831),
113 TO_INT_WITH_PRECISION(178.1797436),
114 TO_INT_WITH_PRECISION(188.7748625),
115 TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */
118 #define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0])))
119 #define SEMITONE_END (NUM_SEMITONES/2)
120 #define SEMITONE_START (-SEMITONE_END)
122 /* A table of values for approximating the cent curve with
123 linear interpolation. Multipy the next lowest semitone
124 by this much to find the corresponding cent percentage.
126 These values were calculated as
127 x(n) = 100 * 2^(n * 20/1200)
130 static const int cent_interp[] =
132 TO_INT_WITH_PRECISION(100.0000000),
133 TO_INT_WITH_PRECISION(101.1619440),
134 TO_INT_WITH_PRECISION(102.3373892),
135 TO_INT_WITH_PRECISION(103.5264924),
136 TO_INT_WITH_PRECISION(104.7294123),
137 /* this one's the next semitone but we have it here for convenience */
138 TO_INT_WITH_PRECISION(105.9463094),
141 /* Number of cents between entries in the cent_interp table */
142 #define CENT_INTERP_INTERVAL 20
143 #define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0])))
145 /* This stores whether the pitch and speed are at their own limits */
146 /* or that of the timestretching algorithm */
147 static bool at_limit = false;
149 static void pitchscreen_fix_viewports(struct viewport *parent,
150 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
152 int i, font_height;
153 font_height = font_get(parent->font)->height;
154 for (i = 0; i < PITCH_ITEM_COUNT; i++)
156 pitch_viewports[i] = *parent;
157 pitch_viewports[i].height = font_height;
159 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
161 pitch_viewports[PITCH_MID].x += ICON_BORDER;
162 pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
163 pitch_viewports[PITCH_MID].height = parent->height - ICON_BORDER*2
164 - font_height * 2;
165 if(pitch_viewports[PITCH_MID].height < font_height * 2)
166 pitch_viewports[PITCH_MID].height = font_height * 2;
167 pitch_viewports[PITCH_MID].y += parent->height / 2 -
168 pitch_viewports[PITCH_MID].height / 2;
170 pitch_viewports[PITCH_BOTTOM].y += parent->height - font_height
171 - ICON_BORDER;
174 /* must be called before pitchscreen_draw, or within
175 * since it neither clears nor updates the display */
176 static void pitchscreen_draw_icons(struct screen *display,
177 struct viewport *parent)
179 display->set_viewport(parent);
180 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
181 parent->width/2 - 3,
182 2, 7, 8);
183 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
184 parent->width /2 - 3,
185 parent->height - 10, 7, 8);
186 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
187 parent->width - 10,
188 parent->height /2 - 4, 7, 8);
189 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
191 parent->height /2 - 4, 7, 8);
192 display->update_viewport();
195 static void pitchscreen_draw(struct screen *display, int max_lines,
196 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
197 int32_t pitch, int32_t semitone
198 #if CONFIG_CODEC == SWCODEC
199 ,int32_t speed
200 #endif
203 unsigned char* ptr;
204 char buf[32];
205 int w, h;
206 bool show_lang_pitch;
208 /* "Pitch up/Pitch down" - hide for a small screen */
209 if (max_lines >= 5)
211 /* UP: Pitch Up */
212 display->set_viewport(&pitch_viewports[PITCH_TOP]);
213 if (global_settings.pitch_mode_semitone)
214 ptr = str(LANG_PITCH_UP_SEMITONE);
215 else
216 ptr = str(LANG_PITCH_UP);
217 display->getstringsize(ptr, &w, &h);
218 display->clear_viewport();
219 /* draw text */
220 display->putsxy((pitch_viewports[PITCH_TOP].width / 2) -
221 (w / 2), 0, ptr);
222 display->update_viewport();
224 /* DOWN: Pitch Down */
225 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
226 if (global_settings.pitch_mode_semitone)
227 ptr = str(LANG_PITCH_DOWN_SEMITONE);
228 else
229 ptr = str(LANG_PITCH_DOWN);
230 display->getstringsize(ptr, &w, &h);
231 display->clear_viewport();
232 /* draw text */
233 display->putsxy((pitch_viewports[PITCH_BOTTOM].width / 2) -
234 (w / 2), 0, ptr);
235 display->update_viewport();
238 /* Middle section */
239 display->set_viewport(&pitch_viewports[PITCH_MID]);
240 display->clear_viewport();
241 int width_used = 0;
243 /* Middle section upper line - hide for a small screen */
244 if ((show_lang_pitch = (max_lines >= 3)))
246 #if CONFIG_CODEC == SWCODEC
247 if(global_settings.pitch_mode_timestretch)
249 /* Pitch:XXX.X% */
250 if(global_settings.pitch_mode_semitone)
252 snprintf(buf, sizeof(buf), "%s: %s%ld.%02ld", str(LANG_PITCH),
253 semitone >= 0 ? "+" : "-",
254 ABS(semitone / PITCH_SPEED_PRECISION),
255 ABS((semitone % PITCH_SPEED_PRECISION) /
256 (PITCH_SPEED_PRECISION / 100))
259 else
261 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
262 pitch / PITCH_SPEED_PRECISION,
263 (pitch % PITCH_SPEED_PRECISION) /
264 (PITCH_SPEED_PRECISION / 10));
267 else
268 #endif
270 /* Rate */
271 snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
273 display->getstringsize(buf, &w, &h);
274 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
275 (pitch_viewports[PITCH_MID].height / 2) - h, buf);
276 if (w > width_used)
277 width_used = w;
280 /* Middle section lower line */
281 /* "Speed:XXX%" */
282 #if CONFIG_CODEC == SWCODEC
283 if(global_settings.pitch_mode_timestretch)
285 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
286 speed / PITCH_SPEED_PRECISION,
287 (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
289 else
290 #endif
292 if(global_settings.pitch_mode_semitone)
294 snprintf(buf, sizeof(buf), "%s%ld.%02ld",
295 semitone >= 0 ? "+" : "-",
296 ABS(semitone / PITCH_SPEED_PRECISION),
297 ABS((semitone % PITCH_SPEED_PRECISION) /
298 (PITCH_SPEED_PRECISION / 100))
301 else
303 snprintf(buf, sizeof(buf), "%ld.%ld%%",
304 pitch / PITCH_SPEED_PRECISION,
305 (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
309 display->getstringsize(buf, &w, &h);
310 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
311 show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
312 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
313 buf);
314 if (w > width_used)
315 width_used = w;
317 /* "limit" and "timestretch" labels */
318 if (max_lines >= 7)
320 if(at_limit)
322 snprintf(buf, sizeof(buf), "%s", str(LANG_STRETCH_LIMIT));
323 display->getstringsize(buf, &w, &h);
324 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
325 (pitch_viewports[PITCH_MID].height / 2) + h, buf);
326 if (w > width_used)
327 width_used = w;
331 /* Middle section left/right labels */
332 const char *leftlabel = "-2%";
333 const char *rightlabel = "+2%";
334 #if CONFIG_CODEC == SWCODEC
335 if (global_settings.pitch_mode_timestretch)
337 leftlabel = "<<";
338 rightlabel = ">>";
340 #endif
342 /* Only display if they fit */
343 display->getstringsize(leftlabel, &w, &h);
344 width_used += w;
345 display->getstringsize(rightlabel, &w, &h);
346 width_used += w;
348 if (width_used <= pitch_viewports[PITCH_MID].width)
350 display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
351 leftlabel);
352 display->putsxy((pitch_viewports[PITCH_MID].width - w),
353 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
354 rightlabel);
356 display->update_viewport();
357 display->set_viewport(NULL);
360 static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
361 #if CONFIG_CODEC == SWCODEC
362 /* need this to maintain correct pitch/speed caps */
363 , int32_t speed
364 #endif
367 int32_t new_pitch;
368 #if CONFIG_CODEC == SWCODEC
369 int32_t new_stretch;
370 #endif
371 at_limit = false;
373 if (pitch_delta < 0)
375 /* for large jumps, snap up to whole numbers */
376 if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
377 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
379 pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
382 new_pitch = pitch + pitch_delta;
384 if (new_pitch < PITCH_MIN)
386 if (!allow_cutoff)
388 return pitch;
390 new_pitch = PITCH_MIN;
391 at_limit = true;
394 else if (pitch_delta > 0)
396 /* for large jumps, snap down to whole numbers */
397 if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
398 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
400 pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
403 new_pitch = pitch + pitch_delta;
405 if (new_pitch > PITCH_MAX)
407 if (!allow_cutoff)
408 return pitch;
409 new_pitch = PITCH_MAX;
410 at_limit = true;
413 else
415 /* pitch_delta == 0 -> no real change */
416 return pitch;
418 #if CONFIG_CODEC == SWCODEC
419 if (dsp_timestretch_available())
421 /* increase the multiple to increase precision of this calculation */
422 new_stretch = GET_STRETCH(new_pitch, speed);
423 if(new_stretch < STRETCH_MIN)
425 /* we have to ignore allow_cutoff, because we can't have the */
426 /* stretch go higher than STRETCH_MAX */
427 new_pitch = GET_PITCH(speed, STRETCH_MIN);
429 else if(new_stretch > STRETCH_MAX)
431 /* we have to ignore allow_cutoff, because we can't have the */
432 /* stretch go higher than STRETCH_MAX */
433 new_pitch = GET_PITCH(speed, STRETCH_MAX);
436 if(new_stretch >= STRETCH_MAX ||
437 new_stretch <= STRETCH_MIN)
439 at_limit = true;
442 #endif
444 sound_set_pitch(new_pitch);
446 return new_pitch;
449 static int32_t get_semitone_from_pitch(int32_t pitch)
451 int semitone = 0;
452 int32_t fractional_index = 0;
454 while(semitone < NUM_SEMITONES - 1 &&
455 pitch >= semitone_table[semitone + 1])
457 semitone++;
461 /* now find the fractional part */
462 while(pitch > (cent_interp[fractional_index + 1] *
463 semitone_table[semitone] / PITCH_SPEED_100))
465 /* Check to make sure fractional_index isn't too big */
466 /* This should never happen. */
467 if(fractional_index >= CENT_INTERP_NUM - 1)
469 break;
471 fractional_index++;
474 int32_t semitone_pitch_a = cent_interp[fractional_index] *
475 semitone_table[semitone] /
476 PITCH_SPEED_100;
477 int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
478 semitone_table[semitone] /
479 PITCH_SPEED_100;
480 /* this will be the integer offset from the cent_interp entry */
481 int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
482 (semitone_pitch_b - semitone_pitch_a);
483 semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
484 fractional_index * CENT_INTERP_INTERVAL +
485 semitone_frac_ofs;
487 return semitone;
490 static int32_t get_pitch_from_semitone(int32_t semitone)
492 int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
494 /* Find the index into the semitone table */
495 int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
497 /* set pitch to the semitone's integer part value */
498 int32_t pitch = semitone_table[semitone_index];
499 /* get the range of the cent modification for future calculation */
500 int32_t pitch_mod_a =
501 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
502 CENT_INTERP_INTERVAL];
503 int32_t pitch_mod_b =
504 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
505 CENT_INTERP_INTERVAL + 1];
506 /* figure out the cent mod amount based on the semitone fractional value */
507 int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
508 (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
510 /* modify pitch based on the mod amount we just calculated */
511 return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
514 static int32_t pitch_increase_semitone(int32_t pitch,
515 int32_t current_semitone,
516 int32_t semitone_delta
517 #if CONFIG_CODEC == SWCODEC
518 , int32_t speed
519 #endif
522 int32_t new_semitone = current_semitone;
524 /* snap to the delta interval */
525 if(current_semitone % semitone_delta != 0)
527 if(current_semitone > 0 && semitone_delta > 0)
528 new_semitone += semitone_delta;
529 else if(current_semitone < 0 && semitone_delta < 0)
530 new_semitone += semitone_delta;
532 new_semitone -= new_semitone % semitone_delta;
534 else
535 new_semitone += semitone_delta;
537 /* clamp the pitch so it doesn't go beyond the pitch limits */
538 if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
540 new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
541 at_limit = true;
543 else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
545 new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
546 at_limit = true;
549 int32_t new_pitch = get_pitch_from_semitone(new_semitone);
551 #if CONFIG_CODEC == SWCODEC
552 int32_t new_stretch = GET_STRETCH(new_pitch, speed);
554 /* clamp the pitch so it doesn't go beyond the stretch limits */
555 if( new_stretch > STRETCH_MAX)
557 new_pitch = GET_PITCH(speed, STRETCH_MAX);
558 new_semitone = get_semitone_from_pitch(new_pitch);
559 at_limit = true;
561 else if (new_stretch < STRETCH_MIN)
563 new_pitch = GET_PITCH(speed, STRETCH_MIN);
564 new_semitone = get_semitone_from_pitch(new_pitch);
565 at_limit = true;
567 #endif
569 pitch_increase(pitch, new_pitch - pitch, false
570 #if CONFIG_CODEC == SWCODEC
571 , speed
572 #endif
575 return new_semitone;
579 returns:
580 0 on exit
581 1 if USB was connected
584 int gui_syncpitchscreen_run(void)
586 int button, i;
587 int32_t pitch = sound_get_pitch();
588 int32_t semitone;
590 int32_t new_pitch;
591 int32_t pitch_delta;
592 bool nudged = false;
593 bool exit = false;
594 /* should maybe be passed per parameter later, not needed for now */
595 struct viewport parent[NB_SCREENS];
596 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
597 int max_lines[NB_SCREENS];
599 #if CONFIG_CODEC == SWCODEC
600 int32_t new_speed = 0, new_stretch;
602 /* the speed variable holds the apparent speed of the playback */
603 int32_t speed;
604 if (dsp_timestretch_available())
606 speed = GET_SPEED(pitch, dsp_get_timestretch());
608 else
610 speed = pitch;
613 /* Figure out whether to be in timestretch mode */
614 if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
616 global_settings.pitch_mode_timestretch = false;
617 settings_save();
619 #endif
621 /* set the semitone index based on the current pitch */
622 semitone = get_semitone_from_pitch(pitch);
624 /* initialize pitchscreen vps */
625 FOR_NB_SCREENS(i)
627 screens[i].clear_display();
628 viewport_set_defaults(&parent[i], i);
629 max_lines[i] = viewport_get_nb_lines(&parent[i]);
630 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
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;