Oops.
[kugel-rb.git] / apps / gui / pitchscreen.c
blob16fac0c3b52aca7c4624ed51a3b1b84b9c8a66a6
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 "config.h"
26 #include "sprintf.h"
27 #include "action.h"
28 #include "dsp.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 #if CONFIG_CODEC == SWCODEC
40 #include "tdspeed.h"
41 #endif
44 #define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
45 /* on both sides when drawing */
47 #define PITCH_MAX 2000
48 #define PITCH_MIN 500
49 #define PITCH_SMALL_DELTA 1
50 #define PITCH_BIG_DELTA 10
51 #define PITCH_NUDGE_DELTA 20
53 static bool pitch_mode_semitone = false;
54 #if CONFIG_CODEC == SWCODEC
55 static bool pitch_mode_timestretch = false;
56 #endif
58 enum
60 PITCH_TOP = 0,
61 PITCH_MID,
62 PITCH_BOTTOM,
63 PITCH_ITEM_COUNT,
66 static void pitchscreen_fix_viewports(struct viewport *parent,
67 struct viewport pitch_viewports[PITCH_ITEM_COUNT])
69 int i, height;
70 height = font_get(parent->font)->height;
71 for (i = 0; i < PITCH_ITEM_COUNT; i++)
73 pitch_viewports[i] = *parent;
74 pitch_viewports[i].height = height;
76 pitch_viewports[PITCH_TOP].y += ICON_BORDER;
78 pitch_viewports[PITCH_MID].x += ICON_BORDER;
79 pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
80 pitch_viewports[PITCH_MID].height = height * 2;
81 pitch_viewports[PITCH_MID].y += parent->height / 2 -
82 pitch_viewports[PITCH_MID].height / 2;
84 pitch_viewports[PITCH_BOTTOM].y += parent->height - height - ICON_BORDER;
87 /* must be called before pitchscreen_draw, or within
88 * since it neither clears nor updates the display */
89 static void pitchscreen_draw_icons(struct screen *display,
90 struct viewport *parent)
92 display->set_viewport(parent);
93 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
94 parent->width/2 - 3,
95 2, 7, 8);
96 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
97 parent->width /2 - 3,
98 parent->height - 10, 7, 8);
99 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
100 parent->width - 10,
101 parent->height /2 - 4, 7, 8);
102 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
104 parent->height /2 - 4, 7, 8);
105 display->update_viewport();
108 static void pitchscreen_draw(struct screen *display, int max_lines,
109 struct viewport pitch_viewports[PITCH_ITEM_COUNT],
110 int pitch
111 #if CONFIG_CODEC == SWCODEC
112 ,int speed
113 #endif
116 unsigned char* ptr;
117 char buf[32];
118 int w, h;
119 bool show_lang_pitch;
121 /* "Pitch up/Pitch down" - hide for a small screen */
122 if (max_lines >= 5)
124 /* UP: Pitch Up */
125 display->set_viewport(&pitch_viewports[PITCH_TOP]);
126 if (pitch_mode_semitone)
127 ptr = str(LANG_PITCH_UP_SEMITONE);
128 else
129 ptr = str(LANG_PITCH_UP);
130 display->getstringsize(ptr, &w, &h);
131 display->clear_viewport();
132 /* draw text */
133 display->putsxy((pitch_viewports[PITCH_TOP].width / 2) -
134 (w / 2), 0, ptr);
135 display->update_viewport();
137 /* DOWN: Pitch Down */
138 display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
139 if (pitch_mode_semitone)
140 ptr = str(LANG_PITCH_DOWN_SEMITONE);
141 else
142 ptr = str(LANG_PITCH_DOWN);
143 display->getstringsize(ptr, &w, &h);
144 display->clear_viewport();
145 /* draw text */
146 display->putsxy((pitch_viewports[PITCH_BOTTOM].width / 2) -
147 (w / 2), 0, ptr);
148 display->update_viewport();
151 /* Middle section */
152 display->set_viewport(&pitch_viewports[PITCH_MID]);
153 display->clear_viewport();
154 int width_used = 0;
156 /* Middle section upper line - hide for a small screen */
157 if ((show_lang_pitch = (max_lines >= 3)))
159 #if CONFIG_CODEC == SWCODEC
160 if (!pitch_mode_timestretch)
162 #endif
163 /* LANG_PITCH */
164 snprintf(buf, sizeof(buf), "%s", str(LANG_PITCH));
165 #if CONFIG_CODEC == SWCODEC
167 else
169 /* Pitch:XXX.X% */
170 snprintf(buf, sizeof(buf), "%s:%d.%d%%", str(LANG_PITCH),
171 pitch / 10, pitch % 10);
173 #endif
174 display->getstringsize(buf, &w, &h);
175 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
176 0, buf);
177 if (w > width_used)
178 width_used = w;
181 /* Middle section lower line */
182 #if CONFIG_CODEC == SWCODEC
183 if (!pitch_mode_timestretch)
185 #endif
186 /* "XXX.X%" */
187 snprintf(buf, sizeof(buf), "%d.%d%%",
188 pitch / 10, pitch % 10);
189 #if CONFIG_CODEC == SWCODEC
191 else
193 /* "Speed:XXX%" */
194 snprintf(buf, sizeof(buf), "%s:%d%%", str(LANG_SPEED),
195 speed / 1000);
197 #endif
198 display->getstringsize(buf, &w, &h);
199 display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
200 (show_lang_pitch ? h : h/2), buf);
201 if (w > width_used)
202 width_used = w;
204 /* Middle section left/right labels */
205 const char *leftlabel = "-2%";
206 const char *rightlabel = "+2%";
207 #if CONFIG_CODEC == SWCODEC
208 if (pitch_mode_timestretch)
210 leftlabel = "<<";
211 rightlabel = ">>";
213 #endif
215 /* Only display if they fit */
216 display->getstringsize(leftlabel, &w, &h);
217 width_used += w;
218 display->getstringsize(rightlabel, &w, &h);
219 width_used += w;
221 if (width_used <= pitch_viewports[PITCH_MID].width)
223 display->putsxy(0, h / 2, leftlabel);
224 display->putsxy(pitch_viewports[PITCH_MID].width - w, h /2, rightlabel);
226 display->update_viewport();
227 display->set_viewport(NULL);
230 static int pitch_increase(int pitch, int pitch_delta, bool allow_cutoff)
232 int new_pitch;
234 if (pitch_delta < 0)
236 if (pitch + pitch_delta >= PITCH_MIN)
237 new_pitch = pitch + pitch_delta;
238 else
240 if (!allow_cutoff)
241 return pitch;
242 new_pitch = PITCH_MIN;
245 else if (pitch_delta > 0)
247 if (pitch + pitch_delta <= PITCH_MAX)
248 new_pitch = pitch + pitch_delta;
249 else
251 if (!allow_cutoff)
252 return pitch;
253 new_pitch = PITCH_MAX;
256 else
258 /* pitch_delta == 0 -> no real change */
259 return pitch;
261 sound_set_pitch(new_pitch);
263 return new_pitch;
266 /* Factor for changing the pitch one half tone up.
267 The exact value is 2^(1/12) = 1.05946309436
268 But we use only integer arithmetics, so take
269 rounded factor multiplied by 10^5=100,000. This is
270 enough to get the same promille values as if we
271 had used floating point (checked with a spread
272 sheet).
274 #define PITCH_SEMITONE_FACTOR 105946L
276 /* Some helpful constants. K is the scaling factor for SEMITONE.
277 N is for more accurate rounding
278 KN is K * N
280 #define PITCH_K_FCT 100000UL
281 #define PITCH_N_FCT 10
282 #define PITCH_KN_FCT 1000000UL
284 static int pitch_increase_semitone(int pitch, bool up)
286 uint32_t tmp;
287 uint32_t round_fct; /* How much to scale down at the end */
288 tmp = pitch;
289 if (up)
291 tmp = tmp * PITCH_SEMITONE_FACTOR;
292 round_fct = PITCH_K_FCT;
294 else
296 tmp = (tmp * PITCH_KN_FCT) / PITCH_SEMITONE_FACTOR;
297 round_fct = PITCH_N_FCT;
299 /* Scaling down with rounding */
300 tmp = (tmp + round_fct / 2) / round_fct;
301 return pitch_increase(pitch, tmp - pitch, false);
305 returns:
306 0 on exit
307 1 if USB was connected
310 int gui_syncpitchscreen_run(void)
312 int button, i;
313 int pitch = sound_get_pitch();
314 #if CONFIG_CODEC == SWCODEC
315 int stretch = dsp_get_timestretch();
316 int speed = stretch * pitch; /* speed to maintain */
317 #endif
318 int new_pitch;
319 int pitch_delta;
320 bool nudged = false;
321 bool exit = false;
322 /* should maybe be passed per parameter later, not needed for now */
323 struct viewport parent[NB_SCREENS];
324 struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
325 int max_lines[NB_SCREENS];
327 /* initialize pitchscreen vps */
328 FOR_NB_SCREENS(i)
330 screens[i].clear_display();
331 viewport_set_defaults(&parent[i], i);
332 max_lines[i] = viewport_get_nb_lines(&parent[i]);
333 pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
335 /* also, draw the icons now, it's only needed once */
336 pitchscreen_draw_icons(&screens[i], &parent[i]);
338 #if CONFIG_CODEC == SWCODEC
339 pcmbuf_set_low_latency(true);
340 #endif
342 while (!exit)
344 FOR_NB_SCREENS(i)
345 pitchscreen_draw(&screens[i], max_lines[i],
346 pitch_viewports[i], pitch
347 #if CONFIG_CODEC == SWCODEC
348 , speed
349 #endif
351 pitch_delta = 0;
352 button = get_action(CONTEXT_PITCHSCREEN, HZ);
353 switch (button)
355 case ACTION_PS_INC_SMALL:
356 pitch_delta = PITCH_SMALL_DELTA;
357 break;
359 case ACTION_PS_INC_BIG:
360 pitch_delta = PITCH_BIG_DELTA;
361 break;
363 case ACTION_PS_DEC_SMALL:
364 pitch_delta = -PITCH_SMALL_DELTA;
365 break;
367 case ACTION_PS_DEC_BIG:
368 pitch_delta = -PITCH_BIG_DELTA;
369 break;
371 case ACTION_PS_NUDGE_RIGHT:
372 #if CONFIG_CODEC == SWCODEC
373 if (!pitch_mode_timestretch)
375 #endif
376 new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
377 nudged = (new_pitch != pitch);
378 pitch = new_pitch;
379 break;
380 #if CONFIG_CODEC == SWCODEC
383 case ACTION_PS_FASTER:
384 if (pitch_mode_timestretch && stretch < STRETCH_MAX)
386 stretch++;
387 dsp_set_timestretch(stretch);
388 speed = stretch * pitch;
390 break;
391 #endif
393 case ACTION_PS_NUDGE_RIGHTOFF:
394 if (nudged)
396 pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
397 nudged = false;
399 break;
401 case ACTION_PS_NUDGE_LEFT:
402 #if CONFIG_CODEC == SWCODEC
403 if (!pitch_mode_timestretch)
405 #endif
406 new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
407 nudged = (new_pitch != pitch);
408 pitch = new_pitch;
409 break;
410 #if CONFIG_CODEC == SWCODEC
413 case ACTION_PS_SLOWER:
414 if (pitch_mode_timestretch && stretch > STRETCH_MIN)
416 stretch--;
417 dsp_set_timestretch(stretch);
418 speed = stretch * pitch;
420 break;
421 #endif
423 case ACTION_PS_NUDGE_LEFTOFF:
424 if (nudged)
426 pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
427 nudged = false;
429 break;
431 case ACTION_PS_RESET:
432 pitch = 1000;
433 sound_set_pitch(pitch);
434 #if CONFIG_CODEC == SWCODEC
435 stretch = 100;
436 dsp_set_timestretch(stretch);
437 speed = stretch * pitch;
438 #endif
439 break;
441 case ACTION_PS_TOGGLE_MODE:
442 #if CONFIG_CODEC == SWCODEC
443 if (dsp_timestretch_available() && pitch_mode_semitone)
444 pitch_mode_timestretch = !pitch_mode_timestretch;
445 #endif
446 pitch_mode_semitone = !pitch_mode_semitone;
447 break;
449 case ACTION_PS_EXIT:
450 exit = true;
451 break;
453 default:
454 if (default_event_handler(button) == SYS_USB_CONNECTED)
455 return 1;
456 break;
458 if (pitch_delta)
460 if (pitch_mode_semitone)
461 pitch = pitch_increase_semitone(pitch, pitch_delta > 0);
462 else
463 pitch = pitch_increase(pitch, pitch_delta, true);
464 #if CONFIG_CODEC == SWCODEC
465 if (pitch_mode_timestretch)
467 /* Set stretch to maintain speed */
468 /* i.e. increase pitch, reduce stretch */
469 int new_stretch = speed / pitch;
470 if (new_stretch >= STRETCH_MIN && new_stretch <= STRETCH_MAX)
472 stretch = new_stretch;
473 dsp_set_timestretch(stretch);
476 else
477 speed = stretch * pitch;
478 #endif
481 #if CONFIG_CODEC == SWCODEC
482 pcmbuf_set_low_latency(false);
483 #endif
484 return 0;