Bump version numbers for 3.13
[maemo-rb.git] / apps / gui / pitchscreen.c
blob200033bbf9b2d328f5da8abc0dc2f808be739d35
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 "sound.h"
30 #include "pcmbuf.h"
31 #include "lang.h"
32 #include "icons.h"
33 #include "screens.h"
34 #include "viewport.h"
35 #include "font.h"
36 #include "system.h"
37 #include "misc.h"
38 #include "pitchscreen.h"
39 #include "settings.h"
40 #if CONFIG_CODEC == SWCODEC
41 #include "tdspeed.h"
42 #endif
44 #define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
45 /* on both sides when drawing */
47 #define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
48 #define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
49 #define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
50 #define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
51 #define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
53 #define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
54 #define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
56 #define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
57 #define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
59 enum
61 PITCH_TOP = 0,
62 PITCH_MID,
63 PITCH_BOTTOM,
64 PITCH_ITEM_COUNT,
68 /* This is a table of semitone percentage values of the appropriate
69 precision (based on PITCH_SPEED_PRECISION). Note that these are
70 all constant expressions, which will be evaluated at compile time,
71 so no need to worry about how complex the expressions look.
72 That's just to get the precision right.
74 I calculated these values, starting from 50, as
76 x(n) = 50 * 2^(n/12)
78 All that math in each entry simply converts the float constant
79 to an integer equal to PITCH_SPEED_PRECISION times the float value,
80 with as little precision loss as possible (i.e. correctly rounding
81 the last digit).
83 #define TO_INT_WITH_PRECISION(x) \
84 ( (unsigned short)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) )
86 static const unsigned short semitone_table[] =
88 TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */
89 TO_INT_WITH_PRECISION(52.97315472),
90 TO_INT_WITH_PRECISION(56.12310242),
91 TO_INT_WITH_PRECISION(59.46035575),
92 TO_INT_WITH_PRECISION(62.99605249),
93 TO_INT_WITH_PRECISION(66.74199271),
94 TO_INT_WITH_PRECISION(70.71067812),
95 TO_INT_WITH_PRECISION(74.91535384),
96 TO_INT_WITH_PRECISION(79.37005260),
97 TO_INT_WITH_PRECISION(84.08964153),
98 TO_INT_WITH_PRECISION(89.08987181),
99 TO_INT_WITH_PRECISION(94.38743127),
100 TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */
101 TO_INT_WITH_PRECISION(105.9463094),
102 TO_INT_WITH_PRECISION(112.2462048),
103 TO_INT_WITH_PRECISION(118.9207115),
104 TO_INT_WITH_PRECISION(125.9921049),
105 TO_INT_WITH_PRECISION(133.4839854),
106 TO_INT_WITH_PRECISION(141.4213562),
107 TO_INT_WITH_PRECISION(149.8307077),
108 TO_INT_WITH_PRECISION(158.7401052),
109 TO_INT_WITH_PRECISION(168.1792831),
110 TO_INT_WITH_PRECISION(178.1797436),
111 TO_INT_WITH_PRECISION(188.7748625),
112 TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */
115 #define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0])))
116 #define SEMITONE_END (NUM_SEMITONES/2)
117 #define SEMITONE_START (-SEMITONE_END)
119 /* A table of values for approximating the cent curve with
120 linear interpolation. Multipy the next lowest semitone
121 by this much to find the corresponding cent percentage.
123 These values were calculated as
124 x(n) = 100 * 2^(n * 20/1200)
127 static const unsigned short cent_interp[] =
129 TO_INT_WITH_PRECISION(100.0000000),
130 TO_INT_WITH_PRECISION(101.1619440),
131 TO_INT_WITH_PRECISION(102.3373892),
132 TO_INT_WITH_PRECISION(103.5264924),
133 TO_INT_WITH_PRECISION(104.7294123),
134 /* this one's the next semitone but we have it here for convenience */
135 TO_INT_WITH_PRECISION(105.9463094),
138 /* Number of cents between entries in the cent_interp table */
139 #define CENT_INTERP_INTERVAL 20
140 #define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0])))
142 /* This stores whether the pitch and speed are at their own limits */
143 /* or that of the timestretching algorithm */
144 static bool at_limit = false;
148 * The pitchscreen is divided into 3 viewports (each row is a viewport)
149 * Then each viewport is again divided into 3 colums, each showsing some infos
150 * Additionally, on touchscreen, each cell represents a button
152 * Below a sketch describing what each cell will show (what's drawn on it)
153 * --------------------------
154 * | | | | <-- pitch up in the middle (text and button)
155 * | | | | <-- arrows for mode toggling on the sides for touchscreen
156 * |------------------------|
157 * | | | | <-- semitone/speed up/down on the sides
158 * | | | | <-- reset pitch&speed in the middle
159 * |------------------------|
160 * | | | | <-- pitch down in the middle
161 * | | | | <-- Two "OK" for exit on the sides for touchscreen
162 * |------------------------|
168 * Fixes the viewports so they represent the 3 rows, and adds a little margin
169 * on all sides for the icons (which are drawn outside of the grid
171 * The modified viewports need to be passed to the touchscreen handling function
173 static void pitchscreen_fix_viewports(struct viewport *parent,
174 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
176 int i, font_height;
177 font_height = font_get(parent->font)->height;
178 for (i = 0; i < PITCH_ITEM_COUNT; i++)
180 pitch_viewports[i] = *parent;
181 pitch_viewports[i].height = parent->height / PITCH_ITEM_COUNT;
182 pitch_viewports[i].x += ICON_BORDER;
183 pitch_viewports[i].width -= 2*ICON_BORDER;
185 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
186 pitch_viewports[PITCH_TOP].height -= ICON_BORDER;
188 if(pitch_viewports[PITCH_MID].height < font_height * 2)
189 pitch_viewports[PITCH_MID].height = font_height * 2;
191 pitch_viewports[PITCH_MID].y = pitch_viewports[PITCH_TOP].y
192 + pitch_viewports[PITCH_TOP].height;
194 pitch_viewports[PITCH_BOTTOM].y = pitch_viewports[PITCH_MID].y
195 + pitch_viewports[PITCH_MID].height;
197 pitch_viewports[PITCH_BOTTOM].height -= ICON_BORDER;
200 /* must be called before pitchscreen_draw, or within
201 * since it neither clears nor updates the display */
202 static void pitchscreen_draw_icons(struct screen *display,
203 struct viewport *parent)
205 display->set_viewport(parent);
206 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
207 parent->width/2 - 3,
208 2, 7, 8);
209 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
210 parent->width /2 - 3,
211 parent->height - 10, 7, 8);
212 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
213 parent->width - 10,
214 parent->height /2 - 4, 7, 8);
215 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
217 parent->height /2 - 4, 7, 8);
218 display->update_viewport();
221 static void pitchscreen_draw(struct screen *display, int max_lines,
222 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
223 int32_t pitch, int32_t semitone
224 #if CONFIG_CODEC == SWCODEC
225 ,int32_t speed
226 #endif
229 const char* ptr;
230 char buf[32];
231 int w, h;
232 bool show_lang_pitch;
234 /* "Pitch up/Pitch down" - hide for a small screen,
235 * the text is drawn centered automatically
237 * note: this assumes 5 lines always fit on a touchscreen (should be
238 * reasonable) */
239 if (max_lines >= 5)
241 int w, h;
242 struct viewport *vp = &pitch_viewports[PITCH_TOP];
243 display->set_viewport(vp);
244 display->clear_viewport();
245 #ifdef HAVE_TOUCHSCREEN
246 /* two arrows in the top row, left and right column */
247 char *arrows[] = { "<", ">"};
248 display->getstringsize(arrows[0], &w, &h);
249 display->putsxy(0, vp->height/2 - h/2, arrows[0]);
250 display->putsxy(vp->width - w, vp->height/2 - h/2, arrows[1]);
251 #endif
252 /* UP: Pitch Up */
253 if (global_settings.pitch_mode_semitone)
254 ptr = str(LANG_PITCH_UP_SEMITONE);
255 else
256 ptr = str(LANG_PITCH_UP);
258 display->getstringsize(ptr, &w, NULL);
259 /* draw text */
260 display->putsxy(vp->width/2 - w/2, 0, ptr);
261 display->update_viewport();
263 /* DOWN: Pitch Down */
264 vp = &pitch_viewports[PITCH_BOTTOM];
265 display->set_viewport(vp);
266 display->clear_viewport();
268 #ifdef HAVE_TOUCHSCREEN
269 ptr = str(LANG_KBD_OK);
270 display->getstringsize(ptr, &w, &h);
271 /* one OK in the middle first column of the vp (at half height) */
272 display->putsxy(vp->width/6 - w/2, vp->height/2 - h/2, ptr);
273 /* one OK in the middle of the last column of the vp (at half height) */
274 display->putsxy(5*vp->width/6 - w/2, vp->height/2 - h/2, ptr);
275 #endif
276 if (global_settings.pitch_mode_semitone)
277 ptr = str(LANG_PITCH_DOWN_SEMITONE);
278 else
279 ptr = str(LANG_PITCH_DOWN);
280 display->getstringsize(ptr, &w, &h);
281 /* draw text */
282 display->putsxy(vp->width/2 - w/2, vp->height - h, ptr);
283 display->update_viewport();
286 /* Middle section */
287 display->set_viewport(&pitch_viewports[PITCH_MID]);
288 display->clear_viewport();
289 int width_used = 0;
291 /* Middle section upper line - hide for a small screen */
292 if ((show_lang_pitch = (max_lines >= 3)))
294 #if CONFIG_CODEC == SWCODEC
295 if(global_settings.pitch_mode_timestretch)
297 /* Pitch:XXX.X% */
298 if(global_settings.pitch_mode_semitone)
300 snprintf(buf, sizeof(buf), "%s: %s%d.%02d", str(LANG_PITCH),
301 semitone >= 0 ? "+" : "-",
302 abs(semitone / PITCH_SPEED_PRECISION),
303 abs((semitone % PITCH_SPEED_PRECISION) /
304 (PITCH_SPEED_PRECISION / 100))
307 else
309 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
310 pitch / PITCH_SPEED_PRECISION,
311 (pitch % PITCH_SPEED_PRECISION) /
312 (PITCH_SPEED_PRECISION / 10));
315 else
316 #endif
318 /* Rate */
319 snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
321 display->getstringsize(buf, &w, &h);
322 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
323 (pitch_viewports[PITCH_MID].height / 2) - h, buf);
324 if (w > width_used)
325 width_used = w;
328 /* Middle section lower line */
329 /* "Speed:XXX%" */
330 #if CONFIG_CODEC == SWCODEC
331 if(global_settings.pitch_mode_timestretch)
333 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
334 speed / PITCH_SPEED_PRECISION,
335 (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
337 else
338 #endif
340 if(global_settings.pitch_mode_semitone)
342 snprintf(buf, sizeof(buf), "%s%d.%02d",
343 semitone >= 0 ? "+" : "-",
344 abs(semitone / PITCH_SPEED_PRECISION),
345 abs((semitone % PITCH_SPEED_PRECISION) /
346 (PITCH_SPEED_PRECISION / 100))
349 else
351 snprintf(buf, sizeof(buf), "%ld.%ld%%",
352 pitch / PITCH_SPEED_PRECISION,
353 (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
357 display->getstringsize(buf, &w, &h);
358 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
359 show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
360 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
361 buf);
362 if (w > width_used)
363 width_used = w;
365 /* "limit" and "timestretch" labels */
366 if (max_lines >= 7)
368 if(at_limit)
370 const char * const p = str(LANG_STRETCH_LIMIT);
371 display->getstringsize(p, &w, &h);
372 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
373 (pitch_viewports[PITCH_MID].height / 2) + h, p);
374 if (w > width_used)
375 width_used = w;
379 /* Middle section left/right labels */
380 const char *leftlabel = "-2%";
381 const char *rightlabel = "+2%";
382 #if CONFIG_CODEC == SWCODEC
383 if (global_settings.pitch_mode_timestretch)
385 leftlabel = "<<";
386 rightlabel = ">>";
388 #endif
390 /* Only display if they fit */
391 display->getstringsize(leftlabel, &w, &h);
392 width_used += w;
393 display->getstringsize(rightlabel, &w, &h);
394 width_used += w;
396 if (width_used <= pitch_viewports[PITCH_MID].width)
398 display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
399 leftlabel);
400 display->putsxy((pitch_viewports[PITCH_MID].width - w),
401 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
402 rightlabel);
404 display->update_viewport();
405 display->set_viewport(NULL);
408 static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
409 #if CONFIG_CODEC == SWCODEC
410 /* need this to maintain correct pitch/speed caps */
411 , int32_t speed
412 #endif
415 int32_t new_pitch;
416 #if CONFIG_CODEC == SWCODEC
417 int32_t new_stretch;
418 #endif
419 at_limit = false;
421 if (pitch_delta < 0)
423 /* for large jumps, snap up to whole numbers */
424 if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
425 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
427 pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
430 new_pitch = pitch + pitch_delta;
432 if (new_pitch < PITCH_MIN)
434 if (!allow_cutoff)
436 return pitch;
438 new_pitch = PITCH_MIN;
439 at_limit = true;
442 else if (pitch_delta > 0)
444 /* for large jumps, snap down to whole numbers */
445 if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
446 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
448 pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
451 new_pitch = pitch + pitch_delta;
453 if (new_pitch > PITCH_MAX)
455 if (!allow_cutoff)
456 return pitch;
457 new_pitch = PITCH_MAX;
458 at_limit = true;
461 else
463 /* pitch_delta == 0 -> no real change */
464 return pitch;
466 #if CONFIG_CODEC == SWCODEC
467 if (dsp_timestretch_available())
469 /* increase the multiple to increase precision of this calculation */
470 new_stretch = GET_STRETCH(new_pitch, speed);
471 if(new_stretch < STRETCH_MIN)
473 /* we have to ignore allow_cutoff, because we can't have the */
474 /* stretch go higher than STRETCH_MAX */
475 new_pitch = GET_PITCH(speed, STRETCH_MIN);
477 else if(new_stretch > STRETCH_MAX)
479 /* we have to ignore allow_cutoff, because we can't have the */
480 /* stretch go higher than STRETCH_MAX */
481 new_pitch = GET_PITCH(speed, STRETCH_MAX);
484 if(new_stretch >= STRETCH_MAX ||
485 new_stretch <= STRETCH_MIN)
487 at_limit = true;
490 #endif
492 sound_set_pitch(new_pitch);
494 return new_pitch;
497 static int32_t get_semitone_from_pitch(int32_t pitch)
499 int semitone = 0;
500 int32_t fractional_index = 0;
502 while(semitone < NUM_SEMITONES - 1 &&
503 pitch >= semitone_table[semitone + 1])
505 semitone++;
509 /* now find the fractional part */
510 while(pitch > (cent_interp[fractional_index + 1] *
511 semitone_table[semitone] / PITCH_SPEED_100))
513 /* Check to make sure fractional_index isn't too big */
514 /* This should never happen. */
515 if(fractional_index >= CENT_INTERP_NUM - 1)
517 break;
519 fractional_index++;
522 int32_t semitone_pitch_a = cent_interp[fractional_index] *
523 semitone_table[semitone] /
524 PITCH_SPEED_100;
525 int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
526 semitone_table[semitone] /
527 PITCH_SPEED_100;
528 /* this will be the integer offset from the cent_interp entry */
529 int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
530 (semitone_pitch_b - semitone_pitch_a);
531 semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
532 fractional_index * CENT_INTERP_INTERVAL +
533 semitone_frac_ofs;
535 return semitone;
538 static int32_t get_pitch_from_semitone(int32_t semitone)
540 int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
542 /* Find the index into the semitone table */
543 int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
545 /* set pitch to the semitone's integer part value */
546 int32_t pitch = semitone_table[semitone_index];
547 /* get the range of the cent modification for future calculation */
548 int32_t pitch_mod_a =
549 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
550 CENT_INTERP_INTERVAL];
551 int32_t pitch_mod_b =
552 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
553 CENT_INTERP_INTERVAL + 1];
554 /* figure out the cent mod amount based on the semitone fractional value */
555 int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
556 (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
558 /* modify pitch based on the mod amount we just calculated */
559 return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
562 static int32_t pitch_increase_semitone(int32_t pitch,
563 int32_t current_semitone,
564 int32_t semitone_delta
565 #if CONFIG_CODEC == SWCODEC
566 , int32_t speed
567 #endif
570 int32_t new_semitone = current_semitone;
572 /* snap to the delta interval */
573 if(current_semitone % semitone_delta != 0)
575 if(current_semitone > 0 && semitone_delta > 0)
576 new_semitone += semitone_delta;
577 else if(current_semitone < 0 && semitone_delta < 0)
578 new_semitone += semitone_delta;
580 new_semitone -= new_semitone % semitone_delta;
582 else
583 new_semitone += semitone_delta;
585 /* clamp the pitch so it doesn't go beyond the pitch limits */
586 if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
588 new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
589 at_limit = true;
591 else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
593 new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
594 at_limit = true;
597 int32_t new_pitch = get_pitch_from_semitone(new_semitone);
599 #if CONFIG_CODEC == SWCODEC
600 int32_t new_stretch = GET_STRETCH(new_pitch, speed);
602 /* clamp the pitch so it doesn't go beyond the stretch limits */
603 if( new_stretch > STRETCH_MAX)
605 new_pitch = GET_PITCH(speed, STRETCH_MAX);
606 new_semitone = get_semitone_from_pitch(new_pitch);
607 at_limit = true;
609 else if (new_stretch < STRETCH_MIN)
611 new_pitch = GET_PITCH(speed, STRETCH_MIN);
612 new_semitone = get_semitone_from_pitch(new_pitch);
613 at_limit = true;
615 #endif
617 pitch_increase(pitch, new_pitch - pitch, false
618 #if CONFIG_CODEC == SWCODEC
619 , speed
620 #endif
623 return new_semitone;
626 #ifdef HAVE_TOUCHSCREEN
628 * Check for touchscreen presses as per sketch above in this file
630 * goes through each row of the, checks whether the touchscreen
631 * was pressed in it. Then it looks the columns of each row for specific actions
633 static int pitchscreen_do_touchscreen(struct viewport vps[])
635 short x, y;
636 struct viewport *this_vp = &vps[PITCH_TOP];
637 int ret;
638 static bool wait_for_release = false;
639 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
641 /* top row */
642 if (ret > ACTION_UNKNOWN)
643 { /* press on top row, left or right column
644 * only toggle mode if released */
645 int column = this_vp->width / 3;
646 if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
647 return ACTION_PS_TOGGLE_MODE;
650 else if (x >= column && x <= (2*column))
651 { /* center column pressed */
652 if (ret == BUTTON_REPEAT)
653 return ACTION_PS_INC_BIG;
654 else if (ret & BUTTON_REL)
655 return ACTION_PS_INC_SMALL;
657 return ACTION_NONE;
660 /* now the center row */
661 this_vp = &vps[PITCH_MID];
662 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
664 if (ret > ACTION_UNKNOWN)
666 int column = this_vp->width / 3;
668 if (x < column)
669 { /* left column */
670 if (ret & BUTTON_REL)
672 wait_for_release = false;
673 return ACTION_PS_NUDGE_LEFTOFF;
675 else if (ret & BUTTON_REPEAT)
676 return ACTION_PS_SLOWER;
677 if (!wait_for_release)
679 wait_for_release = true;
680 return ACTION_PS_NUDGE_LEFT;
683 else if (x > (2*column))
684 { /* right column */
685 if (ret & BUTTON_REL)
687 wait_for_release = false;
688 return ACTION_PS_NUDGE_RIGHTOFF;
690 else if (ret & BUTTON_REPEAT)
691 return ACTION_PS_FASTER;
692 if (!wait_for_release)
694 wait_for_release = true;
695 return ACTION_PS_NUDGE_RIGHT;
698 else
699 /* center column was pressed */
700 return ACTION_PS_RESET;
703 /* now the bottom row */
704 this_vp = &vps[PITCH_BOTTOM];
705 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
707 if (ret > ACTION_UNKNOWN)
709 int column = this_vp->width / 3;
711 /* left or right column is exit */
712 if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
713 return ACTION_PS_EXIT;
714 else if (x >= column && x <= (2*column))
715 { /* center column was pressed */
716 if (ret & BUTTON_REPEAT)
717 return ACTION_PS_DEC_BIG;
718 else if (ret & BUTTON_REL)
719 return ACTION_PS_DEC_SMALL;
721 return ACTION_NONE;
723 return ACTION_NONE;
726 #endif
728 returns:
729 0 on exit
730 1 if USB was connected
733 int gui_syncpitchscreen_run(void)
735 int button;
736 int32_t pitch = sound_get_pitch();
737 int32_t semitone;
739 int32_t new_pitch;
740 int32_t pitch_delta;
741 bool nudged = false;
742 bool exit = false;
743 /* should maybe be passed per parameter later, not needed for now */
744 struct viewport parent[NB_SCREENS];
745 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
746 int max_lines[NB_SCREENS];
748 push_current_activity(ACTIVITY_PITCHSCREEN);
750 #if CONFIG_CODEC == SWCODEC
751 int32_t new_speed = 0, new_stretch;
753 /* the speed variable holds the apparent speed of the playback */
754 int32_t speed;
755 if (dsp_timestretch_available())
757 speed = GET_SPEED(pitch, dsp_get_timestretch());
759 else
761 speed = pitch;
764 /* Figure out whether to be in timestretch mode */
765 if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
767 global_settings.pitch_mode_timestretch = false;
768 settings_save();
770 #endif
772 /* set the semitone index based on the current pitch */
773 semitone = get_semitone_from_pitch(pitch);
775 /* initialize pitchscreen vps */
776 FOR_NB_SCREENS(i)
778 viewport_set_defaults(&parent[i], i);
779 max_lines[i] = viewport_get_nb_lines(&parent[i]);
780 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
781 screens[i].set_viewport(&parent[i]);
782 screens[i].clear_viewport();
784 /* also, draw the icons now, it's only needed once */
785 pitchscreen_draw_icons(&screens[i], &parent[i]);
787 #if CONFIG_CODEC == SWCODEC
788 pcmbuf_set_low_latency(true);
789 #endif
791 while (!exit)
793 FOR_NB_SCREENS(i)
794 pitchscreen_draw(&screens[i], max_lines[i],
795 pitch_viewports[i], pitch, semitone
796 #if CONFIG_CODEC == SWCODEC
797 , speed
798 #endif
800 pitch_delta = 0;
801 #if CONFIG_CODEC == SWCODEC
802 new_speed = 0;
803 #endif
804 button = get_action(CONTEXT_PITCHSCREEN, HZ);
806 #ifdef HAVE_TOUCHSCREEN
807 if (button == ACTION_TOUCHSCREEN)
809 FOR_NB_SCREENS(i)
810 button = pitchscreen_do_touchscreen(pitch_viewports[i]);
812 #endif
813 switch (button)
815 case ACTION_PS_INC_SMALL:
816 if(global_settings.pitch_mode_semitone)
817 pitch_delta = SEMITONE_SMALL_DELTA;
818 else
819 pitch_delta = PITCH_SMALL_DELTA;
820 break;
822 case ACTION_PS_INC_BIG:
823 if(global_settings.pitch_mode_semitone)
824 pitch_delta = SEMITONE_BIG_DELTA;
825 else
826 pitch_delta = PITCH_BIG_DELTA;
827 break;
829 case ACTION_PS_DEC_SMALL:
830 if(global_settings.pitch_mode_semitone)
831 pitch_delta = -SEMITONE_SMALL_DELTA;
832 else
833 pitch_delta = -PITCH_SMALL_DELTA;
834 break;
836 case ACTION_PS_DEC_BIG:
837 if(global_settings.pitch_mode_semitone)
838 pitch_delta = -SEMITONE_BIG_DELTA;
839 else
840 pitch_delta = -PITCH_BIG_DELTA;
841 break;
843 case ACTION_PS_NUDGE_RIGHT:
844 #if CONFIG_CODEC == SWCODEC
845 if (!global_settings.pitch_mode_timestretch)
847 #endif
848 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
849 #if CONFIG_CODEC == SWCODEC
850 , speed
851 #endif
853 nudged = (new_pitch != pitch);
854 pitch = new_pitch;
855 semitone = get_semitone_from_pitch(pitch);
856 #if CONFIG_CODEC == SWCODEC
857 speed = pitch;
858 #endif
859 break;
860 #if CONFIG_CODEC == SWCODEC
862 else
864 new_speed = speed + SPEED_SMALL_DELTA;
865 at_limit = false;
867 break;
869 case ACTION_PS_FASTER:
870 if (global_settings.pitch_mode_timestretch)
872 new_speed = speed + SPEED_BIG_DELTA;
873 /* snap to whole numbers */
874 if(new_speed % PITCH_SPEED_PRECISION != 0)
875 new_speed -= new_speed % PITCH_SPEED_PRECISION;
876 at_limit = false;
878 break;
879 #endif
881 case ACTION_PS_NUDGE_RIGHTOFF:
882 if (nudged)
884 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
885 #if CONFIG_CODEC == SWCODEC
886 , speed
887 #endif
889 #if CONFIG_CODEC == SWCODEC
890 speed = pitch;
891 #endif
892 semitone = get_semitone_from_pitch(pitch);
893 nudged = false;
895 break;
897 case ACTION_PS_NUDGE_LEFT:
898 #if CONFIG_CODEC == SWCODEC
899 if (!global_settings.pitch_mode_timestretch)
901 #endif
902 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
903 #if CONFIG_CODEC == SWCODEC
904 , speed
905 #endif
907 nudged = (new_pitch != pitch);
908 pitch = new_pitch;
909 semitone = get_semitone_from_pitch(pitch);
910 #if CONFIG_CODEC == SWCODEC
911 speed = pitch;
912 #endif
913 break;
914 #if CONFIG_CODEC == SWCODEC
916 else
918 new_speed = speed - SPEED_SMALL_DELTA;
919 at_limit = false;
921 break;
923 case ACTION_PS_SLOWER:
924 if (global_settings.pitch_mode_timestretch)
926 new_speed = speed - SPEED_BIG_DELTA;
927 /* snap to whole numbers */
928 if(new_speed % PITCH_SPEED_PRECISION != 0)
929 new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
930 at_limit = false;
932 break;
933 #endif
935 case ACTION_PS_NUDGE_LEFTOFF:
936 if (nudged)
938 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
939 #if CONFIG_CODEC == SWCODEC
940 , speed
941 #endif
943 #if CONFIG_CODEC == SWCODEC
944 speed = pitch;
945 #endif
946 semitone = get_semitone_from_pitch(pitch);
947 nudged = false;
949 break;
951 case ACTION_PS_RESET:
952 pitch = PITCH_SPEED_100;
953 sound_set_pitch(pitch);
954 #if CONFIG_CODEC == SWCODEC
955 speed = PITCH_SPEED_100;
956 if (dsp_timestretch_available())
958 dsp_set_timestretch(PITCH_SPEED_100);
959 at_limit = false;
961 #endif
962 semitone = get_semitone_from_pitch(pitch);
963 break;
965 case ACTION_PS_TOGGLE_MODE:
966 global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
967 #if CONFIG_CODEC == SWCODEC
969 if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
971 global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
972 if(!global_settings.pitch_mode_timestretch)
974 /* no longer in timestretch mode. Reset speed */
975 speed = pitch;
976 dsp_set_timestretch(PITCH_SPEED_100);
979 settings_save();
980 #endif
981 break;
983 case ACTION_PS_EXIT:
984 exit = true;
985 break;
987 default:
988 if (default_event_handler(button) == SYS_USB_CONNECTED)
989 return 1;
990 break;
992 if (pitch_delta)
994 if (global_settings.pitch_mode_semitone)
996 semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
997 #if CONFIG_CODEC == SWCODEC
998 , speed
999 #endif
1001 pitch = get_pitch_from_semitone(semitone);
1003 else
1005 pitch = pitch_increase(pitch, pitch_delta, true
1006 #if CONFIG_CODEC == SWCODEC
1007 , speed
1008 #endif
1010 semitone = get_semitone_from_pitch(pitch);
1012 #if CONFIG_CODEC == SWCODEC
1013 if (global_settings.pitch_mode_timestretch)
1015 /* do this to make sure we properly obey the stretch limits */
1016 new_speed = speed;
1018 else
1020 speed = pitch;
1022 #endif
1025 #if CONFIG_CODEC == SWCODEC
1026 if(new_speed)
1028 new_stretch = GET_STRETCH(pitch, new_speed);
1030 /* limit the amount of stretch */
1031 if(new_stretch > STRETCH_MAX)
1033 new_stretch = STRETCH_MAX;
1034 new_speed = GET_SPEED(pitch, new_stretch);
1036 else if(new_stretch < STRETCH_MIN)
1038 new_stretch = STRETCH_MIN;
1039 new_speed = GET_SPEED(pitch, new_stretch);
1042 new_stretch = GET_STRETCH(pitch, new_speed);
1043 if(new_stretch >= STRETCH_MAX ||
1044 new_stretch <= STRETCH_MIN)
1046 at_limit = true;
1049 /* set the amount of stretch */
1050 dsp_set_timestretch(new_stretch);
1052 /* update the speed variable with the new speed */
1053 speed = new_speed;
1055 /* Reset new_speed so we only call dsp_set_timestretch */
1056 /* when needed */
1057 new_speed = 0;
1059 #endif
1061 #if CONFIG_CODEC == SWCODEC
1062 pcmbuf_set_low_latency(false);
1063 #endif
1064 pop_current_activity();
1065 return 0;