Improvements to the pitch screen UI (FS#10359 by David Johnston)
[kugel-rb.git] / apps / gui / pitchscreen.c
bloba699d4a7b4e7a2a9e9346fa835450cda104704d6
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.
85 #define SEMITONE_VALUE(x) \
86 ( (int)(((x) + 0.5 / PITCH_SPEED_PRECISION) * PITCH_SPEED_PRECISION) )
88 static const int semitone_table[] =
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 )
117 #define NUM_SEMITONES ((int)(sizeof(semitone_table) / sizeof(int)))
118 #define SEMITONE_START -12
119 #define SEMITONE_END 12
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 #define CENT_INTERP(x) \
130 ( (int)(((x) + 0.5 / PITCH_SPEED_PRECISION) * PITCH_SPEED_PRECISION) )
133 static const int cent_interp[] =
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),
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)))
148 /* This stores whether the pitch and speed are at their own limits */
149 /* or that of the timestretching algorithm */
150 static bool at_limit = false;
152 static void pitchscreen_fix_viewports(struct viewport *parent,
153 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
155 int i, font_height;
156 font_height = font_get(parent->font)->height;
157 for (i = 0; i < PITCH_ITEM_COUNT; i++)
159 pitch_viewports[i] = *parent;
160 pitch_viewports[i].height = font_height;
162 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
164 pitch_viewports[PITCH_MID].x += ICON_BORDER;
165 pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*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;
170 pitch_viewports[PITCH_MID].y += parent->height / 2 -
171 pitch_viewports[PITCH_MID].height / 2;
173 pitch_viewports[PITCH_BOTTOM].y += parent->height - font_height
174 - ICON_BORDER;
177 /* must be called before pitchscreen_draw, or within
178 * since it neither clears nor updates the display */
179 static void pitchscreen_draw_icons(struct screen *display,
180 struct viewport *parent)
182 display->set_viewport(parent);
183 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
184 parent->width/2 - 3,
185 2, 7, 8);
186 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
187 parent->width /2 - 3,
188 parent->height - 10, 7, 8);
189 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
190 parent->width - 10,
191 parent->height /2 - 4, 7, 8);
192 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
194 parent->height /2 - 4, 7, 8);
195 display->update_viewport();
198 static void pitchscreen_draw(struct screen *display, int max_lines,
199 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
200 int32_t pitch, int32_t semitone
201 #if CONFIG_CODEC == SWCODEC
202 ,int32_t speed
203 #endif
206 unsigned char* ptr;
207 char buf[32];
208 int w, h;
209 bool show_lang_pitch;
211 /* "Pitch up/Pitch down" - hide for a small screen */
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->getstringsize(ptr, &w, &h);
221 display->clear_viewport();
222 /* draw text */
223 display->putsxy((pitch_viewports[PITCH_TOP].width / 2) -
224 (w / 2), 0, ptr);
225 display->update_viewport();
227 /* DOWN: Pitch Down */
228 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
229 if (global_settings.pitch_mode_semitone)
230 ptr = str(LANG_PITCH_DOWN_SEMITONE);
231 else
232 ptr = str(LANG_PITCH_DOWN);
233 display->getstringsize(ptr, &w, &h);
234 display->clear_viewport();
235 /* draw text */
236 display->putsxy((pitch_viewports[PITCH_BOTTOM].width / 2) -
237 (w / 2), 0, ptr);
238 display->update_viewport();
241 /* Middle section */
242 display->set_viewport(&pitch_viewports[PITCH_MID]);
243 display->clear_viewport();
244 int width_used = 0;
246 /* Middle section upper line - hide for a small screen */
247 if ((show_lang_pitch = (max_lines >= 3)))
249 #if CONFIG_CODEC == SWCODEC
250 if(global_settings.pitch_mode_timestretch)
252 /* Pitch:XXX.X% */
253 if(global_settings.pitch_mode_semitone)
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))
262 else
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));
270 else
271 #endif
273 /* Rate */
274 snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
276 display->getstringsize(buf, &w, &h);
277 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
278 (pitch_viewports[PITCH_MID].height / 2) - h, buf);
279 if (w > width_used)
280 width_used = w;
283 /* Middle section lower line */
284 /* "Speed:XXX%" */
285 #if CONFIG_CODEC == SWCODEC
286 if(global_settings.pitch_mode_timestretch)
288 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
289 speed / PITCH_SPEED_PRECISION,
290 (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
292 else
293 #endif
295 if(global_settings.pitch_mode_semitone)
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))
304 else
306 snprintf(buf, sizeof(buf), "%ld.%ld%%",
307 pitch / PITCH_SPEED_PRECISION,
308 (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
312 display->getstringsize(buf, &w, &h);
313 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
314 show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
315 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
316 buf);
317 if (w > width_used)
318 width_used = w;
320 /* "limit" and "timestretch" labels */
321 if (max_lines >= 7)
323 if(at_limit)
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;
334 /* Middle section left/right labels */
335 const char *leftlabel = "-2%";
336 const char *rightlabel = "+2%";
337 #if CONFIG_CODEC == SWCODEC
338 if (global_settings.pitch_mode_timestretch)
340 leftlabel = "<<";
341 rightlabel = ">>";
343 #endif
345 /* Only display if they fit */
346 display->getstringsize(leftlabel, &w, &h);
347 width_used += w;
348 display->getstringsize(rightlabel, &w, &h);
349 width_used += w;
351 if (width_used <= pitch_viewports[PITCH_MID].width)
353 display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
354 leftlabel);
355 display->putsxy((pitch_viewports[PITCH_MID].width - w),
356 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
357 rightlabel);
359 display->update_viewport();
360 display->set_viewport(NULL);
363 static 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
370 int32_t new_pitch;
371 #if CONFIG_CODEC == SWCODEC
372 int32_t new_stretch;
373 #endif
374 at_limit = false;
376 if (pitch_delta < 0)
378 /* for large jumps, snap up to whole numbers */
379 if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
380 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
382 pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
385 new_pitch = pitch + pitch_delta;
387 if (new_pitch < PITCH_MIN)
389 if (!allow_cutoff)
391 return pitch;
393 new_pitch = PITCH_MIN;
394 at_limit = true;
397 else if (pitch_delta > 0)
399 /* for large jumps, snap down to whole numbers */
400 if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
401 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
403 pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
406 new_pitch = pitch + pitch_delta;
408 if (new_pitch > PITCH_MAX)
410 if (!allow_cutoff)
411 return pitch;
412 new_pitch = PITCH_MAX;
413 at_limit = true;
416 else
418 /* pitch_delta == 0 -> no real change */
419 return pitch;
421 #if CONFIG_CODEC == SWCODEC
422 if (dsp_timestretch_available())
424 /* increase the multiple to increase precision of this calculation */
425 new_stretch = GET_STRETCH(new_pitch, speed);
426 if(new_stretch < STRETCH_MIN)
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);
432 else if(new_stretch > STRETCH_MAX)
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);
439 if(new_stretch >= STRETCH_MAX ||
440 new_stretch <= STRETCH_MIN)
442 at_limit = true;
445 #endif
447 sound_set_pitch(new_pitch);
449 return new_pitch;
452 static int32_t get_semitone_from_pitch(int32_t pitch)
454 int semitone = 0;
455 int32_t fractional_index = 0;
457 while(semitone < NUM_SEMITONES - 1 &&
458 pitch >= semitone_table[semitone + 1])
460 semitone++;
464 /* now find the fractional part */
465 while(pitch > (cent_interp[fractional_index + 1] *
466 semitone_table[semitone] / PITCH_SPEED_100))
468 /* Check to make sure fractional_index isn't too big */
469 /* This should never happen. */
470 if(fractional_index >= CENT_INTERP_NUM - 1)
472 break;
474 fractional_index++;
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;
490 return semitone;
493 static int32_t get_pitch_from_semitone(int32_t semitone)
495 int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
497 /* Find the index into the semitone table */
498 int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
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;
513 /* modify pitch based on the mod amount we just calculated */
514 return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
517 static 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
525 int32_t new_semitone = current_semitone;
527 /* snap to the delta interval */
528 if(current_semitone % semitone_delta != 0)
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;
535 new_semitone -= new_semitone % semitone_delta;
537 else
538 new_semitone += semitone_delta;
540 /* clamp the pitch so it doesn't go beyond the pitch limits */
541 if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
543 new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
544 at_limit = true;
546 else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
548 new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
549 at_limit = true;
552 int32_t new_pitch = get_pitch_from_semitone(new_semitone);
554 #if CONFIG_CODEC == SWCODEC
555 int32_t new_stretch = GET_STRETCH(new_pitch, speed);
557 /* clamp the pitch so it doesn't go beyond the stretch limits */
558 if( new_stretch > STRETCH_MAX)
560 new_pitch = GET_PITCH(speed, STRETCH_MAX);
561 new_semitone = get_semitone_from_pitch(new_pitch);
562 at_limit = true;
564 else if (new_stretch < STRETCH_MIN)
566 new_pitch = GET_PITCH(speed, STRETCH_MIN);
567 new_semitone = get_semitone_from_pitch(new_pitch);
568 at_limit = true;
570 #endif
572 pitch_increase(pitch, new_pitch - pitch, false
573 #if CONFIG_CODEC == SWCODEC
574 , speed
575 #endif
578 return new_semitone;
582 returns:
583 0 on exit
584 1 if USB was connected
587 int gui_syncpitchscreen_run(void)
589 int button, i;
590 int32_t pitch = sound_get_pitch();
591 int32_t semitone;
593 int32_t new_pitch;
594 int32_t pitch_delta;
595 bool nudged = false;
596 bool exit = false;
597 /* should maybe be passed per parameter later, not needed for now */
598 struct viewport parent[NB_SCREENS];
599 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
600 int max_lines[NB_SCREENS];
602 #if CONFIG_CODEC == SWCODEC
603 int32_t new_speed = 0, new_stretch;
605 /* the speed variable holds the apparent speed of the playback */
606 int32_t speed;
607 if (dsp_timestretch_available())
609 speed = GET_SPEED(pitch, dsp_get_timestretch());
611 else
613 speed = pitch;
616 /* Figure out whether to be in timestretch mode */
617 if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
619 global_settings.pitch_mode_timestretch = false;
620 settings_save();
622 #endif
624 /* set the semitone index based on the current pitch */
625 semitone = get_semitone_from_pitch(pitch);
627 /* initialize pitchscreen vps */
628 FOR_NB_SCREENS(i)
630 screens[i].clear_display();
631 viewport_set_defaults(&parent[i], i);
632 max_lines[i] = viewport_get_nb_lines(&parent[i]);
633 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
635 /* also, draw the icons now, it's only needed once */
636 pitchscreen_draw_icons(&screens[i], &parent[i]);
638 #if CONFIG_CODEC == SWCODEC
639 pcmbuf_set_low_latency(true);
640 #endif
642 while (!exit)
644 FOR_NB_SCREENS(i)
645 pitchscreen_draw(&screens[i], max_lines[i],
646 pitch_viewports[i], pitch, semitone
647 #if CONFIG_CODEC == SWCODEC
648 , speed
649 #endif
651 pitch_delta = 0;
652 #if CONFIG_CODEC == SWCODEC
653 new_speed = 0;
654 #endif
655 button = get_action(CONTEXT_PITCHSCREEN, HZ);
656 switch (button)
658 case ACTION_PS_INC_SMALL:
659 if(global_settings.pitch_mode_semitone)
660 pitch_delta = SEMITONE_SMALL_DELTA;
661 else
662 pitch_delta = PITCH_SMALL_DELTA;
663 break;
665 case ACTION_PS_INC_BIG:
666 if(global_settings.pitch_mode_semitone)
667 pitch_delta = SEMITONE_BIG_DELTA;
668 else
669 pitch_delta = PITCH_BIG_DELTA;
670 break;
672 case ACTION_PS_DEC_SMALL:
673 if(global_settings.pitch_mode_semitone)
674 pitch_delta = -SEMITONE_SMALL_DELTA;
675 else
676 pitch_delta = -PITCH_SMALL_DELTA;
677 break;
679 case ACTION_PS_DEC_BIG:
680 if(global_settings.pitch_mode_semitone)
681 pitch_delta = -SEMITONE_BIG_DELTA;
682 else
683 pitch_delta = -PITCH_BIG_DELTA;
684 break;
686 case ACTION_PS_NUDGE_RIGHT:
687 #if CONFIG_CODEC == SWCODEC
688 if (!global_settings.pitch_mode_timestretch)
690 #endif
691 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
692 #if CONFIG_CODEC == SWCODEC
693 , speed
694 #endif
696 nudged = (new_pitch != pitch);
697 pitch = new_pitch;
698 semitone = get_semitone_from_pitch(pitch);
699 #if CONFIG_CODEC == SWCODEC
700 speed = pitch;
701 #endif
702 break;
703 #if CONFIG_CODEC == SWCODEC
705 else
707 new_speed = speed + SPEED_SMALL_DELTA;
708 at_limit = false;
710 break;
712 case ACTION_PS_FASTER:
713 if (global_settings.pitch_mode_timestretch)
715 new_speed = speed + SPEED_BIG_DELTA;
716 /* snap to whole numbers */
717 if(new_speed % PITCH_SPEED_PRECISION != 0)
718 new_speed -= new_speed % PITCH_SPEED_PRECISION;
719 at_limit = false;
721 break;
722 #endif
724 case ACTION_PS_NUDGE_RIGHTOFF:
725 if (nudged)
727 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
728 #if CONFIG_CODEC == SWCODEC
729 , speed
730 #endif
732 #if CONFIG_CODEC == SWCODEC
733 speed = pitch;
734 #endif
735 semitone = get_semitone_from_pitch(pitch);
736 nudged = false;
738 break;
740 case ACTION_PS_NUDGE_LEFT:
741 #if CONFIG_CODEC == SWCODEC
742 if (!global_settings.pitch_mode_timestretch)
744 #endif
745 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
746 #if CONFIG_CODEC == SWCODEC
747 , speed
748 #endif
750 nudged = (new_pitch != pitch);
751 pitch = new_pitch;
752 semitone = get_semitone_from_pitch(pitch);
753 #if CONFIG_CODEC == SWCODEC
754 speed = pitch;
755 #endif
756 break;
757 #if CONFIG_CODEC == SWCODEC
759 else
761 new_speed = speed - SPEED_SMALL_DELTA;
762 at_limit = false;
764 break;
766 case ACTION_PS_SLOWER:
767 if (global_settings.pitch_mode_timestretch)
769 new_speed = speed - SPEED_BIG_DELTA;
770 /* snap to whole numbers */
771 if(new_speed % PITCH_SPEED_PRECISION != 0)
772 new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
773 at_limit = false;
775 break;
776 #endif
778 case ACTION_PS_NUDGE_LEFTOFF:
779 if (nudged)
781 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
782 #if CONFIG_CODEC == SWCODEC
783 , speed
784 #endif
786 #if CONFIG_CODEC == SWCODEC
787 speed = pitch;
788 #endif
789 semitone = get_semitone_from_pitch(pitch);
790 nudged = false;
792 break;
794 case ACTION_PS_RESET:
795 pitch = PITCH_SPEED_100;
796 sound_set_pitch(pitch);
797 #if CONFIG_CODEC == SWCODEC
798 speed = PITCH_SPEED_100;
799 if (dsp_timestretch_available())
801 dsp_set_timestretch(PITCH_SPEED_100);
802 at_limit = false;
804 #endif
805 semitone = get_semitone_from_pitch(pitch);
806 break;
808 case ACTION_PS_TOGGLE_MODE:
809 global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
810 #if CONFIG_CODEC == SWCODEC
812 if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
814 global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
815 if(!global_settings.pitch_mode_timestretch)
817 /* no longer in timestretch mode. Reset speed */
818 speed = pitch;
819 dsp_set_timestretch(PITCH_SPEED_100);
822 settings_save();
823 #endif
824 break;
826 case ACTION_PS_EXIT:
827 exit = true;
828 break;
830 default:
831 if (default_event_handler(button) == SYS_USB_CONNECTED)
832 return 1;
833 break;
835 if (pitch_delta)
837 if (global_settings.pitch_mode_semitone)
839 semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
840 #if CONFIG_CODEC == SWCODEC
841 , speed
842 #endif
844 pitch = get_pitch_from_semitone(semitone);
846 else
848 pitch = pitch_increase(pitch, pitch_delta, true
849 #if CONFIG_CODEC == SWCODEC
850 , speed
851 #endif
853 semitone = get_semitone_from_pitch(pitch);
855 #if CONFIG_CODEC == SWCODEC
856 if (global_settings.pitch_mode_timestretch)
858 /* do this to make sure we properly obey the stretch limits */
859 new_speed = speed;
861 else
863 speed = pitch;
865 #endif
868 #if CONFIG_CODEC == SWCODEC
869 if(new_speed)
871 new_stretch = GET_STRETCH(pitch, new_speed);
873 /* limit the amount of stretch */
874 if(new_stretch > STRETCH_MAX)
876 new_stretch = STRETCH_MAX;
877 new_speed = GET_SPEED(pitch, new_stretch);
879 else if(new_stretch < STRETCH_MIN)
881 new_stretch = STRETCH_MIN;
882 new_speed = GET_SPEED(pitch, new_stretch);
885 new_stretch = GET_STRETCH(pitch, new_speed);
886 if(new_stretch >= STRETCH_MAX ||
887 new_stretch <= STRETCH_MIN)
889 at_limit = true;
892 /* set the amount of stretch */
893 dsp_set_timestretch(new_stretch);
895 /* update the speed variable with the new speed */
896 speed = new_speed;
898 /* Reset new_speed so we only call dsp_set_timestretch */
899 /* when needed */
900 new_speed = 0;
902 #endif
904 #if CONFIG_CODEC == SWCODEC
905 pcmbuf_set_low_latency(false);
906 #endif
907 return 0;