diff options
author | William Wilgus <wilgus.william@gmail.com> | 2021-08-19 22:20:56 -0400 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2021-11-10 22:38:37 -0500 |
commit | cf009b4cbb1eec083ab17ce370df090979eaf68e (patch) | |
tree | 19c2346a0e92878ef078879b77e262bf3380ec72 /apps | |
parent | 67fb558c8997f47440b176fe5553246486b1a838 (diff) | |
download | rockbox-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')
-rw-r--r-- | apps/gui/pitchscreen.c | 1055 | ||||
-rw-r--r-- | apps/plugin.c | 17 | ||||
-rw-r--r-- | apps/plugin.h | 23 | ||||
-rw-r--r-- | apps/plugins/CATEGORIES | 1 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 4 | ||||
-rw-r--r-- | apps/plugins/lib/arg_helper.c | 4 | ||||
-rw-r--r-- | apps/plugins/lib/arg_helper.h | 2 | ||||
-rw-r--r-- | apps/plugins/pitch_screen.c | 1279 |
8 files changed, 1314 insertions, 1071 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 | |||
58 | enum | ||
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 | |||
85 | static 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 | |||
126 | static 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 */ | ||
143 | static 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 | |||
166 | static 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 | **/ | ||
184 | static 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 */ | ||
213 | static 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 | |||
232 | static 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 | |||
412 | static 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 | |||
495 | static 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 | |||
536 | static 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 | |||
560 | static 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 | */ | ||
625 | static 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 | |||
725 | int gui_syncpitchscreen_run(void) | 22 | int 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 | } |
diff --git a/apps/plugin.c b/apps/plugin.c index d1f0348cc3..c9c3d047d2 100644 --- a/apps/plugin.c +++ b/apps/plugin.c | |||
@@ -352,6 +352,7 @@ static const struct plugin_api rockbox_api = { | |||
352 | get_action, | 352 | get_action, |
353 | #ifdef HAVE_TOUCHSCREEN | 353 | #ifdef HAVE_TOUCHSCREEN |
354 | action_get_touchscreen_press, | 354 | action_get_touchscreen_press, |
355 | action_get_touchscreen_press_in_vp, | ||
355 | #endif | 356 | #endif |
356 | action_userabort, | 357 | action_userabort, |
357 | 358 | ||
@@ -413,6 +414,7 @@ static const struct plugin_api rockbox_api = { | |||
413 | FS_PREFIX(file_exists), | 414 | FS_PREFIX(file_exists), |
414 | strip_extension, | 415 | strip_extension, |
415 | crc_32, | 416 | crc_32, |
417 | crc_32r, | ||
416 | filetype_get_attr, | 418 | filetype_get_attr, |
417 | 419 | ||
418 | /* dir */ | 420 | /* dir */ |
@@ -593,6 +595,7 @@ static const struct plugin_api rockbox_api = { | |||
593 | sound_enum_hw_eq_band_setting, | 595 | sound_enum_hw_eq_band_setting, |
594 | #endif | 596 | #endif |
595 | #if defined (HAVE_PITCHCONTROL) | 597 | #if defined (HAVE_PITCHCONTROL) |
598 | sound_get_pitch, | ||
596 | sound_set_pitch, | 599 | sound_set_pitch, |
597 | #endif | 600 | #endif |
598 | &audio_master_sampr_list[0], | 601 | &audio_master_sampr_list[0], |
@@ -622,7 +625,10 @@ static const struct plugin_api rockbox_api = { | |||
622 | dsp_eq_enable, | 625 | dsp_eq_enable, |
623 | dsp_dither_enable, | 626 | dsp_dither_enable, |
624 | #ifdef HAVE_PITCHCONTROL | 627 | #ifdef HAVE_PITCHCONTROL |
628 | dsp_get_timestretch, | ||
625 | dsp_set_timestretch, | 629 | dsp_set_timestretch, |
630 | dsp_timestretch_enable, | ||
631 | dsp_timestretch_available, | ||
626 | #endif | 632 | #endif |
627 | dsp_configure, | 633 | dsp_configure, |
628 | dsp_get_config, | 634 | dsp_get_config, |
@@ -641,6 +647,7 @@ static const struct plugin_api rockbox_api = { | |||
641 | mixer_get_frequency, | 647 | mixer_get_frequency, |
642 | 648 | ||
643 | pcmbuf_fade, | 649 | pcmbuf_fade, |
650 | pcmbuf_set_low_latency, | ||
644 | system_sound_play, | 651 | system_sound_play, |
645 | keyclick_click, | 652 | keyclick_click, |
646 | 653 | ||
@@ -692,6 +699,9 @@ static const struct plugin_api rockbox_api = { | |||
692 | audio_current_track, | 699 | audio_current_track, |
693 | audio_flush_and_reload_tracks, | 700 | audio_flush_and_reload_tracks, |
694 | audio_get_file_pos, | 701 | audio_get_file_pos, |
702 | #ifdef PLUGIN_USE_IRAM | ||
703 | audio_hard_stop, | ||
704 | #endif | ||
695 | 705 | ||
696 | /* menu */ | 706 | /* menu */ |
697 | root_menu_get_options, | 707 | root_menu_get_options, |
@@ -735,6 +745,7 @@ static const struct plugin_api rockbox_api = { | |||
735 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 745 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
736 | __errno, | 746 | __errno, |
737 | #endif | 747 | #endif |
748 | led, | ||
738 | srand, | 749 | srand, |
739 | rand, | 750 | rand, |
740 | (void *)qsort, | 751 | (void *)qsort, |
@@ -780,7 +791,6 @@ static const struct plugin_api rockbox_api = { | |||
780 | detect_flashed_ramimage, | 791 | detect_flashed_ramimage, |
781 | detect_flashed_romimage, | 792 | detect_flashed_romimage, |
782 | #endif | 793 | #endif |
783 | led, | ||
784 | 794 | ||
785 | /*plugin*/ | 795 | /*plugin*/ |
786 | plugin_open, | 796 | plugin_open, |
@@ -789,11 +799,6 @@ static const struct plugin_api rockbox_api = { | |||
789 | plugin_release_audio_buffer, /* defined in plugin.c */ | 799 | plugin_release_audio_buffer, /* defined in plugin.c */ |
790 | plugin_tsr, /* defined in plugin.c */ | 800 | plugin_tsr, /* defined in plugin.c */ |
791 | plugin_get_current_filename, | 801 | plugin_get_current_filename, |
792 | #ifdef PLUGIN_USE_IRAM | ||
793 | audio_hard_stop, | ||
794 | #endif | ||
795 | crc_32r, | ||
796 | |||
797 | /* new stuff at the end, sort into place next time | 802 | /* new stuff at the end, sort into place next time |
798 | the API gets incompatible */ | 803 | the API gets incompatible */ |
799 | 804 | ||
diff --git a/apps/plugin.h b/apps/plugin.h index bd95331143..6ca0488e06 100644 --- a/apps/plugin.h +++ b/apps/plugin.h | |||
@@ -155,12 +155,12 @@ int plugin_open(const char *plugin, const char *parameter); | |||
155 | #define PLUGIN_MAGIC 0x526F634B /* RocK */ | 155 | #define PLUGIN_MAGIC 0x526F634B /* RocK */ |
156 | 156 | ||
157 | /* increase this every time the api struct changes */ | 157 | /* increase this every time the api struct changes */ |
158 | #define PLUGIN_API_VERSION 244 | 158 | #define PLUGIN_API_VERSION 245 |
159 | 159 | ||
160 | /* update this to latest version if a change to the api struct breaks | 160 | /* update this to latest version if a change to the api struct breaks |
161 | backwards compatibility (and please take the opportunity to sort in any | 161 | backwards compatibility (and please take the opportunity to sort in any |
162 | new function which are "waiting" at the end of the function table) */ | 162 | new function which are "waiting" at the end of the function table) */ |
163 | #define PLUGIN_MIN_API_VERSION 244 | 163 | #define PLUGIN_MIN_API_VERSION 245 |
164 | 164 | ||
165 | /* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */ | 165 | /* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */ |
166 | 166 | ||
@@ -400,6 +400,7 @@ struct plugin_api { | |||
400 | int (*get_action)(int context, int timeout); | 400 | int (*get_action)(int context, int timeout); |
401 | #ifdef HAVE_TOUCHSCREEN | 401 | #ifdef HAVE_TOUCHSCREEN |
402 | int (*action_get_touchscreen_press)(short *x, short *y); | 402 | int (*action_get_touchscreen_press)(short *x, short *y); |
403 | int (*action_get_touchscreen_press_in_vp)(short *x1, short *y1, struct viewport *vp); | ||
403 | #endif | 404 | #endif |
404 | bool (*action_userabort)(int timeout); | 405 | bool (*action_userabort)(int timeout); |
405 | 406 | ||
@@ -462,6 +463,7 @@ struct plugin_api { | |||
462 | bool (*file_exists)(const char *path); | 463 | bool (*file_exists)(const char *path); |
463 | char* (*strip_extension)(char* buffer, int buffer_size, const char *filename); | 464 | char* (*strip_extension)(char* buffer, int buffer_size, const char *filename); |
464 | uint32_t (*crc_32)(const void *src, uint32_t len, uint32_t crc32); | 465 | uint32_t (*crc_32)(const void *src, uint32_t len, uint32_t crc32); |
466 | uint32_t (*crc_32r)(const void *src, uint32_t len, uint32_t crc32); | ||
465 | 467 | ||
466 | int (*filetype_get_attr)(const char* file); | 468 | int (*filetype_get_attr)(const char* file); |
467 | 469 | ||
@@ -667,6 +669,7 @@ struct plugin_api { | |||
667 | unsigned int band_setting); | 669 | unsigned int band_setting); |
668 | #endif /* AUDIOHW_HAVE_EQ */ | 670 | #endif /* AUDIOHW_HAVE_EQ */ |
669 | #if defined (HAVE_PITCHCONTROL) | 671 | #if defined (HAVE_PITCHCONTROL) |
672 | int32_t (*sound_get_pitch)(void); | ||
670 | void (*sound_set_pitch)(int32_t pitch); | 673 | void (*sound_set_pitch)(int32_t pitch); |
671 | #endif | 674 | #endif |
672 | const unsigned long *audio_master_sampr_list; | 675 | const unsigned long *audio_master_sampr_list; |
@@ -701,7 +704,10 @@ struct plugin_api { | |||
701 | void (*dsp_eq_enable)(bool enable); | 704 | void (*dsp_eq_enable)(bool enable); |
702 | void (*dsp_dither_enable)(bool enable); | 705 | void (*dsp_dither_enable)(bool enable); |
703 | #ifdef HAVE_PITCHCONTROL | 706 | #ifdef HAVE_PITCHCONTROL |
707 | int32_t (*dsp_get_timestretch)(void); | ||
704 | void (*dsp_set_timestretch)(int32_t percent); | 708 | void (*dsp_set_timestretch)(int32_t percent); |
709 | void (*dsp_timestretch_enable)(bool enabled); | ||
710 | bool (*dsp_timestretch_available)(void); | ||
705 | #endif | 711 | #endif |
706 | intptr_t (*dsp_configure)(struct dsp_config *dsp, | 712 | intptr_t (*dsp_configure)(struct dsp_config *dsp, |
707 | unsigned int setting, intptr_t value); | 713 | unsigned int setting, intptr_t value); |
@@ -727,6 +733,7 @@ struct plugin_api { | |||
727 | void (*mixer_set_frequency)(unsigned int samplerate); | 733 | void (*mixer_set_frequency)(unsigned int samplerate); |
728 | unsigned int (*mixer_get_frequency)(void); | 734 | unsigned int (*mixer_get_frequency)(void); |
729 | void (*pcmbuf_fade)(bool fade, bool in); | 735 | void (*pcmbuf_fade)(bool fade, bool in); |
736 | void (*pcmbuf_set_low_latency)(bool state); | ||
730 | void (*system_sound_play)(enum system_sound sound); | 737 | void (*system_sound_play)(enum system_sound sound); |
731 | void (*keyclick_click)(bool rawbutton, int action); | 738 | void (*keyclick_click)(bool rawbutton, int action); |
732 | 739 | ||
@@ -793,6 +800,9 @@ struct plugin_api { | |||
793 | struct mp3entry* (*audio_current_track)(void); | 800 | struct mp3entry* (*audio_current_track)(void); |
794 | void (*audio_flush_and_reload_tracks)(void); | 801 | void (*audio_flush_and_reload_tracks)(void); |
795 | int (*audio_get_file_pos)(void); | 802 | int (*audio_get_file_pos)(void); |
803 | #ifdef PLUGIN_USE_IRAM | ||
804 | void (*audio_hard_stop)(void); | ||
805 | #endif | ||
796 | 806 | ||
797 | /* menu */ | 807 | /* menu */ |
798 | struct menu_table *(*root_menu_get_options)(int *nb_options); | 808 | struct menu_table *(*root_menu_get_options)(int *nb_options); |
@@ -853,6 +863,7 @@ struct plugin_api { | |||
853 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 863 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
854 | int * (*__errno)(void); | 864 | int * (*__errno)(void); |
855 | #endif | 865 | #endif |
866 | void (*led)(bool on); | ||
856 | void (*srand)(unsigned int seed); | 867 | void (*srand)(unsigned int seed); |
857 | int (*rand)(void); | 868 | int (*rand)(void); |
858 | void (*qsort)(void *base, size_t nmemb, size_t size, | 869 | void (*qsort)(void *base, size_t nmemb, size_t size, |
@@ -907,9 +918,6 @@ struct plugin_api { | |||
907 | bool (*detect_flashed_ramimage)(void); | 918 | bool (*detect_flashed_ramimage)(void); |
908 | bool (*detect_flashed_romimage)(void); | 919 | bool (*detect_flashed_romimage)(void); |
909 | #endif | 920 | #endif |
910 | |||
911 | void (*led)(bool on); | ||
912 | |||
913 | /*plugin*/ | 921 | /*plugin*/ |
914 | int (*plugin_open)(const char *path, const char *parameter); | 922 | int (*plugin_open)(const char *path, const char *parameter); |
915 | void* (*plugin_get_buffer)(size_t *buffer_size); | 923 | void* (*plugin_get_buffer)(size_t *buffer_size); |
@@ -917,11 +925,6 @@ struct plugin_api { | |||
917 | void (*plugin_release_audio_buffer)(void); | 925 | void (*plugin_release_audio_buffer)(void); |
918 | void (*plugin_tsr)(bool (*exit_callback)(bool reenter)); | 926 | void (*plugin_tsr)(bool (*exit_callback)(bool reenter)); |
919 | char* (*plugin_get_current_filename)(void); | 927 | char* (*plugin_get_current_filename)(void); |
920 | #ifdef PLUGIN_USE_IRAM | ||
921 | void (*audio_hard_stop)(void); | ||
922 | #endif | ||
923 | uint32_t (*crc_32r)(const void *src, uint32_t len, uint32_t crc32); | ||
924 | |||
925 | /* new stuff at the end, sort into place next time | 928 | /* new stuff at the end, sort into place next time |
926 | the API gets incompatible */ | 929 | the API gets incompatible */ |
927 | 930 | ||
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index d3093689f9..471a32b9e1 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES | |||
@@ -76,6 +76,7 @@ pegbox,games | |||
76 | periodic_table,apps | 76 | periodic_table,apps |
77 | pictureflow,demos | 77 | pictureflow,demos |
78 | pitch_detector,apps | 78 | pitch_detector,apps |
79 | pitch_screen,viewers | ||
79 | pixel-painter,games | 80 | pixel-painter,games |
80 | plasma,demos | 81 | plasma,demos |
81 | png,viewers | 82 | png,viewers |
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 910ffe4161..ec13c17dc9 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES | |||
@@ -48,6 +48,10 @@ lamp.c | |||
48 | pitch_detector.c | 48 | pitch_detector.c |
49 | #endif | 49 | #endif |
50 | 50 | ||
51 | #ifdef HAVE_PITCHCONTROL | ||
52 | pitch_screen.c | ||
53 | #endif | ||
54 | |||
51 | mp3_encoder.c | 55 | mp3_encoder.c |
52 | wav2wv.c | 56 | wav2wv.c |
53 | 57 | ||
diff --git a/apps/plugins/lib/arg_helper.c b/apps/plugins/lib/arg_helper.c index d402300900..3ea5ba714d 100644 --- a/apps/plugins/lib/arg_helper.c +++ b/apps/plugins/lib/arg_helper.c | |||
@@ -28,7 +28,9 @@ | |||
28 | 28 | ||
29 | #define SWCHAR '-' | 29 | #define SWCHAR '-' |
30 | #define DECSEPCHAR '.' | 30 | #define DECSEPCHAR '.' |
31 | 31 | #ifdef PLUGIN | |
32 | #define strchr rb->strchr | ||
33 | #endif | ||
32 | int string_parse(const char **parameter, char* buf, size_t buf_sz) | 34 | int string_parse(const char **parameter, char* buf, size_t buf_sz) |
33 | { | 35 | { |
34 | /* fills buf with a string upto buf_sz, null terminates the buffer | 36 | /* fills buf with a string upto buf_sz, null terminates the buffer |
diff --git a/apps/plugins/lib/arg_helper.h b/apps/plugins/lib/arg_helper.h index c7b14f7f7a..2cf94ba1dd 100644 --- a/apps/plugins/lib/arg_helper.h +++ b/apps/plugins/lib/arg_helper.h | |||
@@ -26,7 +26,7 @@ | |||
26 | #define ARGPARSE_MAX_FRAC_DIGITS 9 /* Uses 30 bits max (0.999999999) */ | 26 | #define ARGPARSE_MAX_FRAC_DIGITS 9 /* Uses 30 bits max (0.999999999) */ |
27 | 27 | ||
28 | #define ARGP_EXP(a, b) (a ##E## b) | 28 | #define ARGP_EXP(a, b) (a ##E## b) |
29 | #define ARGP_FRAC_DEC_MULTIPLIER(n) AP_EXP(1,n) /*1x10^n*/ | 29 | #define ARGP_FRAC_DEC_MULTIPLIER(n) ARGP_EXP(1,n) /*1x10^n*/ |
30 | #define ARGPARSE_FRAC_DEC_MULTIPLIER \ | 30 | #define ARGPARSE_FRAC_DEC_MULTIPLIER \ |
31 | (long)ARGP_FRAC_DEC_MULTIPLIER(ARGPARSE_MAX_FRAC_DIGITS) | 31 | (long)ARGP_FRAC_DEC_MULTIPLIER(ARGPARSE_MAX_FRAC_DIGITS) |
32 | 32 | ||
diff --git a/apps/plugins/pitch_screen.c b/apps/plugins/pitch_screen.c new file mode 100644 index 0000000000..e24e0240a2 --- /dev/null +++ b/apps/plugins/pitch_screen.c | |||
@@ -0,0 +1,1279 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Björn Stenberg | ||
11 | * | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #include "plugin.h" | ||
23 | #include "lib/icon_helper.h" | ||
24 | #include "lib/arg_helper.h" | ||
25 | |||
26 | #define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */ | ||
27 | /* on both sides when drawing */ | ||
28 | |||
29 | #define PITCH_MAX (200 * PITCH_SPEED_PRECISION) | ||
30 | #define PITCH_MIN (50 * PITCH_SPEED_PRECISION) | ||
31 | #define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */ | ||
32 | #define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */ | ||
33 | #define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */ | ||
34 | |||
35 | #define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */ | ||
36 | #define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */ | ||
37 | |||
38 | #define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */ | ||
39 | #define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */ | ||
40 | |||
41 | #define PVAR_VERBOSE 0x01 | ||
42 | #define PVAR_GUI 0x02 | ||
43 | struct pvars | ||
44 | { | ||
45 | int32_t speed; | ||
46 | int32_t pitch; | ||
47 | int32_t stretch; | ||
48 | int32_t flags; | ||
49 | }; | ||
50 | static struct pvars pitch_vars; | ||
51 | |||
52 | enum | ||
53 | { | ||
54 | PITCH_TOP = 0, | ||
55 | PITCH_MID, | ||
56 | PITCH_BOTTOM, | ||
57 | PITCH_ITEM_COUNT, | ||
58 | }; | ||
59 | |||
60 | /* This is a table of semitone percentage values of the appropriate | ||
61 | precision (based on PITCH_SPEED_PRECISION). Note that these are | ||
62 | all constant expressions, which will be evaluated at compile time, | ||
63 | so no need to worry about how complex the expressions look. | ||
64 | That's just to get the precision right. | ||
65 | |||
66 | I calculated these values, starting from 50, as | ||
67 | |||
68 | x(n) = 50 * 2^(n/12) | ||
69 | |||
70 | All that math in each entry simply converts the float constant | ||
71 | to an integer equal to PITCH_SPEED_PRECISION times the float value, | ||
72 | with as little precision loss as possible (i.e. correctly rounding | ||
73 | the last digit). | ||
74 | */ | ||
75 | #define TO_INT_WITH_PRECISION(x) \ | ||
76 | ( (unsigned short)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) ) | ||
77 | |||
78 | static const unsigned short semitone_table[] = | ||
79 | { | ||
80 | TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */ | ||
81 | TO_INT_WITH_PRECISION(52.97315472), | ||
82 | TO_INT_WITH_PRECISION(56.12310242), | ||
83 | TO_INT_WITH_PRECISION(59.46035575), | ||
84 | TO_INT_WITH_PRECISION(62.99605249), | ||
85 | TO_INT_WITH_PRECISION(66.74199271), | ||
86 | TO_INT_WITH_PRECISION(70.71067812), | ||
87 | TO_INT_WITH_PRECISION(74.91535384), | ||
88 | TO_INT_WITH_PRECISION(79.37005260), | ||
89 | TO_INT_WITH_PRECISION(84.08964153), | ||
90 | TO_INT_WITH_PRECISION(89.08987181), | ||
91 | TO_INT_WITH_PRECISION(94.38743127), | ||
92 | TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */ | ||
93 | TO_INT_WITH_PRECISION(105.9463094), | ||
94 | TO_INT_WITH_PRECISION(112.2462048), | ||
95 | TO_INT_WITH_PRECISION(118.9207115), | ||
96 | TO_INT_WITH_PRECISION(125.9921049), | ||
97 | TO_INT_WITH_PRECISION(133.4839854), | ||
98 | TO_INT_WITH_PRECISION(141.4213562), | ||
99 | TO_INT_WITH_PRECISION(149.8307077), | ||
100 | TO_INT_WITH_PRECISION(158.7401052), | ||
101 | TO_INT_WITH_PRECISION(168.1792831), | ||
102 | TO_INT_WITH_PRECISION(178.1797436), | ||
103 | TO_INT_WITH_PRECISION(188.7748625), | ||
104 | TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */ | ||
105 | }; | ||
106 | |||
107 | #define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0]))) | ||
108 | #define SEMITONE_END (NUM_SEMITONES/2) | ||
109 | #define SEMITONE_START (-SEMITONE_END) | ||
110 | |||
111 | /* A table of values for approximating the cent curve with | ||
112 | linear interpolation. Multipy the next lowest semitone | ||
113 | by this much to find the corresponding cent percentage. | ||
114 | |||
115 | These values were calculated as | ||
116 | x(n) = 100 * 2^(n * 20/1200) | ||
117 | */ | ||
118 | |||
119 | static const unsigned short cent_interp[] = | ||
120 | { | ||
121 | TO_INT_WITH_PRECISION(100.0000000), | ||
122 | TO_INT_WITH_PRECISION(101.1619440), | ||
123 | TO_INT_WITH_PRECISION(102.3373892), | ||
124 | TO_INT_WITH_PRECISION(103.5264924), | ||
125 | TO_INT_WITH_PRECISION(104.7294123), | ||
126 | /* this one's the next semitone but we have it here for convenience */ | ||
127 | TO_INT_WITH_PRECISION(105.9463094), | ||
128 | }; | ||
129 | |||
130 | int viewport_get_nb_lines(const struct viewport *vp) | ||
131 | { | ||
132 | return vp->height/rb->font_get(vp->font)->height; | ||
133 | } | ||
134 | #if 0 /* replaced with cbmp_get_icon(CBMP_Mono_7x8, Icon_ABCD, &w, &h) */ | ||
135 | enum icons_7x8 { | ||
136 | Icon_Plug, | ||
137 | Icon_USBPlug, | ||
138 | Icon_Mute, | ||
139 | Icon_Play, | ||
140 | Icon_Stop, | ||
141 | Icon_Pause, | ||
142 | Icon_FastForward, | ||
143 | Icon_FastBackward, | ||
144 | Icon_Record, | ||
145 | Icon_RecPause, | ||
146 | Icon_Radio, | ||
147 | Icon_Radio_Mute, | ||
148 | Icon_Repeat, | ||
149 | Icon_RepeatOne, | ||
150 | Icon_Shuffle, | ||
151 | Icon_DownArrow, | ||
152 | Icon_UpArrow, | ||
153 | Icon_RepeatAB, | ||
154 | Icon7x8Last | ||
155 | }; | ||
156 | |||
157 | const unsigned char bitmap_icons_7x8[][7] = | ||
158 | { | ||
159 | {0x08,0x1c,0x3e,0x3e,0x3e,0x14,0x14}, /* Power plug */ | ||
160 | {0x1c,0x14,0x3e,0x2a,0x22,0x1c,0x08}, /* USB plug */ | ||
161 | {0x01,0x1e,0x1c,0x3e,0x7f,0x20,0x40}, /* Speaker mute */ | ||
162 | {0x00,0x7f,0x7f,0x3e,0x1c,0x08,0x00}, /* Play */ | ||
163 | {0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f}, /* Stop */ | ||
164 | {0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x00}, /* Pause */ | ||
165 | {0x7f,0x3e,0x1c,0x7f,0x3e,0x1c,0x08}, /* Fast forward */ | ||
166 | {0x08,0x1c,0x3e,0x7f,0x1c,0x3e,0x7f}, /* Fast backward */ | ||
167 | {0x1c,0x3e,0x7f,0x7f,0x7f,0x3e,0x1c}, /* Record */ | ||
168 | {0x1c,0x3e,0x7f,0x00,0x7f,0x3e,0x1c}, /* Record pause */ | ||
169 | {0x40,0xa0,0xa0,0xa0,0x7f,0x02,0x02}, /* Radio on */ | ||
170 | {0x42,0xa4,0xa8,0xb0,0x7f,0x22,0x42}, /* Radio mute */ | ||
171 | {0x44,0x4e,0x5f,0x44,0x44,0x44,0x38}, /* Repeat playmode */ | ||
172 | {0x44,0x4e,0x5f,0x44,0x38,0x02,0x7f}, /* Repeat-one playmode */ | ||
173 | {0x3e,0x41,0x51,0x41,0x45,0x41,0x3e}, /* Shuffle playmode (dice) */ | ||
174 | {0x04,0x0c,0x1c,0x3c,0x1c,0x0c,0x04}, /* Down-arrow */ | ||
175 | {0x20,0x30,0x38,0x3c,0x38,0x30,0x20}, /* Up-arrow */ | ||
176 | {0x7f,0x04,0x4e,0x5f,0x44,0x38,0x7f} /* Repeat-AB playmode */ | ||
177 | }; | ||
178 | #endif | ||
179 | |||
180 | /* Number of cents between entries in the cent_interp table */ | ||
181 | #define CENT_INTERP_INTERVAL 20 | ||
182 | #define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0]))) | ||
183 | |||
184 | /* This stores whether the pitch and speed are at their own limits */ | ||
185 | /* or that of the timestretching algorithm */ | ||
186 | static bool at_limit = false; | ||
187 | |||
188 | /* | ||
189 | * | ||
190 | * The pitchscreen is divided into 3 viewports (each row is a viewport) | ||
191 | * Then each viewport is again divided into 3 colums, each showsing some infos | ||
192 | * Additionally, on touchscreen, each cell represents a button | ||
193 | * | ||
194 | * Below a sketch describing what each cell will show (what's drawn on it) | ||
195 | * -------------------------- | ||
196 | * | | | | <-- pitch up in the middle (text and button) | ||
197 | * | | | | <-- arrows for mode toggling on the sides for touchscreen | ||
198 | * |------------------------| | ||
199 | * | | | | <-- semitone/speed up/down on the sides | ||
200 | * | | | | <-- reset pitch&speed in the middle | ||
201 | * |------------------------| | ||
202 | * | | | | <-- pitch down in the middle | ||
203 | * | | | | <-- Two "OK" for exit on the sides for touchscreen | ||
204 | * |------------------------| | ||
205 | * | ||
206 | * | ||
207 | */ | ||
208 | |||
209 | static void speak_pitch_mode(bool enqueue) | ||
210 | { | ||
211 | bool timestretch_mode = rb->global_settings->pitch_mode_timestretch && rb->dsp_timestretch_available(); | ||
212 | if (timestretch_mode) | ||
213 | rb->talk_id(VOICE_PITCH_TIMESTRETCH_MODE, enqueue); | ||
214 | if (rb->global_settings->pitch_mode_semitone) | ||
215 | rb->talk_id(VOICE_PITCH_SEMITONE_MODE, timestretch_mode ? true : enqueue); | ||
216 | else | ||
217 | rb->talk_id(VOICE_PITCH_ABSOLUTE_MODE, timestretch_mode ? true : enqueue); | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | * Fixes the viewports so they represent the 3 rows, and adds a little margin | ||
223 | * on all sides for the icons (which are drawn outside of the grid | ||
224 | * | ||
225 | * The modified viewports need to be passed to the touchscreen handling function | ||
226 | **/ | ||
227 | static void pitchscreen_fix_viewports(struct viewport *parent, | ||
228 | struct viewport pitch_viewports[PITCH_ITEM_COUNT]) | ||
229 | { | ||
230 | int i, font_height; | ||
231 | font_height = rb->font_get(parent->font)->height; | ||
232 | for (i = 0; i < PITCH_ITEM_COUNT; i++) | ||
233 | { | ||
234 | pitch_viewports[i] = *parent; | ||
235 | pitch_viewports[i].height = parent->height / PITCH_ITEM_COUNT; | ||
236 | pitch_viewports[i].x += ICON_BORDER; | ||
237 | pitch_viewports[i].width -= 2*ICON_BORDER; | ||
238 | } | ||
239 | pitch_viewports[PITCH_TOP].y += ICON_BORDER; | ||
240 | pitch_viewports[PITCH_TOP].height -= ICON_BORDER; | ||
241 | |||
242 | if(pitch_viewports[PITCH_MID].height < font_height * 2) | ||
243 | pitch_viewports[PITCH_MID].height = font_height * 2; | ||
244 | |||
245 | pitch_viewports[PITCH_MID].y = pitch_viewports[PITCH_TOP].y | ||
246 | + pitch_viewports[PITCH_TOP].height; | ||
247 | |||
248 | pitch_viewports[PITCH_BOTTOM].y = pitch_viewports[PITCH_MID].y | ||
249 | + pitch_viewports[PITCH_MID].height; | ||
250 | |||
251 | pitch_viewports[PITCH_BOTTOM].height -= ICON_BORDER; | ||
252 | } | ||
253 | |||
254 | /* must be called before pitchscreen_draw, or within | ||
255 | * since it neither clears nor updates the display */ | ||
256 | static void pitchscreen_draw_icons(struct screen *display, | ||
257 | struct viewport *parent) | ||
258 | { | ||
259 | |||
260 | display->set_viewport(parent); | ||
261 | int w, h; | ||
262 | const unsigned char* uparrow = cbmp_get_icon(CBMP_Mono_7x8, Icon_UpArrow, &w, &h); | ||
263 | if (uparrow) | ||
264 | display->mono_bitmap(uparrow, parent->width/2 - 3, 2, w, h); | ||
265 | |||
266 | const unsigned char* dnarrow = cbmp_get_icon(CBMP_Mono_7x8, Icon_DownArrow, &w, &h); | ||
267 | if (dnarrow) | ||
268 | display->mono_bitmap(dnarrow, parent->width /2 - 3, parent->height - 10, w, h); | ||
269 | |||
270 | const unsigned char* fastfwd = cbmp_get_icon(CBMP_Mono_7x8, Icon_FastForward, &w, &h); | ||
271 | if (fastfwd) | ||
272 | display->mono_bitmap(fastfwd, parent->width - 10, parent->height /2 - 4, 7, 8); | ||
273 | |||
274 | const unsigned char* fastrew = cbmp_get_icon(CBMP_Mono_7x8, Icon_FastBackward, &w, &h); | ||
275 | if (fastrew) | ||
276 | display->mono_bitmap(fastrew, 2, parent->height /2 - 4, w, h); | ||
277 | |||
278 | display->update_viewport(); | ||
279 | |||
280 | } | ||
281 | |||
282 | static void pitchscreen_draw(struct screen *display, int max_lines, | ||
283 | struct viewport pitch_viewports[PITCH_ITEM_COUNT], | ||
284 | int32_t pitch, int32_t semitone | ||
285 | ,int32_t speed | ||
286 | ) | ||
287 | { | ||
288 | const char* ptr; | ||
289 | char buf[32]; | ||
290 | int w, h; | ||
291 | bool show_lang_pitch; | ||
292 | struct viewport *last_vp = NULL; | ||
293 | |||
294 | /* "Pitch up/Pitch down" - hide for a small screen, | ||
295 | * the text is drawn centered automatically | ||
296 | * | ||
297 | * note: this assumes 5 lines always fit on a touchscreen (should be | ||
298 | * reasonable) */ | ||
299 | if (max_lines >= 5) | ||
300 | { | ||
301 | int w, h; | ||
302 | struct viewport *vp = &pitch_viewports[PITCH_TOP]; | ||
303 | last_vp = display->set_viewport(vp); | ||
304 | display->clear_viewport(); | ||
305 | #ifdef HAVE_TOUCHSCREEN | ||
306 | /* two arrows in the top row, left and right column */ | ||
307 | char *arrows[] = { "<", ">"}; | ||
308 | display->getstringsize(arrows[0], &w, &h); | ||
309 | display->putsxy(0, vp->height/2 - h/2, arrows[0]); | ||
310 | display->putsxy(vp->width - w, vp->height/2 - h/2, arrows[1]); | ||
311 | #endif | ||
312 | /* UP: Pitch Up */ | ||
313 | if (rb->global_settings->pitch_mode_semitone) | ||
314 | ptr = rb->str(LANG_PITCH_UP_SEMITONE); | ||
315 | else | ||
316 | ptr = rb->str(LANG_PITCH_UP); | ||
317 | |||
318 | display->getstringsize(ptr, &w, NULL); | ||
319 | /* draw text */ | ||
320 | display->putsxy(vp->width/2 - w/2, 0, ptr); | ||
321 | display->update_viewport(); | ||
322 | |||
323 | /* DOWN: Pitch Down */ | ||
324 | vp = &pitch_viewports[PITCH_BOTTOM]; | ||
325 | display->set_viewport(vp); | ||
326 | display->clear_viewport(); | ||
327 | |||
328 | #ifdef HAVE_TOUCHSCREEN | ||
329 | ptr = rb->str(LANG_KBD_OK); | ||
330 | display->getstringsize(ptr, &w, &h); | ||
331 | /* one OK in the middle first column of the vp (at half height) */ | ||
332 | display->putsxy(vp->width/6 - w/2, vp->height/2 - h/2, ptr); | ||
333 | /* one OK in the middle of the last column of the vp (at half height) */ | ||
334 | display->putsxy(5*vp->width/6 - w/2, vp->height/2 - h/2, ptr); | ||
335 | #endif | ||
336 | if (rb->global_settings->pitch_mode_semitone) | ||
337 | ptr = rb->str(LANG_PITCH_DOWN_SEMITONE); | ||
338 | else | ||
339 | ptr = rb->str(LANG_PITCH_DOWN); | ||
340 | display->getstringsize(ptr, &w, &h); | ||
341 | /* draw text */ | ||
342 | display->putsxy(vp->width/2 - w/2, vp->height - h, ptr); | ||
343 | display->update_viewport(); | ||
344 | } | ||
345 | |||
346 | /* Middle section */ | ||
347 | display->set_viewport(&pitch_viewports[PITCH_MID]); | ||
348 | display->clear_viewport(); | ||
349 | int width_used = 0; | ||
350 | |||
351 | /* Middle section upper line - hide for a small screen */ | ||
352 | if ((show_lang_pitch = (max_lines >= 3))) | ||
353 | { | ||
354 | if(rb->global_settings->pitch_mode_timestretch) | ||
355 | { | ||
356 | /* Pitch:XXX.X% */ | ||
357 | if(rb->global_settings->pitch_mode_semitone) | ||
358 | { | ||
359 | rb->snprintf(buf, sizeof(buf), "%s: %s%d.%02d", rb->str(LANG_PITCH), | ||
360 | semitone >= 0 ? "+" : "-", | ||
361 | abs(semitone / PITCH_SPEED_PRECISION), | ||
362 | abs((semitone % PITCH_SPEED_PRECISION) / | ||
363 | (PITCH_SPEED_PRECISION / 100)) | ||
364 | ); | ||
365 | } | ||
366 | else | ||
367 | { | ||
368 | rb->snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", rb->str(LANG_PITCH), | ||
369 | pitch / PITCH_SPEED_PRECISION, | ||
370 | (pitch % PITCH_SPEED_PRECISION) / | ||
371 | (PITCH_SPEED_PRECISION / 10)); | ||
372 | } | ||
373 | } | ||
374 | else | ||
375 | { | ||
376 | /* Rate */ | ||
377 | rb->snprintf(buf, sizeof(buf), "%s:", rb->str(LANG_PLAYBACK_RATE)); | ||
378 | } | ||
379 | display->getstringsize(buf, &w, &h); | ||
380 | display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), | ||
381 | (pitch_viewports[PITCH_MID].height / 2) - h, buf); | ||
382 | if (w > width_used) | ||
383 | width_used = w; | ||
384 | } | ||
385 | |||
386 | /* Middle section lower line */ | ||
387 | /* "Speed:XXX%" */ | ||
388 | if(rb->global_settings->pitch_mode_timestretch) | ||
389 | { | ||
390 | rb->snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", rb->str(LANG_SPEED), | ||
391 | speed / PITCH_SPEED_PRECISION, | ||
392 | (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10)); | ||
393 | } | ||
394 | else | ||
395 | { | ||
396 | if(rb->global_settings->pitch_mode_semitone) | ||
397 | { | ||
398 | rb->snprintf(buf, sizeof(buf), "%s%d.%02d", | ||
399 | semitone >= 0 ? "+" : "-", | ||
400 | abs(semitone / PITCH_SPEED_PRECISION), | ||
401 | abs((semitone % PITCH_SPEED_PRECISION) / | ||
402 | (PITCH_SPEED_PRECISION / 100)) | ||
403 | ); | ||
404 | } | ||
405 | else | ||
406 | { | ||
407 | rb->snprintf(buf, sizeof(buf), "%ld.%ld%%", | ||
408 | pitch / PITCH_SPEED_PRECISION, | ||
409 | (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10)); | ||
410 | } | ||
411 | } | ||
412 | |||
413 | display->getstringsize(buf, &w, &h); | ||
414 | display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), | ||
415 | show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) : | ||
416 | (pitch_viewports[PITCH_MID].height / 2) - (h / 2), | ||
417 | buf); | ||
418 | if (w > width_used) | ||
419 | width_used = w; | ||
420 | |||
421 | /* "limit" and "timestretch" labels */ | ||
422 | if (max_lines >= 7) | ||
423 | { | ||
424 | if(at_limit) | ||
425 | { | ||
426 | const char * const p = rb->str(LANG_STRETCH_LIMIT); | ||
427 | display->getstringsize(p, &w, &h); | ||
428 | display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), | ||
429 | (pitch_viewports[PITCH_MID].height / 2) + h, p); | ||
430 | if (w > width_used) | ||
431 | width_used = w; | ||
432 | } | ||
433 | } | ||
434 | |||
435 | /* Middle section left/right labels */ | ||
436 | const char *leftlabel = "-2%"; | ||
437 | const char *rightlabel = "+2%"; | ||
438 | if (rb->global_settings->pitch_mode_timestretch) | ||
439 | { | ||
440 | leftlabel = "<<"; | ||
441 | rightlabel = ">>"; | ||
442 | } | ||
443 | |||
444 | /* Only display if they fit */ | ||
445 | display->getstringsize(leftlabel, &w, &h); | ||
446 | width_used += w; | ||
447 | display->getstringsize(rightlabel, &w, &h); | ||
448 | width_used += w; | ||
449 | |||
450 | if (width_used <= pitch_viewports[PITCH_MID].width) | ||
451 | { | ||
452 | display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2), | ||
453 | leftlabel); | ||
454 | display->putsxy((pitch_viewports[PITCH_MID].width - w), | ||
455 | (pitch_viewports[PITCH_MID].height / 2) - (h / 2), | ||
456 | rightlabel); | ||
457 | } | ||
458 | display->update_viewport(); | ||
459 | display->set_viewport(last_vp); | ||
460 | } | ||
461 | |||
462 | static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff | ||
463 | /* need this to maintain correct pitch/speed caps */ | ||
464 | , int32_t speed | ||
465 | ) | ||
466 | { | ||
467 | int32_t new_pitch; | ||
468 | int32_t new_stretch; | ||
469 | at_limit = false; | ||
470 | |||
471 | if (pitch_delta < 0) | ||
472 | { | ||
473 | /* for large jumps, snap up to whole numbers */ | ||
474 | if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION && | ||
475 | (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0) | ||
476 | { | ||
477 | pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION); | ||
478 | } | ||
479 | |||
480 | new_pitch = pitch + pitch_delta; | ||
481 | |||
482 | if (new_pitch < PITCH_MIN) | ||
483 | { | ||
484 | if (!allow_cutoff) | ||
485 | { | ||
486 | return pitch; | ||
487 | } | ||
488 | new_pitch = PITCH_MIN; | ||
489 | at_limit = true; | ||
490 | } | ||
491 | } | ||
492 | else if (pitch_delta > 0) | ||
493 | { | ||
494 | /* for large jumps, snap down to whole numbers */ | ||
495 | if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION && | ||
496 | (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0) | ||
497 | { | ||
498 | pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION; | ||
499 | } | ||
500 | |||
501 | new_pitch = pitch + pitch_delta; | ||
502 | |||
503 | if (new_pitch > PITCH_MAX) | ||
504 | { | ||
505 | if (!allow_cutoff) | ||
506 | return pitch; | ||
507 | new_pitch = PITCH_MAX; | ||
508 | at_limit = true; | ||
509 | } | ||
510 | } | ||
511 | else | ||
512 | { | ||
513 | /* pitch_delta == 0 -> no real change */ | ||
514 | return pitch; | ||
515 | } | ||
516 | if (rb->dsp_timestretch_available()) | ||
517 | { | ||
518 | /* increase the multiple to increase precision of this calculation */ | ||
519 | new_stretch = GET_STRETCH(new_pitch, speed); | ||
520 | if(new_stretch < STRETCH_MIN) | ||
521 | { | ||
522 | /* we have to ignore allow_cutoff, because we can't have the */ | ||
523 | /* stretch go higher than STRETCH_MAX */ | ||
524 | new_pitch = GET_PITCH(speed, STRETCH_MIN); | ||
525 | } | ||
526 | else if(new_stretch > STRETCH_MAX) | ||
527 | { | ||
528 | /* we have to ignore allow_cutoff, because we can't have the */ | ||
529 | /* stretch go higher than STRETCH_MAX */ | ||
530 | new_pitch = GET_PITCH(speed, STRETCH_MAX); | ||
531 | } | ||
532 | |||
533 | if(new_stretch >= STRETCH_MAX || | ||
534 | new_stretch <= STRETCH_MIN) | ||
535 | { | ||
536 | at_limit = true; | ||
537 | } | ||
538 | } | ||
539 | |||
540 | rb->sound_set_pitch(new_pitch); | ||
541 | |||
542 | return new_pitch; | ||
543 | } | ||
544 | |||
545 | static int32_t get_semitone_from_pitch(int32_t pitch) | ||
546 | { | ||
547 | int semitone = 0; | ||
548 | int32_t fractional_index = 0; | ||
549 | |||
550 | while(semitone < NUM_SEMITONES - 1 && | ||
551 | pitch >= semitone_table[semitone + 1]) | ||
552 | { | ||
553 | semitone++; | ||
554 | } | ||
555 | |||
556 | |||
557 | /* now find the fractional part */ | ||
558 | while(pitch > (cent_interp[fractional_index + 1] * | ||
559 | semitone_table[semitone] / PITCH_SPEED_100)) | ||
560 | { | ||
561 | /* Check to make sure fractional_index isn't too big */ | ||
562 | /* This should never happen. */ | ||
563 | if(fractional_index >= CENT_INTERP_NUM - 1) | ||
564 | { | ||
565 | break; | ||
566 | } | ||
567 | fractional_index++; | ||
568 | } | ||
569 | |||
570 | int32_t semitone_pitch_a = cent_interp[fractional_index] * | ||
571 | semitone_table[semitone] / | ||
572 | PITCH_SPEED_100; | ||
573 | int32_t semitone_pitch_b = cent_interp[fractional_index + 1] * | ||
574 | semitone_table[semitone] / | ||
575 | PITCH_SPEED_100; | ||
576 | /* this will be the integer offset from the cent_interp entry */ | ||
577 | int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL / | ||
578 | (semitone_pitch_b - semitone_pitch_a); | ||
579 | semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION + | ||
580 | fractional_index * CENT_INTERP_INTERVAL + | ||
581 | semitone_frac_ofs; | ||
582 | |||
583 | return semitone; | ||
584 | } | ||
585 | |||
586 | static int32_t get_pitch_from_semitone(int32_t semitone) | ||
587 | { | ||
588 | int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION; | ||
589 | |||
590 | /* Find the index into the semitone table */ | ||
591 | int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION); | ||
592 | |||
593 | /* set pitch to the semitone's integer part value */ | ||
594 | int32_t pitch = semitone_table[semitone_index]; | ||
595 | /* get the range of the cent modification for future calculation */ | ||
596 | int32_t pitch_mod_a = | ||
597 | cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) / | ||
598 | CENT_INTERP_INTERVAL]; | ||
599 | int32_t pitch_mod_b = | ||
600 | cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) / | ||
601 | CENT_INTERP_INTERVAL + 1]; | ||
602 | /* figure out the cent mod amount based on the semitone fractional value */ | ||
603 | int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) * | ||
604 | (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL; | ||
605 | |||
606 | /* modify pitch based on the mod amount we just calculated */ | ||
607 | return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100; | ||
608 | } | ||
609 | |||
610 | static int32_t pitch_increase_semitone(int32_t pitch, | ||
611 | int32_t current_semitone, | ||
612 | int32_t semitone_delta | ||
613 | , int32_t speed | ||
614 | ) | ||
615 | { | ||
616 | int32_t new_semitone = current_semitone; | ||
617 | |||
618 | /* snap to the delta interval */ | ||
619 | if(current_semitone % semitone_delta != 0) | ||
620 | { | ||
621 | if(current_semitone > 0 && semitone_delta > 0) | ||
622 | new_semitone += semitone_delta; | ||
623 | else if(current_semitone < 0 && semitone_delta < 0) | ||
624 | new_semitone += semitone_delta; | ||
625 | |||
626 | new_semitone -= new_semitone % semitone_delta; | ||
627 | } | ||
628 | else | ||
629 | new_semitone += semitone_delta; | ||
630 | |||
631 | /* clamp the pitch so it doesn't go beyond the pitch limits */ | ||
632 | if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION)) | ||
633 | { | ||
634 | new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION; | ||
635 | at_limit = true; | ||
636 | } | ||
637 | else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION)) | ||
638 | { | ||
639 | new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION; | ||
640 | at_limit = true; | ||
641 | } | ||
642 | |||
643 | int32_t new_pitch = get_pitch_from_semitone(new_semitone); | ||
644 | |||
645 | int32_t new_stretch = GET_STRETCH(new_pitch, speed); | ||
646 | |||
647 | /* clamp the pitch so it doesn't go beyond the stretch limits */ | ||
648 | if( new_stretch > STRETCH_MAX) | ||
649 | { | ||
650 | new_pitch = GET_PITCH(speed, STRETCH_MAX); | ||
651 | new_semitone = get_semitone_from_pitch(new_pitch); | ||
652 | at_limit = true; | ||
653 | } | ||
654 | else if (new_stretch < STRETCH_MIN) | ||
655 | { | ||
656 | new_pitch = GET_PITCH(speed, STRETCH_MIN); | ||
657 | new_semitone = get_semitone_from_pitch(new_pitch); | ||
658 | at_limit = true; | ||
659 | } | ||
660 | |||
661 | pitch_increase(pitch, new_pitch - pitch, false | ||
662 | , speed | ||
663 | ); | ||
664 | |||
665 | return new_semitone; | ||
666 | } | ||
667 | |||
668 | #ifdef HAVE_TOUCHSCREEN | ||
669 | /* | ||
670 | * Check for touchscreen presses as per sketch above in this file | ||
671 | * | ||
672 | * goes through each row of the, checks whether the touchscreen | ||
673 | * was pressed in it. Then it looks the columns of each row for specific actions | ||
674 | */ | ||
675 | static int pitchscreen_do_touchscreen(struct viewport vps[]) | ||
676 | { | ||
677 | short x, y; | ||
678 | struct viewport *this_vp = &vps[PITCH_TOP]; | ||
679 | int ret; | ||
680 | static bool wait_for_release = false; | ||
681 | ret = rb->action_get_touchscreen_press_in_vp(&x, &y, this_vp); | ||
682 | |||
683 | /* top row */ | ||
684 | if (ret > ACTION_UNKNOWN) | ||
685 | { /* press on top row, left or right column | ||
686 | * only toggle mode if released */ | ||
687 | int column = this_vp->width / 3; | ||
688 | if ((x < column || x > (2*column)) && (ret == BUTTON_REL)) | ||
689 | return ACTION_PS_TOGGLE_MODE; | ||
690 | |||
691 | |||
692 | else if (x >= column && x <= (2*column)) | ||
693 | { /* center column pressed */ | ||
694 | if (ret == BUTTON_REPEAT) | ||
695 | return ACTION_PS_INC_BIG; | ||
696 | else if (ret & BUTTON_REL) | ||
697 | return ACTION_PS_INC_SMALL; | ||
698 | } | ||
699 | return ACTION_NONE; | ||
700 | } | ||
701 | |||
702 | /* now the center row */ | ||
703 | this_vp = &vps[PITCH_MID]; | ||
704 | ret = rb->action_get_touchscreen_press_in_vp(&x, &y, this_vp); | ||
705 | |||
706 | if (ret > ACTION_UNKNOWN) | ||
707 | { | ||
708 | int column = this_vp->width / 3; | ||
709 | |||
710 | if (x < column) | ||
711 | { /* left column */ | ||
712 | if (ret & BUTTON_REL) | ||
713 | { | ||
714 | wait_for_release = false; | ||
715 | return ACTION_PS_NUDGE_LEFTOFF; | ||
716 | } | ||
717 | else if (ret & BUTTON_REPEAT) | ||
718 | return ACTION_PS_SLOWER; | ||
719 | if (!wait_for_release) | ||
720 | { | ||
721 | wait_for_release = true; | ||
722 | return ACTION_PS_NUDGE_LEFT; | ||
723 | } | ||
724 | } | ||
725 | else if (x > (2*column)) | ||
726 | { /* right column */ | ||
727 | if (ret & BUTTON_REL) | ||
728 | { | ||
729 | wait_for_release = false; | ||
730 | return ACTION_PS_NUDGE_RIGHTOFF; | ||
731 | } | ||
732 | else if (ret & BUTTON_REPEAT) | ||
733 | return ACTION_PS_FASTER; | ||
734 | if (!wait_for_release) | ||
735 | { | ||
736 | wait_for_release = true; | ||
737 | return ACTION_PS_NUDGE_RIGHT; | ||
738 | } | ||
739 | } | ||
740 | else | ||
741 | /* center column was pressed */ | ||
742 | return ACTION_PS_RESET; | ||
743 | } | ||
744 | |||
745 | /* now the bottom row */ | ||
746 | this_vp = &vps[PITCH_BOTTOM]; | ||
747 | ret = rb->action_get_touchscreen_press_in_vp(&x, &y, this_vp); | ||
748 | |||
749 | if (ret > ACTION_UNKNOWN) | ||
750 | { | ||
751 | int column = this_vp->width / 3; | ||
752 | |||
753 | /* left or right column is exit */ | ||
754 | if ((x < column || x > (2*column)) && (ret == BUTTON_REL)) | ||
755 | return ACTION_PS_EXIT; | ||
756 | else if (x >= column && x <= (2*column)) | ||
757 | { /* center column was pressed */ | ||
758 | if (ret & BUTTON_REPEAT) | ||
759 | return ACTION_PS_DEC_BIG; | ||
760 | else if (ret & BUTTON_REL) | ||
761 | return ACTION_PS_DEC_SMALL; | ||
762 | } | ||
763 | return ACTION_NONE; | ||
764 | } | ||
765 | return ACTION_NONE; | ||
766 | } | ||
767 | |||
768 | #endif | ||
769 | /* | ||
770 | returns: | ||
771 | 0 on exit | ||
772 | 1 if USB was connected | ||
773 | */ | ||
774 | |||
775 | int gui_syncpitchscreen_run(void) | ||
776 | { | ||
777 | int button; | ||
778 | int32_t pitch = rb->sound_get_pitch(); | ||
779 | int32_t semitone; | ||
780 | |||
781 | int32_t new_pitch; | ||
782 | int32_t pitch_delta; | ||
783 | bool nudged = false; | ||
784 | int i, updated = 4, decimals = 0; | ||
785 | bool exit = false; | ||
786 | /* should maybe be passed per parameter later, not needed for now */ | ||
787 | struct viewport parent[NB_SCREENS]; | ||
788 | struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT]; | ||
789 | int max_lines[NB_SCREENS]; | ||
790 | |||
791 | //push_current_activity(ACTIVITY_PITCHSCREEN); | ||
792 | |||
793 | int32_t new_speed = 0, new_stretch; | ||
794 | |||
795 | /* the speed variable holds the apparent speed of the playback */ | ||
796 | int32_t speed; | ||
797 | if (rb->dsp_timestretch_available()) | ||
798 | { | ||
799 | speed = GET_SPEED(pitch, rb->dsp_get_timestretch()); | ||
800 | } | ||
801 | else | ||
802 | { | ||
803 | speed = pitch; | ||
804 | } | ||
805 | |||
806 | |||
807 | |||
808 | /* Count decimals for speaking */ | ||
809 | for (i = PITCH_SPEED_PRECISION; i >= 10; i /= 10) | ||
810 | decimals++; | ||
811 | |||
812 | /* set the semitone index based on the current pitch */ | ||
813 | semitone = get_semitone_from_pitch(pitch); | ||
814 | |||
815 | /* initialize pitchscreen vps */ | ||
816 | FOR_NB_SCREENS(i) | ||
817 | { | ||
818 | rb->viewport_set_defaults(&parent[i], i); | ||
819 | max_lines[i] = viewport_get_nb_lines(&parent[i]); | ||
820 | pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]); | ||
821 | rb->screens[i]->set_viewport(&parent[i]); | ||
822 | rb->screens[i]->clear_viewport(); | ||
823 | |||
824 | /* also, draw the icons now, it's only needed once */ | ||
825 | pitchscreen_draw_icons(rb->screens[i], &parent[i]); | ||
826 | } | ||
827 | |||
828 | |||
829 | while (!exit) | ||
830 | { | ||
831 | FOR_NB_SCREENS(i) | ||
832 | pitchscreen_draw(rb->screens[i], max_lines[i], | ||
833 | pitch_viewports[i], pitch, semitone | ||
834 | , speed | ||
835 | ); | ||
836 | pitch_delta = 0; | ||
837 | new_speed = 0; | ||
838 | |||
839 | if (rb->global_settings->talk_menu && updated) | ||
840 | { | ||
841 | rb->talk_shutup(); | ||
842 | switch (updated) | ||
843 | { | ||
844 | case 1: | ||
845 | if (rb->global_settings->pitch_mode_semitone) | ||
846 | rb->talk_value_decimal(semitone, UNIT_SIGNED, decimals, false); | ||
847 | else | ||
848 | rb->talk_value_decimal(pitch, UNIT_PERCENT, decimals, false); | ||
849 | break; | ||
850 | case 2: | ||
851 | rb->talk_value_decimal(speed, UNIT_PERCENT, decimals, false); | ||
852 | break; | ||
853 | case 3: | ||
854 | speak_pitch_mode(false); | ||
855 | break; | ||
856 | case 4: | ||
857 | if (rb->global_settings->pitch_mode_timestretch && rb->dsp_timestretch_available()) | ||
858 | rb->talk_id(LANG_PITCH, false); | ||
859 | else | ||
860 | rb->talk_id(LANG_PLAYBACK_RATE, false); | ||
861 | rb->talk_value_decimal(pitch, UNIT_PERCENT, decimals, true); | ||
862 | if (rb->global_settings->pitch_mode_timestretch && rb->dsp_timestretch_available()) | ||
863 | { | ||
864 | rb->talk_id(LANG_SPEED, true); | ||
865 | rb->talk_value_decimal(speed, UNIT_PERCENT, decimals, true); | ||
866 | } | ||
867 | speak_pitch_mode(true); | ||
868 | break; | ||
869 | default: | ||
870 | break; | ||
871 | } | ||
872 | } | ||
873 | updated = 0; | ||
874 | |||
875 | button = rb->get_action(CONTEXT_PITCHSCREEN, HZ); | ||
876 | |||
877 | #ifdef HAVE_TOUCHSCREEN | ||
878 | if (button == ACTION_TOUCHSCREEN) | ||
879 | { | ||
880 | FOR_NB_SCREENS(i) | ||
881 | button = pitchscreen_do_touchscreen(pitch_viewports[i]); | ||
882 | } | ||
883 | #endif | ||
884 | switch (button) | ||
885 | { | ||
886 | case ACTION_PS_INC_SMALL: | ||
887 | if(rb->global_settings->pitch_mode_semitone) | ||
888 | pitch_delta = SEMITONE_SMALL_DELTA; | ||
889 | else | ||
890 | pitch_delta = PITCH_SMALL_DELTA; | ||
891 | updated = 1; | ||
892 | break; | ||
893 | |||
894 | case ACTION_PS_INC_BIG: | ||
895 | if(rb->global_settings->pitch_mode_semitone) | ||
896 | pitch_delta = SEMITONE_BIG_DELTA; | ||
897 | else | ||
898 | pitch_delta = PITCH_BIG_DELTA; | ||
899 | updated = 1; | ||
900 | break; | ||
901 | |||
902 | case ACTION_PS_DEC_SMALL: | ||
903 | if(rb->global_settings->pitch_mode_semitone) | ||
904 | pitch_delta = -SEMITONE_SMALL_DELTA; | ||
905 | else | ||
906 | pitch_delta = -PITCH_SMALL_DELTA; | ||
907 | updated = 1; | ||
908 | break; | ||
909 | |||
910 | case ACTION_PS_DEC_BIG: | ||
911 | if(rb->global_settings->pitch_mode_semitone) | ||
912 | pitch_delta = -SEMITONE_BIG_DELTA; | ||
913 | else | ||
914 | pitch_delta = -PITCH_BIG_DELTA; | ||
915 | updated = 1; | ||
916 | break; | ||
917 | |||
918 | case ACTION_PS_NUDGE_RIGHT: | ||
919 | if (!rb->global_settings->pitch_mode_timestretch) | ||
920 | { | ||
921 | new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false | ||
922 | , speed | ||
923 | ); | ||
924 | nudged = (new_pitch != pitch); | ||
925 | pitch = new_pitch; | ||
926 | semitone = get_semitone_from_pitch(pitch); | ||
927 | speed = pitch; | ||
928 | updated = nudged ? 1 : 0; | ||
929 | break; | ||
930 | } | ||
931 | else | ||
932 | { | ||
933 | new_speed = speed + SPEED_SMALL_DELTA; | ||
934 | at_limit = false; | ||
935 | updated = 2; | ||
936 | } | ||
937 | break; | ||
938 | |||
939 | case ACTION_PS_FASTER: | ||
940 | if (rb->global_settings->pitch_mode_timestretch) | ||
941 | { | ||
942 | new_speed = speed + SPEED_BIG_DELTA; | ||
943 | /* snap to whole numbers */ | ||
944 | if(new_speed % PITCH_SPEED_PRECISION != 0) | ||
945 | new_speed -= new_speed % PITCH_SPEED_PRECISION; | ||
946 | at_limit = false; | ||
947 | updated = 2; | ||
948 | } | ||
949 | break; | ||
950 | |||
951 | case ACTION_PS_NUDGE_RIGHTOFF: | ||
952 | if (nudged) | ||
953 | { | ||
954 | pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false | ||
955 | , speed | ||
956 | ); | ||
957 | speed = pitch; | ||
958 | semitone = get_semitone_from_pitch(pitch); | ||
959 | nudged = false; | ||
960 | updated = 1; | ||
961 | } | ||
962 | break; | ||
963 | |||
964 | case ACTION_PS_NUDGE_LEFT: | ||
965 | if (!rb->global_settings->pitch_mode_timestretch) | ||
966 | { | ||
967 | new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false | ||
968 | , speed | ||
969 | ); | ||
970 | nudged = (new_pitch != pitch); | ||
971 | pitch = new_pitch; | ||
972 | semitone = get_semitone_from_pitch(pitch); | ||
973 | speed = pitch; | ||
974 | updated = nudged ? 1 : 0; | ||
975 | break; | ||
976 | } | ||
977 | else | ||
978 | { | ||
979 | new_speed = speed - SPEED_SMALL_DELTA; | ||
980 | at_limit = false; | ||
981 | updated = 2; | ||
982 | } | ||
983 | break; | ||
984 | |||
985 | case ACTION_PS_SLOWER: | ||
986 | if (rb->global_settings->pitch_mode_timestretch) | ||
987 | { | ||
988 | new_speed = speed - SPEED_BIG_DELTA; | ||
989 | /* snap to whole numbers */ | ||
990 | if(new_speed % PITCH_SPEED_PRECISION != 0) | ||
991 | new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION; | ||
992 | at_limit = false; | ||
993 | updated = 2; | ||
994 | } | ||
995 | break; | ||
996 | |||
997 | case ACTION_PS_NUDGE_LEFTOFF: | ||
998 | if (nudged) | ||
999 | { | ||
1000 | pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false | ||
1001 | , speed | ||
1002 | ); | ||
1003 | speed = pitch; | ||
1004 | semitone = get_semitone_from_pitch(pitch); | ||
1005 | nudged = false; | ||
1006 | updated = 1; | ||
1007 | } | ||
1008 | break; | ||
1009 | |||
1010 | case ACTION_PS_RESET: | ||
1011 | pitch = PITCH_SPEED_100; | ||
1012 | rb->sound_set_pitch(pitch); | ||
1013 | speed = PITCH_SPEED_100; | ||
1014 | if (rb->dsp_timestretch_available()) | ||
1015 | { | ||
1016 | rb->dsp_set_timestretch(PITCH_SPEED_100); | ||
1017 | at_limit = false; | ||
1018 | } | ||
1019 | semitone = get_semitone_from_pitch(pitch); | ||
1020 | updated = 4; | ||
1021 | break; | ||
1022 | |||
1023 | case ACTION_PS_TOGGLE_MODE: | ||
1024 | rb->global_settings->pitch_mode_semitone = !rb->global_settings->pitch_mode_semitone; | ||
1025 | |||
1026 | if (rb->dsp_timestretch_available() && !rb->global_settings->pitch_mode_semitone) | ||
1027 | { | ||
1028 | rb->global_settings->pitch_mode_timestretch = !rb->global_settings->pitch_mode_timestretch; | ||
1029 | if(!rb->global_settings->pitch_mode_timestretch) | ||
1030 | { | ||
1031 | /* no longer in timestretch mode. Reset speed */ | ||
1032 | speed = pitch; | ||
1033 | rb->dsp_set_timestretch(PITCH_SPEED_100); | ||
1034 | } | ||
1035 | } | ||
1036 | rb->settings_save(); | ||
1037 | updated = 3; | ||
1038 | break; | ||
1039 | |||
1040 | case ACTION_PS_EXIT: | ||
1041 | exit = true; | ||
1042 | break; | ||
1043 | |||
1044 | default: | ||
1045 | if (rb->default_event_handler(button) == SYS_USB_CONNECTED) | ||
1046 | return 1; | ||
1047 | break; | ||
1048 | } | ||
1049 | if (pitch_delta) | ||
1050 | { | ||
1051 | if (rb->global_settings->pitch_mode_semitone) | ||
1052 | { | ||
1053 | semitone = pitch_increase_semitone(pitch, semitone, pitch_delta | ||
1054 | , speed | ||
1055 | ); | ||
1056 | pitch = get_pitch_from_semitone(semitone); | ||
1057 | } | ||
1058 | else | ||
1059 | { | ||
1060 | pitch = pitch_increase(pitch, pitch_delta, true | ||
1061 | , speed | ||
1062 | ); | ||
1063 | semitone = get_semitone_from_pitch(pitch); | ||
1064 | } | ||
1065 | if (rb->global_settings->pitch_mode_timestretch) | ||
1066 | { | ||
1067 | /* do this to make sure we properly obey the stretch limits */ | ||
1068 | new_speed = speed; | ||
1069 | } | ||
1070 | else | ||
1071 | { | ||
1072 | speed = pitch; | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | if(new_speed) | ||
1077 | { | ||
1078 | new_stretch = GET_STRETCH(pitch, new_speed); | ||
1079 | |||
1080 | /* limit the amount of stretch */ | ||
1081 | if(new_stretch > STRETCH_MAX) | ||
1082 | { | ||
1083 | new_stretch = STRETCH_MAX; | ||
1084 | new_speed = GET_SPEED(pitch, new_stretch); | ||
1085 | } | ||
1086 | else if(new_stretch < STRETCH_MIN) | ||
1087 | { | ||
1088 | new_stretch = STRETCH_MIN; | ||
1089 | new_speed = GET_SPEED(pitch, new_stretch); | ||
1090 | } | ||
1091 | |||
1092 | new_stretch = GET_STRETCH(pitch, new_speed); | ||
1093 | if(new_stretch >= STRETCH_MAX || | ||
1094 | new_stretch <= STRETCH_MIN) | ||
1095 | { | ||
1096 | at_limit = true; | ||
1097 | } | ||
1098 | |||
1099 | /* set the amount of stretch */ | ||
1100 | rb->dsp_set_timestretch(new_stretch); | ||
1101 | |||
1102 | /* update the speed variable with the new speed */ | ||
1103 | speed = new_speed; | ||
1104 | |||
1105 | /* Reset new_speed so we only call dsp_set_timestretch */ | ||
1106 | /* when needed */ | ||
1107 | new_speed = 0; | ||
1108 | } | ||
1109 | } | ||
1110 | |||
1111 | //rb->pcmbuf_set_low_latency(false); | ||
1112 | //pop_current_activity(); | ||
1113 | |||
1114 | /* Clean up */ | ||
1115 | FOR_NB_SCREENS(i) | ||
1116 | { | ||
1117 | rb->screens[i]->set_viewport(NULL); | ||
1118 | } | ||
1119 | |||
1120 | return 0; | ||
1121 | } | ||
1122 | |||
1123 | static int arg_callback(char argchar, const char **parameter) | ||
1124 | { | ||
1125 | int ret; | ||
1126 | long num, dec; | ||
1127 | bool bret; | ||
1128 | //rb->splashf(100, "Arg: %c", argchar); | ||
1129 | while (*parameter[0] > '/' && ispunct(*parameter[0])) (*parameter)++; | ||
1130 | switch (tolower(argchar)) | ||
1131 | { | ||
1132 | case 'q' : | ||
1133 | pitch_vars.flags &= ~PVAR_VERBOSE; | ||
1134 | break; | ||
1135 | case 'g' : | ||
1136 | pitch_vars.flags |= PVAR_GUI; | ||
1137 | break; | ||
1138 | case 'p' : | ||
1139 | ret = longnum_parse(parameter, &num, &dec); | ||
1140 | if (ret) | ||
1141 | { | ||
1142 | dec /= (ARGPARSE_FRAC_DEC_MULTIPLIER / PITCH_SPEED_PRECISION); | ||
1143 | if (num < 0) | ||
1144 | dec = -dec; | ||
1145 | pitch_vars.pitch = (num * PITCH_SPEED_PRECISION + (dec % PITCH_SPEED_PRECISION)); | ||
1146 | } | ||
1147 | break; | ||
1148 | case 'k' : | ||
1149 | ret = bool_parse(parameter, &bret); | ||
1150 | if (ret) | ||
1151 | { | ||
1152 | if(!bret && rb->dsp_timestretch_available()) | ||
1153 | { | ||
1154 | /* no longer in timestretch mode. Reset speed */ | ||
1155 | rb->dsp_set_timestretch(PITCH_SPEED_100); | ||
1156 | } | ||
1157 | rb->dsp_timestretch_enable(bret); | ||
1158 | if ((pitch_vars.flags & PVAR_VERBOSE) == PVAR_VERBOSE) | ||
1159 | rb->splashf(HZ, "Timestretch: %s", bret ? "true" : "false"); | ||
1160 | int n = 10; /* 1 second */ | ||
1161 | while (bret && n-- > 0 && !rb->dsp_timestretch_available()) | ||
1162 | { | ||
1163 | rb->sleep(HZ / 10); | ||
1164 | } | ||
1165 | } | ||
1166 | break; | ||
1167 | case 's' : | ||
1168 | ret = longnum_parse(parameter, &num, &dec); | ||
1169 | if (ret && rb->dsp_timestretch_available()) | ||
1170 | { | ||
1171 | dec /= (ARGPARSE_FRAC_DEC_MULTIPLIER / PITCH_SPEED_PRECISION); | ||
1172 | if (num < 0) | ||
1173 | break; | ||
1174 | pitch_vars.speed = (num * PITCH_SPEED_PRECISION + (dec % PITCH_SPEED_PRECISION)); | ||
1175 | |||
1176 | } | ||
1177 | break; | ||
1178 | default : | ||
1179 | rb->splashf(HZ, "Unknown switch '%c'",argchar); | ||
1180 | //return 0; | ||
1181 | } | ||
1182 | |||
1183 | return 1; | ||
1184 | } | ||
1185 | |||
1186 | void fill_pitchvars(struct pvars *pv) | ||
1187 | { | ||
1188 | if (!pv) | ||
1189 | return; | ||
1190 | pv->pitch = rb->sound_get_pitch(); | ||
1191 | |||
1192 | /* the speed variable holds the apparent speed of the playback */ | ||
1193 | if (rb->dsp_timestretch_available()) | ||
1194 | { | ||
1195 | pv->speed = GET_SPEED(pv->pitch, rb->dsp_get_timestretch()); | ||
1196 | } | ||
1197 | else | ||
1198 | { | ||
1199 | pv->speed = pv->pitch; | ||
1200 | } | ||
1201 | |||
1202 | pv->stretch = GET_STRETCH(pv->pitch, pv->speed); | ||
1203 | pv->flags |= PVAR_VERBOSE; | ||
1204 | |||
1205 | } | ||
1206 | /* plugin entry point */ | ||
1207 | enum plugin_status plugin_start(const void* parameter) | ||
1208 | { | ||
1209 | /* pitch_screen | ||
1210 | * accepts args -q, -g, -p=, -s=, -k=; (= sign is optional) | ||
1211 | * -q silences output splash | ||
1212 | * -g runs the gui (DEFAULT) | ||
1213 | * -p100 would set pitch to 100% | ||
1214 | * -s=90 sets speed to 90% if timestrech is enabled | ||
1215 | * -k=true -k1 enables time stretch -k0 -kf-kn disables | ||
1216 | */ | ||
1217 | bool gui = false; | ||
1218 | rb->pcmbuf_set_low_latency(true); | ||
1219 | |||
1220 | /* Figure out whether to be in timestretch mode */ | ||
1221 | if (parameter == NULL) /* gui mode */ | ||
1222 | { | ||
1223 | if (rb->global_settings->pitch_mode_timestretch && !rb->dsp_timestretch_available()) | ||
1224 | { | ||
1225 | rb->global_settings->pitch_mode_timestretch = false; | ||
1226 | rb->settings_save(); | ||
1227 | } | ||
1228 | gui = true; | ||
1229 | } | ||
1230 | else | ||
1231 | { | ||
1232 | struct pvars cur; | ||
1233 | fill_pitchvars(&cur); | ||
1234 | fill_pitchvars(&pitch_vars); | ||
1235 | argparse((const char*) parameter, -1, &arg_callback); | ||
1236 | if (pitch_vars.pitch != cur.pitch) | ||
1237 | { | ||
1238 | rb->sound_set_pitch(pitch_vars.pitch); | ||
1239 | pitch_vars.pitch = rb->sound_get_pitch(); | ||
1240 | if ((pitch_vars.flags & PVAR_VERBOSE) == PVAR_VERBOSE) | ||
1241 | rb->splashf(HZ, "pitch: %ld.%02ld%%", | ||
1242 | pitch_vars.pitch / PITCH_SPEED_PRECISION, | ||
1243 | pitch_vars.pitch % PITCH_SPEED_PRECISION); | ||
1244 | } | ||
1245 | if (pitch_vars.speed != cur.speed) | ||
1246 | { | ||
1247 | pitch_vars.stretch = GET_STRETCH(pitch_vars.pitch, pitch_vars.speed); | ||
1248 | |||
1249 | /* limit the amount of stretch */ | ||
1250 | if(pitch_vars.stretch > STRETCH_MAX) | ||
1251 | { | ||
1252 | pitch_vars.stretch = STRETCH_MAX; | ||
1253 | pitch_vars.speed = GET_SPEED(pitch_vars.pitch, pitch_vars.stretch); | ||
1254 | } | ||
1255 | else if(pitch_vars.stretch < STRETCH_MIN) | ||
1256 | { | ||
1257 | pitch_vars.stretch = STRETCH_MIN; | ||
1258 | pitch_vars.speed = GET_SPEED(pitch_vars.pitch, pitch_vars.stretch); | ||
1259 | } | ||
1260 | |||
1261 | pitch_vars.stretch = GET_STRETCH(pitch_vars.pitch, pitch_vars.speed); | ||
1262 | if ((pitch_vars.flags & PVAR_VERBOSE) == PVAR_VERBOSE) | ||
1263 | rb->splashf(HZ, "speed: %ld.%02ld%%", | ||
1264 | pitch_vars.speed / PITCH_SPEED_PRECISION, | ||
1265 | pitch_vars.speed % PITCH_SPEED_PRECISION); | ||
1266 | /* set the amount of stretch */ | ||
1267 | rb->dsp_set_timestretch(pitch_vars.stretch); | ||
1268 | } | ||
1269 | gui = ((pitch_vars.flags & PVAR_GUI) == PVAR_GUI); | ||
1270 | if ((pitch_vars.flags & PVAR_VERBOSE) == PVAR_VERBOSE) | ||
1271 | rb->splashf(HZ, "GUI: %d", gui); | ||
1272 | |||
1273 | } | ||
1274 | |||
1275 | if (gui && gui_syncpitchscreen_run() == 1) | ||
1276 | return PLUGIN_USB_CONNECTED; | ||
1277 | rb->pcmbuf_set_low_latency(false); | ||
1278 | return PLUGIN_OK; | ||
1279 | } | ||