Move c/h files implementing/defining standard library stuff into a new libc directory...
[kugel-rb.git] / apps / gui / pitchscreen.c
bloba0058724ec6101d72ff7d3313f740e4a1921663a
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 "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 ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
46 /* on both sides when drawing */
48 #define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
49 #define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
50 #define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
51 #define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
52 #define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
54 #define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
55 #define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
57 #define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
58 #define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
60 enum
62 PITCH_TOP = 0,
63 PITCH_MID,
64 PITCH_BOTTOM,
65 PITCH_ITEM_COUNT,
69 /* This is a table of semitone percentage values of the appropriate
70 precision (based on PITCH_SPEED_PRECISION). Note that these are
71 all constant expressions, which will be evaluated at compile time,
72 so no need to worry about how complex the expressions look.
73 That's just to get the precision right.
75 I calculated these values, starting from 50, as
77 x(n) = 50 * 2^(n/12)
79 All that math in each entry simply converts the float constant
80 to an integer equal to PITCH_SPEED_PRECISION times the float value,
81 with as little precision loss as possible (i.e. correctly rounding
82 the last digit).
84 #define TO_INT_WITH_PRECISION(x) \
85 ( (unsigned short)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) )
87 static const unsigned short semitone_table[] =
89 TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */
90 TO_INT_WITH_PRECISION(52.97315472),
91 TO_INT_WITH_PRECISION(56.12310242),
92 TO_INT_WITH_PRECISION(59.46035575),
93 TO_INT_WITH_PRECISION(62.99605249),
94 TO_INT_WITH_PRECISION(66.74199271),
95 TO_INT_WITH_PRECISION(70.71067812),
96 TO_INT_WITH_PRECISION(74.91535384),
97 TO_INT_WITH_PRECISION(79.37005260),
98 TO_INT_WITH_PRECISION(84.08964153),
99 TO_INT_WITH_PRECISION(89.08987181),
100 TO_INT_WITH_PRECISION(94.38743127),
101 TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */
102 TO_INT_WITH_PRECISION(105.9463094),
103 TO_INT_WITH_PRECISION(112.2462048),
104 TO_INT_WITH_PRECISION(118.9207115),
105 TO_INT_WITH_PRECISION(125.9921049),
106 TO_INT_WITH_PRECISION(133.4839854),
107 TO_INT_WITH_PRECISION(141.4213562),
108 TO_INT_WITH_PRECISION(149.8307077),
109 TO_INT_WITH_PRECISION(158.7401052),
110 TO_INT_WITH_PRECISION(168.1792831),
111 TO_INT_WITH_PRECISION(178.1797436),
112 TO_INT_WITH_PRECISION(188.7748625),
113 TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */
116 #define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0])))
117 #define SEMITONE_END (NUM_SEMITONES/2)
118 #define SEMITONE_START (-SEMITONE_END)
120 /* A table of values for approximating the cent curve with
121 linear interpolation. Multipy the next lowest semitone
122 by this much to find the corresponding cent percentage.
124 These values were calculated as
125 x(n) = 100 * 2^(n * 20/1200)
128 static const unsigned short cent_interp[] =
130 TO_INT_WITH_PRECISION(100.0000000),
131 TO_INT_WITH_PRECISION(101.1619440),
132 TO_INT_WITH_PRECISION(102.3373892),
133 TO_INT_WITH_PRECISION(103.5264924),
134 TO_INT_WITH_PRECISION(104.7294123),
135 /* this one's the next semitone but we have it here for convenience */
136 TO_INT_WITH_PRECISION(105.9463094),
139 /* Number of cents between entries in the cent_interp table */
140 #define CENT_INTERP_INTERVAL 20
141 #define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0])))
143 /* This stores whether the pitch and speed are at their own limits */
144 /* or that of the timestretching algorithm */
145 static bool at_limit = false;
147 static void pitchscreen_fix_viewports(struct viewport *parent,
148 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
150 int i, font_height;
151 font_height = font_get(parent->font)->height;
152 for (i = 0; i < PITCH_ITEM_COUNT; i++)
154 pitch_viewports[i] = *parent;
155 pitch_viewports[i].height = font_height;
157 if (i == PITCH_TOP || i == PITCH_BOTTOM)
158 pitch_viewports[i].flags |= VP_FLAG_ALIGN_CENTER;
160 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
162 pitch_viewports[PITCH_MID].x += ICON_BORDER;
163 pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
164 pitch_viewports[PITCH_MID].height = parent->height - ICON_BORDER*2
165 - font_height * 2;
166 if(pitch_viewports[PITCH_MID].height < font_height * 2)
167 pitch_viewports[PITCH_MID].height = font_height * 2;
168 pitch_viewports[PITCH_MID].y += parent->height / 2 -
169 pitch_viewports[PITCH_MID].height / 2;
171 pitch_viewports[PITCH_BOTTOM].y += parent->height - font_height
172 - ICON_BORDER;
175 /* must be called before pitchscreen_draw, or within
176 * since it neither clears nor updates the display */
177 static void pitchscreen_draw_icons(struct screen *display,
178 struct viewport *parent)
180 display->set_viewport(parent);
181 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
182 parent->width/2 - 3,
183 2, 7, 8);
184 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
185 parent->width /2 - 3,
186 parent->height - 10, 7, 8);
187 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
188 parent->width - 10,
189 parent->height /2 - 4, 7, 8);
190 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
192 parent->height /2 - 4, 7, 8);
193 display->update_viewport();
196 static void pitchscreen_draw(struct screen *display, int max_lines,
197 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
198 int32_t pitch, int32_t semitone
199 #if CONFIG_CODEC == SWCODEC
200 ,int32_t speed
201 #endif
204 const char* ptr;
205 char buf[32];
206 int w, h;
207 bool show_lang_pitch;
209 /* "Pitch up/Pitch down" - hide for a small screen,
210 * the text is drawn centered automatically */
211 if (max_lines >= 5)
213 /* UP: Pitch Up */
214 display->set_viewport(&pitch_viewports[PITCH_TOP]);
215 if (global_settings.pitch_mode_semitone)
216 ptr = str(LANG_PITCH_UP_SEMITONE);
217 else
218 ptr = str(LANG_PITCH_UP);
219 display->clear_viewport();
220 /* draw text */
221 display->putsxy(0, 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->clear_viewport();
231 /* draw text */
232 display->putsxy(0, 0, ptr);
233 display->update_viewport();
236 /* Middle section */
237 display->set_viewport(&pitch_viewports[PITCH_MID]);
238 display->clear_viewport();
239 int width_used = 0;
241 /* Middle section upper line - hide for a small screen */
242 if ((show_lang_pitch = (max_lines >= 3)))
244 #if CONFIG_CODEC == SWCODEC
245 if(global_settings.pitch_mode_timestretch)
247 /* Pitch:XXX.X% */
248 if(global_settings.pitch_mode_semitone)
250 snprintf(buf, sizeof(buf), "%s: %s%d.%02d", str(LANG_PITCH),
251 semitone >= 0 ? "+" : "-",
252 abs(semitone / PITCH_SPEED_PRECISION),
253 abs((semitone % PITCH_SPEED_PRECISION) /
254 (PITCH_SPEED_PRECISION / 100))
257 else
259 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
260 pitch / PITCH_SPEED_PRECISION,
261 (pitch % PITCH_SPEED_PRECISION) /
262 (PITCH_SPEED_PRECISION / 10));
265 else
266 #endif
268 /* Rate */
269 snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
271 display->getstringsize(buf, &w, &h);
272 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
273 (pitch_viewports[PITCH_MID].height / 2) - h, buf);
274 if (w > width_used)
275 width_used = w;
278 /* Middle section lower line */
279 /* "Speed:XXX%" */
280 #if CONFIG_CODEC == SWCODEC
281 if(global_settings.pitch_mode_timestretch)
283 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
284 speed / PITCH_SPEED_PRECISION,
285 (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
287 else
288 #endif
290 if(global_settings.pitch_mode_semitone)
292 snprintf(buf, sizeof(buf), "%s%d.%02d",
293 semitone >= 0 ? "+" : "-",
294 abs(semitone / PITCH_SPEED_PRECISION),
295 abs((semitone % PITCH_SPEED_PRECISION) /
296 (PITCH_SPEED_PRECISION / 100))
299 else
301 snprintf(buf, sizeof(buf), "%ld.%ld%%",
302 pitch / PITCH_SPEED_PRECISION,
303 (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
307 display->getstringsize(buf, &w, &h);
308 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
309 show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
310 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
311 buf);
312 if (w > width_used)
313 width_used = w;
315 /* "limit" and "timestretch" labels */
316 if (max_lines >= 7)
318 if(at_limit)
320 const char * const p = str(LANG_STRETCH_LIMIT);
321 display->getstringsize(p, &w, &h);
322 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
323 (pitch_viewports[PITCH_MID].height / 2) + h, p);
324 if (w > width_used)
325 width_used = w;
329 /* Middle section left/right labels */
330 const char *leftlabel = "-2%";
331 const char *rightlabel = "+2%";
332 #if CONFIG_CODEC == SWCODEC
333 if (global_settings.pitch_mode_timestretch)
335 leftlabel = "<<";
336 rightlabel = ">>";
338 #endif
340 /* Only display if they fit */
341 display->getstringsize(leftlabel, &w, &h);
342 width_used += w;
343 display->getstringsize(rightlabel, &w, &h);
344 width_used += w;
346 if (width_used <= pitch_viewports[PITCH_MID].width)
348 display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
349 leftlabel);
350 display->putsxy((pitch_viewports[PITCH_MID].width - w),
351 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
352 rightlabel);
354 display->update_viewport();
355 display->set_viewport(NULL);
358 static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
359 #if CONFIG_CODEC == SWCODEC
360 /* need this to maintain correct pitch/speed caps */
361 , int32_t speed
362 #endif
365 int32_t new_pitch;
366 #if CONFIG_CODEC == SWCODEC
367 int32_t new_stretch;
368 #endif
369 at_limit = false;
371 if (pitch_delta < 0)
373 /* for large jumps, snap up to whole numbers */
374 if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
375 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
377 pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
380 new_pitch = pitch + pitch_delta;
382 if (new_pitch < PITCH_MIN)
384 if (!allow_cutoff)
386 return pitch;
388 new_pitch = PITCH_MIN;
389 at_limit = true;
392 else if (pitch_delta > 0)
394 /* for large jumps, snap down to whole numbers */
395 if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
396 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
398 pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
401 new_pitch = pitch + pitch_delta;
403 if (new_pitch > PITCH_MAX)
405 if (!allow_cutoff)
406 return pitch;
407 new_pitch = PITCH_MAX;
408 at_limit = true;
411 else
413 /* pitch_delta == 0 -> no real change */
414 return pitch;
416 #if CONFIG_CODEC == SWCODEC
417 if (dsp_timestretch_available())
419 /* increase the multiple to increase precision of this calculation */
420 new_stretch = GET_STRETCH(new_pitch, speed);
421 if(new_stretch < STRETCH_MIN)
423 /* we have to ignore allow_cutoff, because we can't have the */
424 /* stretch go higher than STRETCH_MAX */
425 new_pitch = GET_PITCH(speed, STRETCH_MIN);
427 else if(new_stretch > STRETCH_MAX)
429 /* we have to ignore allow_cutoff, because we can't have the */
430 /* stretch go higher than STRETCH_MAX */
431 new_pitch = GET_PITCH(speed, STRETCH_MAX);
434 if(new_stretch >= STRETCH_MAX ||
435 new_stretch <= STRETCH_MIN)
437 at_limit = true;
440 #endif
442 sound_set_pitch(new_pitch);
444 return new_pitch;
447 static int32_t get_semitone_from_pitch(int32_t pitch)
449 int semitone = 0;
450 int32_t fractional_index = 0;
452 while(semitone < NUM_SEMITONES - 1 &&
453 pitch >= semitone_table[semitone + 1])
455 semitone++;
459 /* now find the fractional part */
460 while(pitch > (cent_interp[fractional_index + 1] *
461 semitone_table[semitone] / PITCH_SPEED_100))
463 /* Check to make sure fractional_index isn't too big */
464 /* This should never happen. */
465 if(fractional_index >= CENT_INTERP_NUM - 1)
467 break;
469 fractional_index++;
472 int32_t semitone_pitch_a = cent_interp[fractional_index] *
473 semitone_table[semitone] /
474 PITCH_SPEED_100;
475 int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
476 semitone_table[semitone] /
477 PITCH_SPEED_100;
478 /* this will be the integer offset from the cent_interp entry */
479 int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
480 (semitone_pitch_b - semitone_pitch_a);
481 semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
482 fractional_index * CENT_INTERP_INTERVAL +
483 semitone_frac_ofs;
485 return semitone;
488 static int32_t get_pitch_from_semitone(int32_t semitone)
490 int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
492 /* Find the index into the semitone table */
493 int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
495 /* set pitch to the semitone's integer part value */
496 int32_t pitch = semitone_table[semitone_index];
497 /* get the range of the cent modification for future calculation */
498 int32_t pitch_mod_a =
499 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
500 CENT_INTERP_INTERVAL];
501 int32_t pitch_mod_b =
502 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
503 CENT_INTERP_INTERVAL + 1];
504 /* figure out the cent mod amount based on the semitone fractional value */
505 int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
506 (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
508 /* modify pitch based on the mod amount we just calculated */
509 return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
512 static int32_t pitch_increase_semitone(int32_t pitch,
513 int32_t current_semitone,
514 int32_t semitone_delta
515 #if CONFIG_CODEC == SWCODEC
516 , int32_t speed
517 #endif
520 int32_t new_semitone = current_semitone;
522 /* snap to the delta interval */
523 if(current_semitone % semitone_delta != 0)
525 if(current_semitone > 0 && semitone_delta > 0)
526 new_semitone += semitone_delta;
527 else if(current_semitone < 0 && semitone_delta < 0)
528 new_semitone += semitone_delta;
530 new_semitone -= new_semitone % semitone_delta;
532 else
533 new_semitone += semitone_delta;
535 /* clamp the pitch so it doesn't go beyond the pitch limits */
536 if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
538 new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
539 at_limit = true;
541 else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
543 new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
544 at_limit = true;
547 int32_t new_pitch = get_pitch_from_semitone(new_semitone);
549 #if CONFIG_CODEC == SWCODEC
550 int32_t new_stretch = GET_STRETCH(new_pitch, speed);
552 /* clamp the pitch so it doesn't go beyond the stretch limits */
553 if( new_stretch > STRETCH_MAX)
555 new_pitch = GET_PITCH(speed, STRETCH_MAX);
556 new_semitone = get_semitone_from_pitch(new_pitch);
557 at_limit = true;
559 else if (new_stretch < STRETCH_MIN)
561 new_pitch = GET_PITCH(speed, STRETCH_MIN);
562 new_semitone = get_semitone_from_pitch(new_pitch);
563 at_limit = true;
565 #endif
567 pitch_increase(pitch, new_pitch - pitch, false
568 #if CONFIG_CODEC == SWCODEC
569 , speed
570 #endif
573 return new_semitone;
577 returns:
578 0 on exit
579 1 if USB was connected
582 int gui_syncpitchscreen_run(void)
584 int button, i;
585 int32_t pitch = sound_get_pitch();
586 int32_t semitone;
588 int32_t new_pitch;
589 int32_t pitch_delta;
590 bool nudged = false;
591 bool exit = false;
592 /* should maybe be passed per parameter later, not needed for now */
593 struct viewport parent[NB_SCREENS];
594 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
595 int max_lines[NB_SCREENS];
597 #if CONFIG_CODEC == SWCODEC
598 int32_t new_speed = 0, new_stretch;
600 /* the speed variable holds the apparent speed of the playback */
601 int32_t speed;
602 if (dsp_timestretch_available())
604 speed = GET_SPEED(pitch, dsp_get_timestretch());
606 else
608 speed = pitch;
611 /* Figure out whether to be in timestretch mode */
612 if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
614 global_settings.pitch_mode_timestretch = false;
615 settings_save();
617 #endif
619 /* set the semitone index based on the current pitch */
620 semitone = get_semitone_from_pitch(pitch);
622 /* initialize pitchscreen vps */
623 FOR_NB_SCREENS(i)
625 viewport_set_defaults(&parent[i], i);
626 max_lines[i] = viewport_get_nb_lines(&parent[i]);
627 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
628 screens[i].set_viewport(&parent[i]);
629 screens[i].clear_viewport();
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;