Add "elfzip" target to make which creates a zip of all elf files, as mapzip does...
[maemo-rb.git] / apps / gui / pitchscreen.c
blob5620de47e869b114c92650f0750411c3a39879c2
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;
149 * The pitchscreen is divided into 3 viewports (each row is a viewport)
150 * Then each viewport is again divided into 3 colums, each showsing some infos
151 * Additionally, on touchscreen, each cell represents a button
153 * Below a sketch describing what each cell will show (what's drawn on it)
154 * --------------------------
155 * | | | | <-- pitch up in the middle (text and button)
156 * | | | | <-- arrows for mode toggling on the sides for touchscreen
157 * |------------------------|
158 * | | | | <-- semitone/speed up/down on the sides
159 * | | | | <-- reset pitch&speed in the middle
160 * |------------------------|
161 * | | | | <-- pitch down in the middle
162 * | | | | <-- Two "OK" for exit on the sides for touchscreen
163 * |------------------------|
169 * Fixes the viewports so they represent the 3 rows, and adds a little margin
170 * on all sides for the icons (which are drawn outside of the grid
172 * The modified viewports need to be passed to the touchscreen handling function
174 static void pitchscreen_fix_viewports(struct viewport *parent,
175 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
177 int i, font_height;
178 font_height = font_get(parent->font)->height;
179 for (i = 0; i < PITCH_ITEM_COUNT; i++)
181 pitch_viewports[i] = *parent;
182 pitch_viewports[i].height = parent->height / PITCH_ITEM_COUNT;
183 pitch_viewports[i].x += ICON_BORDER;
184 pitch_viewports[i].width -= 2*ICON_BORDER;
186 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
187 pitch_viewports[PITCH_TOP].height -= ICON_BORDER;
189 if(pitch_viewports[PITCH_MID].height < font_height * 2)
190 pitch_viewports[PITCH_MID].height = font_height * 2;
192 pitch_viewports[PITCH_MID].y = pitch_viewports[PITCH_TOP].y
193 + pitch_viewports[PITCH_TOP].height;
195 pitch_viewports[PITCH_BOTTOM].y = pitch_viewports[PITCH_MID].y
196 + pitch_viewports[PITCH_MID].height;
198 pitch_viewports[PITCH_BOTTOM].height -= ICON_BORDER;
201 /* must be called before pitchscreen_draw, or within
202 * since it neither clears nor updates the display */
203 static void pitchscreen_draw_icons(struct screen *display,
204 struct viewport *parent)
206 display->set_viewport(parent);
207 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
208 parent->width/2 - 3,
209 2, 7, 8);
210 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
211 parent->width /2 - 3,
212 parent->height - 10, 7, 8);
213 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
214 parent->width - 10,
215 parent->height /2 - 4, 7, 8);
216 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
218 parent->height /2 - 4, 7, 8);
219 display->update_viewport();
222 static void pitchscreen_draw(struct screen *display, int max_lines,
223 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
224 int32_t pitch, int32_t semitone
225 #if CONFIG_CODEC == SWCODEC
226 ,int32_t speed
227 #endif
230 const char* ptr;
231 char buf[32];
232 int w, h;
233 bool show_lang_pitch;
235 /* "Pitch up/Pitch down" - hide for a small screen,
236 * the text is drawn centered automatically
238 * note: this assumes 5 lines always fit on a touchscreen (should be
239 * reasonable) */
240 if (max_lines >= 5)
242 int w, h;
243 struct viewport *vp = &pitch_viewports[PITCH_TOP];
244 display->set_viewport(vp);
245 display->clear_viewport();
246 #ifdef HAVE_TOUCHSCREEN
247 /* two arrows in the top row, left and right column */
248 char *arrows[] = { "<", ">"};
249 display->getstringsize(arrows[0], &w, &h);
250 display->putsxy(0, vp->height/2 - h/2, arrows[0]);
251 display->putsxy(vp->width - w, vp->height/2 - h/2, arrows[1]);
252 #endif
253 /* UP: Pitch Up */
254 if (global_settings.pitch_mode_semitone)
255 ptr = str(LANG_PITCH_UP_SEMITONE);
256 else
257 ptr = str(LANG_PITCH_UP);
259 display->getstringsize(ptr, &w, NULL);
260 /* draw text */
261 display->putsxy(vp->width/2 - w/2, 0, ptr);
262 display->update_viewport();
264 /* DOWN: Pitch Down */
265 vp = &pitch_viewports[PITCH_BOTTOM];
266 display->set_viewport(vp);
267 display->clear_viewport();
269 #ifdef HAVE_TOUCHSCREEN
270 ptr = str(LANG_KBD_OK);
271 display->getstringsize(ptr, &w, &h);
272 /* one OK in the middle first column of the vp (at half height) */
273 display->putsxy(vp->width/6 - w/2, vp->height/2 - h/2, ptr);
274 /* one OK in the middle of the last column of the vp (at half height) */
275 display->putsxy(5*vp->width/6 - w/2, vp->height/2 - h/2, ptr);
276 #endif
277 if (global_settings.pitch_mode_semitone)
278 ptr = str(LANG_PITCH_DOWN_SEMITONE);
279 else
280 ptr = str(LANG_PITCH_DOWN);
281 display->getstringsize(ptr, &w, &h);
282 /* draw text */
283 display->putsxy(vp->width/2 - w/2, vp->height - h, ptr);
284 display->update_viewport();
287 /* Middle section */
288 display->set_viewport(&pitch_viewports[PITCH_MID]);
289 display->clear_viewport();
290 int width_used = 0;
292 /* Middle section upper line - hide for a small screen */
293 if ((show_lang_pitch = (max_lines >= 3)))
295 #if CONFIG_CODEC == SWCODEC
296 if(global_settings.pitch_mode_timestretch)
298 /* Pitch:XXX.X% */
299 if(global_settings.pitch_mode_semitone)
301 snprintf(buf, sizeof(buf), "%s: %s%d.%02d", str(LANG_PITCH),
302 semitone >= 0 ? "+" : "-",
303 abs(semitone / PITCH_SPEED_PRECISION),
304 abs((semitone % PITCH_SPEED_PRECISION) /
305 (PITCH_SPEED_PRECISION / 100))
308 else
310 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
311 pitch / PITCH_SPEED_PRECISION,
312 (pitch % PITCH_SPEED_PRECISION) /
313 (PITCH_SPEED_PRECISION / 10));
316 else
317 #endif
319 /* Rate */
320 snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
322 display->getstringsize(buf, &w, &h);
323 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
324 (pitch_viewports[PITCH_MID].height / 2) - h, buf);
325 if (w > width_used)
326 width_used = w;
329 /* Middle section lower line */
330 /* "Speed:XXX%" */
331 #if CONFIG_CODEC == SWCODEC
332 if(global_settings.pitch_mode_timestretch)
334 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
335 speed / PITCH_SPEED_PRECISION,
336 (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
338 else
339 #endif
341 if(global_settings.pitch_mode_semitone)
343 snprintf(buf, sizeof(buf), "%s%d.%02d",
344 semitone >= 0 ? "+" : "-",
345 abs(semitone / PITCH_SPEED_PRECISION),
346 abs((semitone % PITCH_SPEED_PRECISION) /
347 (PITCH_SPEED_PRECISION / 100))
350 else
352 snprintf(buf, sizeof(buf), "%ld.%ld%%",
353 pitch / PITCH_SPEED_PRECISION,
354 (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
358 display->getstringsize(buf, &w, &h);
359 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
360 show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
361 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
362 buf);
363 if (w > width_used)
364 width_used = w;
366 /* "limit" and "timestretch" labels */
367 if (max_lines >= 7)
369 if(at_limit)
371 const char * const p = str(LANG_STRETCH_LIMIT);
372 display->getstringsize(p, &w, &h);
373 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
374 (pitch_viewports[PITCH_MID].height / 2) + h, p);
375 if (w > width_used)
376 width_used = w;
380 /* Middle section left/right labels */
381 const char *leftlabel = "-2%";
382 const char *rightlabel = "+2%";
383 #if CONFIG_CODEC == SWCODEC
384 if (global_settings.pitch_mode_timestretch)
386 leftlabel = "<<";
387 rightlabel = ">>";
389 #endif
391 /* Only display if they fit */
392 display->getstringsize(leftlabel, &w, &h);
393 width_used += w;
394 display->getstringsize(rightlabel, &w, &h);
395 width_used += w;
397 if (width_used <= pitch_viewports[PITCH_MID].width)
399 display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
400 leftlabel);
401 display->putsxy((pitch_viewports[PITCH_MID].width - w),
402 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
403 rightlabel);
405 display->update_viewport();
406 display->set_viewport(NULL);
409 static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
410 #if CONFIG_CODEC == SWCODEC
411 /* need this to maintain correct pitch/speed caps */
412 , int32_t speed
413 #endif
416 int32_t new_pitch;
417 #if CONFIG_CODEC == SWCODEC
418 int32_t new_stretch;
419 #endif
420 at_limit = false;
422 if (pitch_delta < 0)
424 /* for large jumps, snap up to whole numbers */
425 if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
426 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
428 pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
431 new_pitch = pitch + pitch_delta;
433 if (new_pitch < PITCH_MIN)
435 if (!allow_cutoff)
437 return pitch;
439 new_pitch = PITCH_MIN;
440 at_limit = true;
443 else if (pitch_delta > 0)
445 /* for large jumps, snap down to whole numbers */
446 if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
447 (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
449 pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
452 new_pitch = pitch + pitch_delta;
454 if (new_pitch > PITCH_MAX)
456 if (!allow_cutoff)
457 return pitch;
458 new_pitch = PITCH_MAX;
459 at_limit = true;
462 else
464 /* pitch_delta == 0 -> no real change */
465 return pitch;
467 #if CONFIG_CODEC == SWCODEC
468 if (dsp_timestretch_available())
470 /* increase the multiple to increase precision of this calculation */
471 new_stretch = GET_STRETCH(new_pitch, speed);
472 if(new_stretch < STRETCH_MIN)
474 /* we have to ignore allow_cutoff, because we can't have the */
475 /* stretch go higher than STRETCH_MAX */
476 new_pitch = GET_PITCH(speed, STRETCH_MIN);
478 else if(new_stretch > STRETCH_MAX)
480 /* we have to ignore allow_cutoff, because we can't have the */
481 /* stretch go higher than STRETCH_MAX */
482 new_pitch = GET_PITCH(speed, STRETCH_MAX);
485 if(new_stretch >= STRETCH_MAX ||
486 new_stretch <= STRETCH_MIN)
488 at_limit = true;
491 #endif
493 sound_set_pitch(new_pitch);
495 return new_pitch;
498 static int32_t get_semitone_from_pitch(int32_t pitch)
500 int semitone = 0;
501 int32_t fractional_index = 0;
503 while(semitone < NUM_SEMITONES - 1 &&
504 pitch >= semitone_table[semitone + 1])
506 semitone++;
510 /* now find the fractional part */
511 while(pitch > (cent_interp[fractional_index + 1] *
512 semitone_table[semitone] / PITCH_SPEED_100))
514 /* Check to make sure fractional_index isn't too big */
515 /* This should never happen. */
516 if(fractional_index >= CENT_INTERP_NUM - 1)
518 break;
520 fractional_index++;
523 int32_t semitone_pitch_a = cent_interp[fractional_index] *
524 semitone_table[semitone] /
525 PITCH_SPEED_100;
526 int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
527 semitone_table[semitone] /
528 PITCH_SPEED_100;
529 /* this will be the integer offset from the cent_interp entry */
530 int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
531 (semitone_pitch_b - semitone_pitch_a);
532 semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
533 fractional_index * CENT_INTERP_INTERVAL +
534 semitone_frac_ofs;
536 return semitone;
539 static int32_t get_pitch_from_semitone(int32_t semitone)
541 int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
543 /* Find the index into the semitone table */
544 int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
546 /* set pitch to the semitone's integer part value */
547 int32_t pitch = semitone_table[semitone_index];
548 /* get the range of the cent modification for future calculation */
549 int32_t pitch_mod_a =
550 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
551 CENT_INTERP_INTERVAL];
552 int32_t pitch_mod_b =
553 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
554 CENT_INTERP_INTERVAL + 1];
555 /* figure out the cent mod amount based on the semitone fractional value */
556 int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
557 (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
559 /* modify pitch based on the mod amount we just calculated */
560 return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
563 static int32_t pitch_increase_semitone(int32_t pitch,
564 int32_t current_semitone,
565 int32_t semitone_delta
566 #if CONFIG_CODEC == SWCODEC
567 , int32_t speed
568 #endif
571 int32_t new_semitone = current_semitone;
573 /* snap to the delta interval */
574 if(current_semitone % semitone_delta != 0)
576 if(current_semitone > 0 && semitone_delta > 0)
577 new_semitone += semitone_delta;
578 else if(current_semitone < 0 && semitone_delta < 0)
579 new_semitone += semitone_delta;
581 new_semitone -= new_semitone % semitone_delta;
583 else
584 new_semitone += semitone_delta;
586 /* clamp the pitch so it doesn't go beyond the pitch limits */
587 if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
589 new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
590 at_limit = true;
592 else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
594 new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
595 at_limit = true;
598 int32_t new_pitch = get_pitch_from_semitone(new_semitone);
600 #if CONFIG_CODEC == SWCODEC
601 int32_t new_stretch = GET_STRETCH(new_pitch, speed);
603 /* clamp the pitch so it doesn't go beyond the stretch limits */
604 if( new_stretch > STRETCH_MAX)
606 new_pitch = GET_PITCH(speed, STRETCH_MAX);
607 new_semitone = get_semitone_from_pitch(new_pitch);
608 at_limit = true;
610 else if (new_stretch < STRETCH_MIN)
612 new_pitch = GET_PITCH(speed, STRETCH_MIN);
613 new_semitone = get_semitone_from_pitch(new_pitch);
614 at_limit = true;
616 #endif
618 pitch_increase(pitch, new_pitch - pitch, false
619 #if CONFIG_CODEC == SWCODEC
620 , speed
621 #endif
624 return new_semitone;
627 #ifdef HAVE_TOUCHSCREEN
629 * Check for touchscreen presses as per sketch above in this file
631 * goes through each row of the, checks whether the touchscreen
632 * was pressed in it. Then it looks the columns of each row for specific actions
634 static int pitchscreen_do_touchscreen(struct viewport vps[])
636 short x, y;
637 struct viewport *this_vp = &vps[PITCH_TOP];
638 int ret;
639 static bool wait_for_release = false;
640 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
642 /* top row */
643 if (ret > ACTION_UNKNOWN)
644 { /* press on top row, left or right column
645 * only toggle mode if released */
646 int column = this_vp->width / 3;
647 if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
648 return ACTION_PS_TOGGLE_MODE;
651 else if (x >= column && x <= (2*column))
652 { /* center column pressed */
653 if (ret == BUTTON_REPEAT)
654 return ACTION_PS_INC_BIG;
655 else if (ret & BUTTON_REL)
656 return ACTION_PS_INC_SMALL;
658 return ACTION_NONE;
661 /* now the center row */
662 this_vp = &vps[PITCH_MID];
663 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
665 if (ret > ACTION_UNKNOWN)
667 int column = this_vp->width / 3;
669 if (x < column)
670 { /* left column */
671 if (ret & BUTTON_REL)
673 wait_for_release = false;
674 return ACTION_PS_NUDGE_LEFTOFF;
676 else if (ret & BUTTON_REPEAT)
677 return ACTION_PS_SLOWER;
678 if (!wait_for_release)
680 wait_for_release = true;
681 return ACTION_PS_NUDGE_LEFT;
684 else if (x > (2*column))
685 { /* right column */
686 if (ret & BUTTON_REL)
688 wait_for_release = false;
689 return ACTION_PS_NUDGE_RIGHTOFF;
691 else if (ret & BUTTON_REPEAT)
692 return ACTION_PS_FASTER;
693 if (!wait_for_release)
695 wait_for_release = true;
696 return ACTION_PS_NUDGE_RIGHT;
699 else
700 /* center column was pressed */
701 return ACTION_PS_RESET;
704 /* now the bottom row */
705 this_vp = &vps[PITCH_BOTTOM];
706 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
708 if (ret > ACTION_UNKNOWN)
710 int column = this_vp->width / 3;
712 /* left or right column is exit */
713 if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
714 return ACTION_PS_EXIT;
715 else if (x >= column && x <= (2*column))
716 { /* center column was pressed */
717 if (ret & BUTTON_REPEAT)
718 return ACTION_PS_DEC_BIG;
719 else if (ret & BUTTON_REL)
720 return ACTION_PS_DEC_SMALL;
722 return ACTION_NONE;
724 return ACTION_NONE;
727 #endif
729 returns:
730 0 on exit
731 1 if USB was connected
734 int gui_syncpitchscreen_run(void)
736 int button, i;
737 int32_t pitch = sound_get_pitch();
738 int32_t semitone;
740 int32_t new_pitch;
741 int32_t pitch_delta;
742 bool nudged = false;
743 bool exit = false;
744 /* should maybe be passed per parameter later, not needed for now */
745 struct viewport parent[NB_SCREENS];
746 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
747 int max_lines[NB_SCREENS];
749 #if CONFIG_CODEC == SWCODEC
750 int32_t new_speed = 0, new_stretch;
752 /* the speed variable holds the apparent speed of the playback */
753 int32_t speed;
754 if (dsp_timestretch_available())
756 speed = GET_SPEED(pitch, dsp_get_timestretch());
758 else
760 speed = pitch;
763 /* Figure out whether to be in timestretch mode */
764 if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
766 global_settings.pitch_mode_timestretch = false;
767 settings_save();
769 #endif
771 /* set the semitone index based on the current pitch */
772 semitone = get_semitone_from_pitch(pitch);
774 /* initialize pitchscreen vps */
775 FOR_NB_SCREENS(i)
777 viewport_set_defaults(&parent[i], i);
778 max_lines[i] = viewport_get_nb_lines(&parent[i]);
779 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
780 screens[i].set_viewport(&parent[i]);
781 screens[i].clear_viewport();
783 /* also, draw the icons now, it's only needed once */
784 pitchscreen_draw_icons(&screens[i], &parent[i]);
786 #if CONFIG_CODEC == SWCODEC
787 pcmbuf_set_low_latency(true);
788 #endif
790 while (!exit)
792 FOR_NB_SCREENS(i)
793 pitchscreen_draw(&screens[i], max_lines[i],
794 pitch_viewports[i], pitch, semitone
795 #if CONFIG_CODEC == SWCODEC
796 , speed
797 #endif
799 pitch_delta = 0;
800 #if CONFIG_CODEC == SWCODEC
801 new_speed = 0;
802 #endif
803 button = get_action(CONTEXT_PITCHSCREEN, HZ);
805 #ifdef HAVE_TOUCHSCREEN
806 if (button == ACTION_TOUCHSCREEN)
808 FOR_NB_SCREENS(i)
809 button = pitchscreen_do_touchscreen(pitch_viewports[i]);
811 #endif
812 switch (button)
814 case ACTION_PS_INC_SMALL:
815 if(global_settings.pitch_mode_semitone)
816 pitch_delta = SEMITONE_SMALL_DELTA;
817 else
818 pitch_delta = PITCH_SMALL_DELTA;
819 break;
821 case ACTION_PS_INC_BIG:
822 if(global_settings.pitch_mode_semitone)
823 pitch_delta = SEMITONE_BIG_DELTA;
824 else
825 pitch_delta = PITCH_BIG_DELTA;
826 break;
828 case ACTION_PS_DEC_SMALL:
829 if(global_settings.pitch_mode_semitone)
830 pitch_delta = -SEMITONE_SMALL_DELTA;
831 else
832 pitch_delta = -PITCH_SMALL_DELTA;
833 break;
835 case ACTION_PS_DEC_BIG:
836 if(global_settings.pitch_mode_semitone)
837 pitch_delta = -SEMITONE_BIG_DELTA;
838 else
839 pitch_delta = -PITCH_BIG_DELTA;
840 break;
842 case ACTION_PS_NUDGE_RIGHT:
843 #if CONFIG_CODEC == SWCODEC
844 if (!global_settings.pitch_mode_timestretch)
846 #endif
847 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
848 #if CONFIG_CODEC == SWCODEC
849 , speed
850 #endif
852 nudged = (new_pitch != pitch);
853 pitch = new_pitch;
854 semitone = get_semitone_from_pitch(pitch);
855 #if CONFIG_CODEC == SWCODEC
856 speed = pitch;
857 #endif
858 break;
859 #if CONFIG_CODEC == SWCODEC
861 else
863 new_speed = speed + SPEED_SMALL_DELTA;
864 at_limit = false;
866 break;
868 case ACTION_PS_FASTER:
869 if (global_settings.pitch_mode_timestretch)
871 new_speed = speed + SPEED_BIG_DELTA;
872 /* snap to whole numbers */
873 if(new_speed % PITCH_SPEED_PRECISION != 0)
874 new_speed -= new_speed % PITCH_SPEED_PRECISION;
875 at_limit = false;
877 break;
878 #endif
880 case ACTION_PS_NUDGE_RIGHTOFF:
881 if (nudged)
883 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
884 #if CONFIG_CODEC == SWCODEC
885 , speed
886 #endif
888 #if CONFIG_CODEC == SWCODEC
889 speed = pitch;
890 #endif
891 semitone = get_semitone_from_pitch(pitch);
892 nudged = false;
894 break;
896 case ACTION_PS_NUDGE_LEFT:
897 #if CONFIG_CODEC == SWCODEC
898 if (!global_settings.pitch_mode_timestretch)
900 #endif
901 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
902 #if CONFIG_CODEC == SWCODEC
903 , speed
904 #endif
906 nudged = (new_pitch != pitch);
907 pitch = new_pitch;
908 semitone = get_semitone_from_pitch(pitch);
909 #if CONFIG_CODEC == SWCODEC
910 speed = pitch;
911 #endif
912 break;
913 #if CONFIG_CODEC == SWCODEC
915 else
917 new_speed = speed - SPEED_SMALL_DELTA;
918 at_limit = false;
920 break;
922 case ACTION_PS_SLOWER:
923 if (global_settings.pitch_mode_timestretch)
925 new_speed = speed - SPEED_BIG_DELTA;
926 /* snap to whole numbers */
927 if(new_speed % PITCH_SPEED_PRECISION != 0)
928 new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
929 at_limit = false;
931 break;
932 #endif
934 case ACTION_PS_NUDGE_LEFTOFF:
935 if (nudged)
937 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
938 #if CONFIG_CODEC == SWCODEC
939 , speed
940 #endif
942 #if CONFIG_CODEC == SWCODEC
943 speed = pitch;
944 #endif
945 semitone = get_semitone_from_pitch(pitch);
946 nudged = false;
948 break;
950 case ACTION_PS_RESET:
951 pitch = PITCH_SPEED_100;
952 sound_set_pitch(pitch);
953 #if CONFIG_CODEC == SWCODEC
954 speed = PITCH_SPEED_100;
955 if (dsp_timestretch_available())
957 dsp_set_timestretch(PITCH_SPEED_100);
958 at_limit = false;
960 #endif
961 semitone = get_semitone_from_pitch(pitch);
962 break;
964 case ACTION_PS_TOGGLE_MODE:
965 global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
966 #if CONFIG_CODEC == SWCODEC
968 if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
970 global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
971 if(!global_settings.pitch_mode_timestretch)
973 /* no longer in timestretch mode. Reset speed */
974 speed = pitch;
975 dsp_set_timestretch(PITCH_SPEED_100);
978 settings_save();
979 #endif
980 break;
982 case ACTION_PS_EXIT:
983 exit = true;
984 break;
986 default:
987 if (default_event_handler(button) == SYS_USB_CONNECTED)
988 return 1;
989 break;
991 if (pitch_delta)
993 if (global_settings.pitch_mode_semitone)
995 semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
996 #if CONFIG_CODEC == SWCODEC
997 , speed
998 #endif
1000 pitch = get_pitch_from_semitone(semitone);
1002 else
1004 pitch = pitch_increase(pitch, pitch_delta, true
1005 #if CONFIG_CODEC == SWCODEC
1006 , speed
1007 #endif
1009 semitone = get_semitone_from_pitch(pitch);
1011 #if CONFIG_CODEC == SWCODEC
1012 if (global_settings.pitch_mode_timestretch)
1014 /* do this to make sure we properly obey the stretch limits */
1015 new_speed = speed;
1017 else
1019 speed = pitch;
1021 #endif
1024 #if CONFIG_CODEC == SWCODEC
1025 if(new_speed)
1027 new_stretch = GET_STRETCH(pitch, new_speed);
1029 /* limit the amount of stretch */
1030 if(new_stretch > STRETCH_MAX)
1032 new_stretch = STRETCH_MAX;
1033 new_speed = GET_SPEED(pitch, new_stretch);
1035 else if(new_stretch < STRETCH_MIN)
1037 new_stretch = STRETCH_MIN;
1038 new_speed = GET_SPEED(pitch, new_stretch);
1041 new_stretch = GET_STRETCH(pitch, new_speed);
1042 if(new_stretch >= STRETCH_MAX ||
1043 new_stretch <= STRETCH_MIN)
1045 at_limit = true;
1048 /* set the amount of stretch */
1049 dsp_set_timestretch(new_stretch);
1051 /* update the speed variable with the new speed */
1052 speed = new_speed;
1054 /* Reset new_speed so we only call dsp_set_timestretch */
1055 /* when needed */
1056 new_speed = 0;
1058 #endif
1060 #if CONFIG_CODEC == SWCODEC
1061 pcmbuf_set_low_latency(false);
1062 #endif
1063 return 0;