summaryrefslogtreecommitdiff
path: root/apps/gui
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2021-08-19 22:20:56 -0400
committerWilliam Wilgus <me.theuser@yahoo.com>2021-11-10 22:38:37 -0500
commitcf009b4cbb1eec083ab17ce370df090979eaf68e (patch)
tree19c2346a0e92878ef078879b77e262bf3380ec72 /apps/gui
parent67fb558c8997f47440b176fe5553246486b1a838 (diff)
downloadrockbox-cf009b4cbb1eec083ab17ce370df090979eaf68e.tar.gz
rockbox-cf009b4cbb1eec083ab17ce370df090979eaf68e.zip
pitchscreen make it a plugin
full commandline parsing -has pitch, speed, time_stretch /* pitch_screen * accepts args -q, -g, -p=, -s=, -k=; (= sign is optional) * -q silences output splash * -g runs the gui (Runs immediately) * -p100 would set pitch to 100% * -s=90 sets speed to 90% if timestrech is enabled * -k=true -k1 enables time stretch -k0 -kf-kn disables */ Change-Id: I900c4b5d184e12ddbe47509f66fdf3e55193a520
Diffstat (limited to 'apps/gui')
-rw-r--r--apps/gui/pitchscreen.c1055
1 files changed, 2 insertions, 1053 deletions
diff --git a/apps/gui/pitchscreen.c b/apps/gui/pitchscreen.c
index 871921a10f..9f42aedb5d 100644
--- a/apps/gui/pitchscreen.c
+++ b/apps/gui/pitchscreen.c
@@ -18,1059 +18,8 @@
18 * KIND, either express or implied. 18 * KIND, either express or implied.
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21 21#include "plugin.h"
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 "talk.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#include "tdspeed.h"
42
43#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
44 /* on both sides when drawing */
45
46#define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
47#define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
48#define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
49#define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
50#define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
51
52#define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
53#define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
54
55#define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
56#define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
57
58enum
59{
60 PITCH_TOP = 0,
61 PITCH_MID,
62 PITCH_BOTTOM,
63 PITCH_ITEM_COUNT,
64};
65
66
67/* This is a table of semitone percentage values of the appropriate
68 precision (based on PITCH_SPEED_PRECISION). Note that these are
69 all constant expressions, which will be evaluated at compile time,
70 so no need to worry about how complex the expressions look.
71 That's just to get the precision right.
72
73 I calculated these values, starting from 50, as
74
75 x(n) = 50 * 2^(n/12)
76
77 All that math in each entry simply converts the float constant
78 to an integer equal to PITCH_SPEED_PRECISION times the float value,
79 with as little precision loss as possible (i.e. correctly rounding
80 the last digit).
81*/
82#define TO_INT_WITH_PRECISION(x) \
83 ( (unsigned short)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) )
84
85static const unsigned short semitone_table[] =
86{
87 TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */
88 TO_INT_WITH_PRECISION(52.97315472),
89 TO_INT_WITH_PRECISION(56.12310242),
90 TO_INT_WITH_PRECISION(59.46035575),
91 TO_INT_WITH_PRECISION(62.99605249),
92 TO_INT_WITH_PRECISION(66.74199271),
93 TO_INT_WITH_PRECISION(70.71067812),
94 TO_INT_WITH_PRECISION(74.91535384),
95 TO_INT_WITH_PRECISION(79.37005260),
96 TO_INT_WITH_PRECISION(84.08964153),
97 TO_INT_WITH_PRECISION(89.08987181),
98 TO_INT_WITH_PRECISION(94.38743127),
99 TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */
100 TO_INT_WITH_PRECISION(105.9463094),
101 TO_INT_WITH_PRECISION(112.2462048),
102 TO_INT_WITH_PRECISION(118.9207115),
103 TO_INT_WITH_PRECISION(125.9921049),
104 TO_INT_WITH_PRECISION(133.4839854),
105 TO_INT_WITH_PRECISION(141.4213562),
106 TO_INT_WITH_PRECISION(149.8307077),
107 TO_INT_WITH_PRECISION(158.7401052),
108 TO_INT_WITH_PRECISION(168.1792831),
109 TO_INT_WITH_PRECISION(178.1797436),
110 TO_INT_WITH_PRECISION(188.7748625),
111 TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */
112};
113
114#define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0])))
115#define SEMITONE_END (NUM_SEMITONES/2)
116#define SEMITONE_START (-SEMITONE_END)
117
118/* A table of values for approximating the cent curve with
119 linear interpolation. Multipy the next lowest semitone
120 by this much to find the corresponding cent percentage.
121
122 These values were calculated as
123 x(n) = 100 * 2^(n * 20/1200)
124*/
125
126static const unsigned short cent_interp[] =
127{
128 TO_INT_WITH_PRECISION(100.0000000),
129 TO_INT_WITH_PRECISION(101.1619440),
130 TO_INT_WITH_PRECISION(102.3373892),
131 TO_INT_WITH_PRECISION(103.5264924),
132 TO_INT_WITH_PRECISION(104.7294123),
133 /* this one's the next semitone but we have it here for convenience */
134 TO_INT_WITH_PRECISION(105.9463094),
135};
136
137/* Number of cents between entries in the cent_interp table */
138#define CENT_INTERP_INTERVAL 20
139#define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0])))
140
141/* This stores whether the pitch and speed are at their own limits */
142/* or that of the timestretching algorithm */
143static bool at_limit = false;
144
145/*
146 *
147 * The pitchscreen is divided into 3 viewports (each row is a viewport)
148 * Then each viewport is again divided into 3 colums, each showsing some infos
149 * Additionally, on touchscreen, each cell represents a button
150 *
151 * Below a sketch describing what each cell will show (what's drawn on it)
152 * --------------------------
153 * | | | | <-- pitch up in the middle (text and button)
154 * | | | | <-- arrows for mode toggling on the sides for touchscreen
155 * |------------------------|
156 * | | | | <-- semitone/speed up/down on the sides
157 * | | | | <-- reset pitch&speed in the middle
158 * |------------------------|
159 * | | | | <-- pitch down in the middle
160 * | | | | <-- Two "OK" for exit on the sides for touchscreen
161 * |------------------------|
162 *
163 *
164 */
165
166static void speak_pitch_mode(bool enqueue)
167{
168 bool timestretch_mode = global_settings.pitch_mode_timestretch && dsp_timestretch_available();
169 if (timestretch_mode)
170 talk_id(VOICE_PITCH_TIMESTRETCH_MODE, enqueue);
171 if (global_settings.pitch_mode_semitone)
172 talk_id(VOICE_PITCH_SEMITONE_MODE, timestretch_mode ? true : enqueue);
173 else
174 talk_id(VOICE_PITCH_ABSOLUTE_MODE, timestretch_mode ? true : enqueue);
175 return;
176}
177
178/*
179 * Fixes the viewports so they represent the 3 rows, and adds a little margin
180 * on all sides for the icons (which are drawn outside of the grid
181 *
182 * The modified viewports need to be passed to the touchscreen handling function
183 **/
184static void pitchscreen_fix_viewports(struct viewport *parent,
185 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
186{
187 int i, font_height;
188 font_height = font_get(parent->font)->height;
189 for (i = 0; i < PITCH_ITEM_COUNT; i++)
190 {
191 pitch_viewports[i] = *parent;
192 pitch_viewports[i].height = parent->height / PITCH_ITEM_COUNT;
193 pitch_viewports[i].x += ICON_BORDER;
194 pitch_viewports[i].width -= 2*ICON_BORDER;
195 }
196 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
197 pitch_viewports[PITCH_TOP].height -= ICON_BORDER;
198
199 if(pitch_viewports[PITCH_MID].height < font_height * 2)
200 pitch_viewports[PITCH_MID].height = font_height * 2;
201
202 pitch_viewports[PITCH_MID].y = pitch_viewports[PITCH_TOP].y
203 + pitch_viewports[PITCH_TOP].height;
204
205 pitch_viewports[PITCH_BOTTOM].y = pitch_viewports[PITCH_MID].y
206 + pitch_viewports[PITCH_MID].height;
207
208 pitch_viewports[PITCH_BOTTOM].height -= ICON_BORDER;
209}
210
211/* must be called before pitchscreen_draw, or within
212 * since it neither clears nor updates the display */
213static void pitchscreen_draw_icons(struct screen *display,
214 struct viewport *parent)
215{
216 display->set_viewport(parent);
217 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
218 parent->width/2 - 3,
219 2, 7, 8);
220 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
221 parent->width /2 - 3,
222 parent->height - 10, 7, 8);
223 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
224 parent->width - 10,
225 parent->height /2 - 4, 7, 8);
226 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
227 2,
228 parent->height /2 - 4, 7, 8);
229 display->update_viewport();
230}
231
232static void pitchscreen_draw(struct screen *display, int max_lines,
233 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
234 int32_t pitch, int32_t semitone
235 ,int32_t speed
236 )
237{
238 const char* ptr;
239 char buf[32];
240 int w, h;
241 bool show_lang_pitch;
242 struct viewport *last_vp = NULL;
243
244 /* "Pitch up/Pitch down" - hide for a small screen,
245 * the text is drawn centered automatically
246 *
247 * note: this assumes 5 lines always fit on a touchscreen (should be
248 * reasonable) */
249 if (max_lines >= 5)
250 {
251 int w, h;
252 struct viewport *vp = &pitch_viewports[PITCH_TOP];
253 last_vp = display->set_viewport(vp);
254 display->clear_viewport();
255#ifdef HAVE_TOUCHSCREEN
256 /* two arrows in the top row, left and right column */
257 char *arrows[] = { "<", ">"};
258 display->getstringsize(arrows[0], &w, &h);
259 display->putsxy(0, vp->height/2 - h/2, arrows[0]);
260 display->putsxy(vp->width - w, vp->height/2 - h/2, arrows[1]);
261#endif
262 /* UP: Pitch Up */
263 if (global_settings.pitch_mode_semitone)
264 ptr = str(LANG_PITCH_UP_SEMITONE);
265 else
266 ptr = str(LANG_PITCH_UP);
267
268 display->getstringsize(ptr, &w, NULL);
269 /* draw text */
270 display->putsxy(vp->width/2 - w/2, 0, ptr);
271 display->update_viewport();
272
273 /* DOWN: Pitch Down */
274 vp = &pitch_viewports[PITCH_BOTTOM];
275 display->set_viewport(vp);
276 display->clear_viewport();
277
278#ifdef HAVE_TOUCHSCREEN
279 ptr = str(LANG_KBD_OK);
280 display->getstringsize(ptr, &w, &h);
281 /* one OK in the middle first column of the vp (at half height) */
282 display->putsxy(vp->width/6 - w/2, vp->height/2 - h/2, ptr);
283 /* one OK in the middle of the last column of the vp (at half height) */
284 display->putsxy(5*vp->width/6 - w/2, vp->height/2 - h/2, ptr);
285#endif
286 if (global_settings.pitch_mode_semitone)
287 ptr = str(LANG_PITCH_DOWN_SEMITONE);
288 else
289 ptr = str(LANG_PITCH_DOWN);
290 display->getstringsize(ptr, &w, &h);
291 /* draw text */
292 display->putsxy(vp->width/2 - w/2, vp->height - h, ptr);
293 display->update_viewport();
294 }
295
296 /* Middle section */
297 display->set_viewport(&pitch_viewports[PITCH_MID]);
298 display->clear_viewport();
299 int width_used = 0;
300
301 /* Middle section upper line - hide for a small screen */
302 if ((show_lang_pitch = (max_lines >= 3)))
303 {
304 if(global_settings.pitch_mode_timestretch)
305 {
306 /* Pitch:XXX.X% */
307 if(global_settings.pitch_mode_semitone)
308 {
309 snprintf(buf, sizeof(buf), "%s: %s%d.%02d", str(LANG_PITCH),
310 semitone >= 0 ? "+" : "-",
311 abs(semitone / PITCH_SPEED_PRECISION),
312 abs((semitone % PITCH_SPEED_PRECISION) /
313 (PITCH_SPEED_PRECISION / 100))
314 );
315 }
316 else
317 {
318 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
319 pitch / PITCH_SPEED_PRECISION,
320 (pitch % PITCH_SPEED_PRECISION) /
321 (PITCH_SPEED_PRECISION / 10));
322 }
323 }
324 else
325 {
326 /* Rate */
327 snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
328 }
329 display->getstringsize(buf, &w, &h);
330 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
331 (pitch_viewports[PITCH_MID].height / 2) - h, buf);
332 if (w > width_used)
333 width_used = w;
334 }
335
336 /* Middle section lower line */
337 /* "Speed:XXX%" */
338 if(global_settings.pitch_mode_timestretch)
339 {
340 snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
341 speed / PITCH_SPEED_PRECISION,
342 (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
343 }
344 else
345 {
346 if(global_settings.pitch_mode_semitone)
347 {
348 snprintf(buf, sizeof(buf), "%s%d.%02d",
349 semitone >= 0 ? "+" : "-",
350 abs(semitone / PITCH_SPEED_PRECISION),
351 abs((semitone % PITCH_SPEED_PRECISION) /
352 (PITCH_SPEED_PRECISION / 100))
353 );
354 }
355 else
356 {
357 snprintf(buf, sizeof(buf), "%ld.%ld%%",
358 pitch / PITCH_SPEED_PRECISION,
359 (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
360 }
361 }
362
363 display->getstringsize(buf, &w, &h);
364 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
365 show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
366 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
367 buf);
368 if (w > width_used)
369 width_used = w;
370
371 /* "limit" and "timestretch" labels */
372 if (max_lines >= 7)
373 {
374 if(at_limit)
375 {
376 const char * const p = str(LANG_STRETCH_LIMIT);
377 display->getstringsize(p, &w, &h);
378 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
379 (pitch_viewports[PITCH_MID].height / 2) + h, p);
380 if (w > width_used)
381 width_used = w;
382 }
383 }
384
385 /* Middle section left/right labels */
386 const char *leftlabel = "-2%";
387 const char *rightlabel = "+2%";
388 if (global_settings.pitch_mode_timestretch)
389 {
390 leftlabel = "<<";
391 rightlabel = ">>";
392 }
393
394 /* Only display if they fit */
395 display->getstringsize(leftlabel, &w, &h);
396 width_used += w;
397 display->getstringsize(rightlabel, &w, &h);
398 width_used += w;
399
400 if (width_used <= pitch_viewports[PITCH_MID].width)
401 {
402 display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
403 leftlabel);
404 display->putsxy((pitch_viewports[PITCH_MID].width - w),
405 (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
406 rightlabel);
407 }
408 display->update_viewport();
409 display->set_viewport(last_vp);
410}
411
412static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
413 /* need this to maintain correct pitch/speed caps */
414 , int32_t speed
415 )
416{
417 int32_t new_pitch;
418 int32_t new_stretch;
419 at_limit = false;
420
421 if (pitch_delta < 0)
422 {
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)
426 {
427 pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
428 }
429
430 new_pitch = pitch + pitch_delta;
431
432 if (new_pitch < PITCH_MIN)
433 {
434 if (!allow_cutoff)
435 {
436 return pitch;
437 }
438 new_pitch = PITCH_MIN;
439 at_limit = true;
440 }
441 }
442 else if (pitch_delta > 0)
443 {
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)
447 {
448 pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
449 }
450
451 new_pitch = pitch + pitch_delta;
452
453 if (new_pitch > PITCH_MAX)
454 {
455 if (!allow_cutoff)
456 return pitch;
457 new_pitch = PITCH_MAX;
458 at_limit = true;
459 }
460 }
461 else
462 {
463 /* pitch_delta == 0 -> no real change */
464 return pitch;
465 }
466 if (dsp_timestretch_available())
467 {
468 /* increase the multiple to increase precision of this calculation */
469 new_stretch = GET_STRETCH(new_pitch, speed);
470 if(new_stretch < STRETCH_MIN)
471 {
472 /* we have to ignore allow_cutoff, because we can't have the */
473 /* stretch go higher than STRETCH_MAX */
474 new_pitch = GET_PITCH(speed, STRETCH_MIN);
475 }
476 else if(new_stretch > STRETCH_MAX)
477 {
478 /* we have to ignore allow_cutoff, because we can't have the */
479 /* stretch go higher than STRETCH_MAX */
480 new_pitch = GET_PITCH(speed, STRETCH_MAX);
481 }
482
483 if(new_stretch >= STRETCH_MAX ||
484 new_stretch <= STRETCH_MIN)
485 {
486 at_limit = true;
487 }
488 }
489
490 sound_set_pitch(new_pitch);
491
492 return new_pitch;
493}
494
495static int32_t get_semitone_from_pitch(int32_t pitch)
496{
497 int semitone = 0;
498 int32_t fractional_index = 0;
499
500 while(semitone < NUM_SEMITONES - 1 &&
501 pitch >= semitone_table[semitone + 1])
502 {
503 semitone++;
504 }
505
506
507 /* now find the fractional part */
508 while(pitch > (cent_interp[fractional_index + 1] *
509 semitone_table[semitone] / PITCH_SPEED_100))
510 {
511 /* Check to make sure fractional_index isn't too big */
512 /* This should never happen. */
513 if(fractional_index >= CENT_INTERP_NUM - 1)
514 {
515 break;
516 }
517 fractional_index++;
518 }
519
520 int32_t semitone_pitch_a = cent_interp[fractional_index] *
521 semitone_table[semitone] /
522 PITCH_SPEED_100;
523 int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
524 semitone_table[semitone] /
525 PITCH_SPEED_100;
526 /* this will be the integer offset from the cent_interp entry */
527 int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
528 (semitone_pitch_b - semitone_pitch_a);
529 semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
530 fractional_index * CENT_INTERP_INTERVAL +
531 semitone_frac_ofs;
532
533 return semitone;
534}
535
536static int32_t get_pitch_from_semitone(int32_t semitone)
537{
538 int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
539
540 /* Find the index into the semitone table */
541 int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
542
543 /* set pitch to the semitone's integer part value */
544 int32_t pitch = semitone_table[semitone_index];
545 /* get the range of the cent modification for future calculation */
546 int32_t pitch_mod_a =
547 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
548 CENT_INTERP_INTERVAL];
549 int32_t pitch_mod_b =
550 cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
551 CENT_INTERP_INTERVAL + 1];
552 /* figure out the cent mod amount based on the semitone fractional value */
553 int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
554 (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
555
556 /* modify pitch based on the mod amount we just calculated */
557 return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
558}
559
560static int32_t pitch_increase_semitone(int32_t pitch,
561 int32_t current_semitone,
562 int32_t semitone_delta
563 , int32_t speed
564 )
565{
566 int32_t new_semitone = current_semitone;
567
568 /* snap to the delta interval */
569 if(current_semitone % semitone_delta != 0)
570 {
571 if(current_semitone > 0 && semitone_delta > 0)
572 new_semitone += semitone_delta;
573 else if(current_semitone < 0 && semitone_delta < 0)
574 new_semitone += semitone_delta;
575
576 new_semitone -= new_semitone % semitone_delta;
577 }
578 else
579 new_semitone += semitone_delta;
580
581 /* clamp the pitch so it doesn't go beyond the pitch limits */
582 if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
583 {
584 new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
585 at_limit = true;
586 }
587 else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
588 {
589 new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
590 at_limit = true;
591 }
592
593 int32_t new_pitch = get_pitch_from_semitone(new_semitone);
594
595 int32_t new_stretch = GET_STRETCH(new_pitch, speed);
596
597 /* clamp the pitch so it doesn't go beyond the stretch limits */
598 if( new_stretch > STRETCH_MAX)
599 {
600 new_pitch = GET_PITCH(speed, STRETCH_MAX);
601 new_semitone = get_semitone_from_pitch(new_pitch);
602 at_limit = true;
603 }
604 else if (new_stretch < STRETCH_MIN)
605 {
606 new_pitch = GET_PITCH(speed, STRETCH_MIN);
607 new_semitone = get_semitone_from_pitch(new_pitch);
608 at_limit = true;
609 }
610
611 pitch_increase(pitch, new_pitch - pitch, false
612 , speed
613 );
614
615 return new_semitone;
616}
617
618#ifdef HAVE_TOUCHSCREEN
619/*
620 * Check for touchscreen presses as per sketch above in this file
621 *
622 * goes through each row of the, checks whether the touchscreen
623 * was pressed in it. Then it looks the columns of each row for specific actions
624 */
625static int pitchscreen_do_touchscreen(struct viewport vps[])
626{
627 short x, y;
628 struct viewport *this_vp = &vps[PITCH_TOP];
629 int ret;
630 static bool wait_for_release = false;
631 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
632
633 /* top row */
634 if (ret > ACTION_UNKNOWN)
635 { /* press on top row, left or right column
636 * only toggle mode if released */
637 int column = this_vp->width / 3;
638 if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
639 return ACTION_PS_TOGGLE_MODE;
640
641
642 else if (x >= column && x <= (2*column))
643 { /* center column pressed */
644 if (ret == BUTTON_REPEAT)
645 return ACTION_PS_INC_BIG;
646 else if (ret & BUTTON_REL)
647 return ACTION_PS_INC_SMALL;
648 }
649 return ACTION_NONE;
650 }
651
652 /* now the center row */
653 this_vp = &vps[PITCH_MID];
654 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
655
656 if (ret > ACTION_UNKNOWN)
657 {
658 int column = this_vp->width / 3;
659
660 if (x < column)
661 { /* left column */
662 if (ret & BUTTON_REL)
663 {
664 wait_for_release = false;
665 return ACTION_PS_NUDGE_LEFTOFF;
666 }
667 else if (ret & BUTTON_REPEAT)
668 return ACTION_PS_SLOWER;
669 if (!wait_for_release)
670 {
671 wait_for_release = true;
672 return ACTION_PS_NUDGE_LEFT;
673 }
674 }
675 else if (x > (2*column))
676 { /* right column */
677 if (ret & BUTTON_REL)
678 {
679 wait_for_release = false;
680 return ACTION_PS_NUDGE_RIGHTOFF;
681 }
682 else if (ret & BUTTON_REPEAT)
683 return ACTION_PS_FASTER;
684 if (!wait_for_release)
685 {
686 wait_for_release = true;
687 return ACTION_PS_NUDGE_RIGHT;
688 }
689 }
690 else
691 /* center column was pressed */
692 return ACTION_PS_RESET;
693 }
694
695 /* now the bottom row */
696 this_vp = &vps[PITCH_BOTTOM];
697 ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
698
699 if (ret > ACTION_UNKNOWN)
700 {
701 int column = this_vp->width / 3;
702
703 /* left or right column is exit */
704 if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
705 return ACTION_PS_EXIT;
706 else if (x >= column && x <= (2*column))
707 { /* center column was pressed */
708 if (ret & BUTTON_REPEAT)
709 return ACTION_PS_DEC_BIG;
710 else if (ret & BUTTON_REL)
711 return ACTION_PS_DEC_SMALL;
712 }
713 return ACTION_NONE;
714 }
715 return ACTION_NONE;
716}
717
718#endif
719/*
720 returns:
721 0 on exit
722 1 if USB was connected
723*/
724
725int gui_syncpitchscreen_run(void) 22int gui_syncpitchscreen_run(void)
726{ 23{
727 int button; 24 return (plugin_load(VIEWERS_DIR"/pitch_screen.rock", NULL) == PLUGIN_USB_CONNECTED);
728 int32_t pitch = sound_get_pitch();
729 int32_t semitone;
730
731 int32_t new_pitch;
732 int32_t pitch_delta;
733 bool nudged = false;
734 int i, updated = 4, decimals = 0;
735 bool exit = false;
736 /* should maybe be passed per parameter later, not needed for now */
737 struct viewport parent[NB_SCREENS];
738 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
739 int max_lines[NB_SCREENS];
740
741 push_current_activity(ACTIVITY_PITCHSCREEN);
742
743 int32_t new_speed = 0, new_stretch;
744
745 /* the speed variable holds the apparent speed of the playback */
746 int32_t speed;
747 if (dsp_timestretch_available())
748 {
749 speed = GET_SPEED(pitch, dsp_get_timestretch());
750 }
751 else
752 {
753 speed = pitch;
754 }
755
756 /* Figure out whether to be in timestretch mode */
757 if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
758 {
759 global_settings.pitch_mode_timestretch = false;
760 settings_save();
761 }
762
763 /* Count decimals for speaking */
764 for (i = PITCH_SPEED_PRECISION; i >= 10; i /= 10)
765 decimals++;
766
767 /* set the semitone index based on the current pitch */
768 semitone = get_semitone_from_pitch(pitch);
769
770 /* initialize pitchscreen vps */
771 FOR_NB_SCREENS(i)
772 {
773 viewport_set_defaults(&parent[i], i);
774 max_lines[i] = viewport_get_nb_lines(&parent[i]);
775 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
776 screens[i].set_viewport(&parent[i]);
777 screens[i].clear_viewport();
778
779 /* also, draw the icons now, it's only needed once */
780 pitchscreen_draw_icons(&screens[i], &parent[i]);
781 }
782 pcmbuf_set_low_latency(true);
783
784 while (!exit)
785 {
786 FOR_NB_SCREENS(i)
787 pitchscreen_draw(&screens[i], max_lines[i],
788 pitch_viewports[i], pitch, semitone
789 , speed
790 );
791 pitch_delta = 0;
792 new_speed = 0;
793
794 if (global_settings.talk_menu && updated)
795 {
796 talk_shutup();
797 switch (updated)
798 {
799 case 1:
800 if (global_settings.pitch_mode_semitone)
801 talk_value_decimal(semitone, UNIT_SIGNED, decimals, false);
802 else
803 talk_value_decimal(pitch, UNIT_PERCENT, decimals, false);
804 break;
805 case 2:
806 talk_value_decimal(speed, UNIT_PERCENT, decimals, false);
807 break;
808 case 3:
809 speak_pitch_mode(false);
810 break;
811 case 4:
812 if (global_settings.pitch_mode_timestretch && dsp_timestretch_available())
813 talk_id(LANG_PITCH, false);
814 else
815 talk_id(LANG_PLAYBACK_RATE, false);
816 talk_value_decimal(pitch, UNIT_PERCENT, decimals, true);
817 if (global_settings.pitch_mode_timestretch && dsp_timestretch_available())
818 {
819 talk_id(LANG_SPEED, true);
820 talk_value_decimal(speed, UNIT_PERCENT, decimals, true);
821 }
822 speak_pitch_mode(true);
823 break;
824 default:
825 break;
826 }
827 }
828 updated = 0;
829
830 button = get_action(CONTEXT_PITCHSCREEN, HZ);
831
832#ifdef HAVE_TOUCHSCREEN
833 if (button == ACTION_TOUCHSCREEN)
834 {
835 FOR_NB_SCREENS(i)
836 button = pitchscreen_do_touchscreen(pitch_viewports[i]);
837 }
838#endif
839 switch (button)
840 {
841 case ACTION_PS_INC_SMALL:
842 if(global_settings.pitch_mode_semitone)
843 pitch_delta = SEMITONE_SMALL_DELTA;
844 else
845 pitch_delta = PITCH_SMALL_DELTA;
846 updated = 1;
847 break;
848
849 case ACTION_PS_INC_BIG:
850 if(global_settings.pitch_mode_semitone)
851 pitch_delta = SEMITONE_BIG_DELTA;
852 else
853 pitch_delta = PITCH_BIG_DELTA;
854 updated = 1;
855 break;
856
857 case ACTION_PS_DEC_SMALL:
858 if(global_settings.pitch_mode_semitone)
859 pitch_delta = -SEMITONE_SMALL_DELTA;
860 else
861 pitch_delta = -PITCH_SMALL_DELTA;
862 updated = 1;
863 break;
864
865 case ACTION_PS_DEC_BIG:
866 if(global_settings.pitch_mode_semitone)
867 pitch_delta = -SEMITONE_BIG_DELTA;
868 else
869 pitch_delta = -PITCH_BIG_DELTA;
870 updated = 1;
871 break;
872
873 case ACTION_PS_NUDGE_RIGHT:
874 if (!global_settings.pitch_mode_timestretch)
875 {
876 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
877 , speed
878 );
879 nudged = (new_pitch != pitch);
880 pitch = new_pitch;
881 semitone = get_semitone_from_pitch(pitch);
882 speed = pitch;
883 updated = nudged ? 1 : 0;
884 break;
885 }
886 else
887 {
888 new_speed = speed + SPEED_SMALL_DELTA;
889 at_limit = false;
890 updated = 2;
891 }
892 break;
893
894 case ACTION_PS_FASTER:
895 if (global_settings.pitch_mode_timestretch)
896 {
897 new_speed = speed + SPEED_BIG_DELTA;
898 /* snap to whole numbers */
899 if(new_speed % PITCH_SPEED_PRECISION != 0)
900 new_speed -= new_speed % PITCH_SPEED_PRECISION;
901 at_limit = false;
902 updated = 2;
903 }
904 break;
905
906 case ACTION_PS_NUDGE_RIGHTOFF:
907 if (nudged)
908 {
909 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
910 , speed
911 );
912 speed = pitch;
913 semitone = get_semitone_from_pitch(pitch);
914 nudged = false;
915 updated = 1;
916 }
917 break;
918
919 case ACTION_PS_NUDGE_LEFT:
920 if (!global_settings.pitch_mode_timestretch)
921 {
922 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
923 , speed
924 );
925 nudged = (new_pitch != pitch);
926 pitch = new_pitch;
927 semitone = get_semitone_from_pitch(pitch);
928 speed = pitch;
929 updated = nudged ? 1 : 0;
930 break;
931 }
932 else
933 {
934 new_speed = speed - SPEED_SMALL_DELTA;
935 at_limit = false;
936 updated = 2;
937 }
938 break;
939
940 case ACTION_PS_SLOWER:
941 if (global_settings.pitch_mode_timestretch)
942 {
943 new_speed = speed - SPEED_BIG_DELTA;
944 /* snap to whole numbers */
945 if(new_speed % PITCH_SPEED_PRECISION != 0)
946 new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
947 at_limit = false;
948 updated = 2;
949 }
950 break;
951
952 case ACTION_PS_NUDGE_LEFTOFF:
953 if (nudged)
954 {
955 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
956 , speed
957 );
958 speed = pitch;
959 semitone = get_semitone_from_pitch(pitch);
960 nudged = false;
961 updated = 1;
962 }
963 break;
964
965 case ACTION_PS_RESET:
966 pitch = PITCH_SPEED_100;
967 sound_set_pitch(pitch);
968 speed = PITCH_SPEED_100;
969 if (dsp_timestretch_available())
970 {
971 dsp_set_timestretch(PITCH_SPEED_100);
972 at_limit = false;
973 }
974 semitone = get_semitone_from_pitch(pitch);
975 updated = 4;
976 break;
977
978 case ACTION_PS_TOGGLE_MODE:
979 global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
980
981 if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
982 {
983 global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
984 if(!global_settings.pitch_mode_timestretch)
985 {
986 /* no longer in timestretch mode. Reset speed */
987 speed = pitch;
988 dsp_set_timestretch(PITCH_SPEED_100);
989 }
990 }
991 settings_save();
992 updated = 3;
993 break;
994
995 case ACTION_PS_EXIT:
996 exit = true;
997 break;
998
999 default:
1000 if (default_event_handler(button) == SYS_USB_CONNECTED)
1001 return 1;
1002 break;
1003 }
1004 if (pitch_delta)
1005 {
1006 if (global_settings.pitch_mode_semitone)
1007 {
1008 semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
1009 , speed
1010 );
1011 pitch = get_pitch_from_semitone(semitone);
1012 }
1013 else
1014 {
1015 pitch = pitch_increase(pitch, pitch_delta, true
1016 , speed
1017 );
1018 semitone = get_semitone_from_pitch(pitch);
1019 }
1020 if (global_settings.pitch_mode_timestretch)
1021 {
1022 /* do this to make sure we properly obey the stretch limits */
1023 new_speed = speed;
1024 }
1025 else
1026 {
1027 speed = pitch;
1028 }
1029 }
1030
1031 if(new_speed)
1032 {
1033 new_stretch = GET_STRETCH(pitch, new_speed);
1034
1035 /* limit the amount of stretch */
1036 if(new_stretch > STRETCH_MAX)
1037 {
1038 new_stretch = STRETCH_MAX;
1039 new_speed = GET_SPEED(pitch, new_stretch);
1040 }
1041 else if(new_stretch < STRETCH_MIN)
1042 {
1043 new_stretch = STRETCH_MIN;
1044 new_speed = GET_SPEED(pitch, new_stretch);
1045 }
1046
1047 new_stretch = GET_STRETCH(pitch, new_speed);
1048 if(new_stretch >= STRETCH_MAX ||
1049 new_stretch <= STRETCH_MIN)
1050 {
1051 at_limit = true;
1052 }
1053
1054 /* set the amount of stretch */
1055 dsp_set_timestretch(new_stretch);
1056
1057 /* update the speed variable with the new speed */
1058 speed = new_speed;
1059
1060 /* Reset new_speed so we only call dsp_set_timestretch */
1061 /* when needed */
1062 new_speed = 0;
1063 }
1064 }
1065
1066 pcmbuf_set_low_latency(false);
1067 pop_current_activity();
1068
1069 /* Clean up */
1070 FOR_NB_SCREENS(i)
1071 {
1072 screens[i].set_viewport(NULL);
1073 }
1074
1075 return 0;
1076} 25}