A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Zakk Roberts
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "plugin.h"
22#include "lib/helper.h"
23#include "lib/playback_control.h"
24#include "lib/pluginlib_actions.h"
25
26/* this set the context to use with PLA */
27static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
28
29/***
30 * FIREWORKS.C by ZAKK ROBERTS
31 * Rockbox plugin simulating a fireworks display.
32 * Supports all bitmap LCDs, fully scalable.
33 * Currently disabled for Archos Recorder - runs too slow.
34 ***/
35
36/* We use PLA */
37#define BTN_EXIT PLA_EXIT
38#define BTN_FIRE PLA_SELECT
39#define BTN_FIRE_REPEAT PLA_SELECT_REPEAT
40
41#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
42 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
43 || (CONFIG_KEYPAD == IPOD_4G_PAD)
44#define BTN_MENU PLA_UP
45#else
46#define BTN_MENU PLA_CANCEL
47#endif
48
49/* The lowdown on source terminology:
50 * a ROCKET is launched from the LCD bottom.
51 * FIREWORKs are ejected from the rocket when it explodes. */
52
53#define MAX_ROCKETS 40
54#define ROCKET_LIFE (LCD_HEIGHT/2)
55#define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
56#define ROCKET_SIZE 2
57#define ROCKET_MOVEMENT_RANGE 4
58#define ROCKET_TRAIL_PARTICLES 50
59
60#define MAX_FIREWORKS 40
61#define FIREWORK_MOVEMENT_RANGE 6
62#define FIREWORK_SIZE 2
63
64/* position, speed, "phase" (age), color of all fireworks */
65int firework_xpoints[MAX_ROCKETS][MAX_FIREWORKS];
66int firework_ypoints[MAX_ROCKETS][MAX_FIREWORKS];
67int firework_xspeed[MAX_ROCKETS][MAX_FIREWORKS];
68int firework_yspeed[MAX_ROCKETS][MAX_FIREWORKS];
69int firework_phase[MAX_ROCKETS];
70#ifdef HAVE_LCD_COLOR
71int firework_color[MAX_ROCKETS][MAX_FIREWORKS];
72#endif
73
74/* position, speed, "phase" (age) of all rockets */
75int rocket_xpos[MAX_ROCKETS];
76int rocket_ypos[MAX_ROCKETS];
77int rocket_xspeed[MAX_ROCKETS];
78int rocket_yspeed[MAX_ROCKETS];
79int rocket_phase[MAX_ROCKETS];
80int rocket_targetphase[MAX_ROCKETS];
81
82/* settings values. these should eventually be saved to
83 * disk. maybe a preset loading/saving system? */
84int autofire_delay = 0;
85int particles_per_firework = 2;
86int particle_life = 1;
87int gravity = 1;
88int show_rockets = 1;
89int frames_per_second = 4;
90bool quit_plugin = false;
91
92/* firework colors:
93 * firework_colors = brightest firework color, used most of the time.
94 * DARK colors = fireworks are nearly burnt out.
95 * DARKER colors = fireworks are several frames away from burning out.
96 * DARKEST colors = fireworks are a couple frames from burning out. */
97#ifdef HAVE_LCD_COLOR
98static const unsigned firework_colors[] = {
99LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
100LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
101LCD_RGBPACK(151,84,213) };
102
103static const unsigned firework_dark_colors[] = {
104LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
105LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
106LCD_RGBPACK(75,42,105) };
107
108static const unsigned firework_darker_colors[] = {
109LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
110LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
111LCD_RGBPACK(38,21,52) };
112
113static const unsigned firework_darkest_colors[] = {
114LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
115LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
116LCD_RGBPACK(19,10,26) };
117
118#define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
119
120#endif
121
122static const struct opt_items autofire_delay_settings[15] = {
123 { STR(LANG_OFF) },
124 { "50ms", TALK_ID(50, UNIT_MS) },
125 { "100ms", TALK_ID(100, UNIT_MS) },
126 { "200ms", TALK_ID(200, UNIT_MS) },
127 { "300ms", TALK_ID(300, UNIT_MS) },
128 { "400ms", TALK_ID(400, UNIT_MS) },
129 { "500ms", TALK_ID(500, UNIT_MS) },
130 { "600ms", TALK_ID(600, UNIT_MS) },
131 { "700ms", TALK_ID(700, UNIT_MS) },
132 { "800ms", TALK_ID(800, UNIT_MS) },
133 { "900ms", TALK_ID(900, UNIT_MS) },
134 { "1s", TALK_ID(1, UNIT_SEC) },
135 { "2s", TALK_ID(2, UNIT_SEC) },
136 { "3s", TALK_ID(3, UNIT_SEC) },
137 { "4s", TALK_ID(4, UNIT_SEC) }
138};
139
140int autofire_delay_values[15] = {
141 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
142
143static const struct opt_items particle_settings[8] = {
144 { "5", TALK_ID(5, UNIT_INT) },
145 { "10", TALK_ID(10, UNIT_INT) },
146 { "15", TALK_ID(15, UNIT_INT) },
147 { "20", TALK_ID(20, UNIT_INT) },
148 { "25", TALK_ID(25, UNIT_INT) },
149 { "30", TALK_ID(30, UNIT_INT) },
150 { "35", TALK_ID(35, UNIT_INT) },
151 { "40", TALK_ID(40, UNIT_INT) },
152};
153
154int particle_values[8] = {
155 5, 10, 15, 20, 25, 30, 35, 40 };
156
157static const struct opt_items particle_life_settings[9] = {
158 { "20 cycles", -1 },
159 { "30 cycles", -1 },
160 { "40 cycles", -1 },
161 { "50 cycles", -1 },
162 { "60 cycles", -1 },
163 { "70 cycles", -1 },
164 { "80 cycles", -1 },
165 { "90 cycles", -1 },
166 { "100 cycles", -1 }
167};
168
169int particle_life_values[9] = {
170 20, 30, 40, 50, 60, 70, 80, 90, 100 };
171
172static const struct opt_items gravity_settings[4] = {
173 { STR(LANG_OFF) },
174 { "Weak", -1 },
175 { "Moderate", -1 },
176 { "Strong", -1 },
177};
178
179int gravity_values[4] = {
180 0, 30, 20, 10 };
181
182#ifdef HAVE_LCD_COLOR
183
184static const struct opt_items rocket_settings[3] = {
185 { "No", -1 },
186 { "Yes (no trails)", -1 },
187 { "Yes (with trails)", -1 },
188};
189int rocket_values[4] = {
190 2, 1, 0 };
191
192#else
193
194static const struct opt_items rocket_settings[2] = {
195 { STR(LANG_SET_BOOL_NO) },
196 { STR(LANG_SET_BOOL_YES) },
197};
198int rocket_values[4] = {
199 1, 0 };
200
201#endif
202
203static const struct opt_items fps_settings[9] = {
204 { "20 FPS", -1 },
205 { "25 FPS", -1 },
206 { "30 FPS", -1 },
207 { "35 FPS", -1 },
208 { "40 FPS", -1 },
209 { "45 FPS", -1 },
210 { "50 FPS", -1 },
211 { "55 FPS", -1 },
212 { "60 FPS", -1 }
213};
214
215int fps_values[9] = {
216 20, 25, 30, 35, 40, 45, 50, 55, 60 };
217
218MENUITEM_STRINGLIST(menu, "Fireworks Menu", NULL,
219 "Start Demo", "Auto-Fire", "Particles Per Firework",
220 "Particle Life", "Gravity", "Show Rockets",
221 "FPS (Speed)", "Playback Control", "Quit");
222
223/* called on startup. initializes all variables, etc */
224static void init_all(void)
225{
226 int j;
227
228 for(j=0; j<MAX_ROCKETS; j++)
229 {
230 rocket_phase[j] = -1;
231 firework_phase[j] = -1;
232 }
233}
234
235/* called when a rocket hits its destination height.
236 * prepares all associated fireworks to be expelled. */
237static void init_explode(int x, int y, int firework, int points)
238{
239 int i;
240
241 for(i=0; i<points; i++)
242 {
243 rb->srand(*rb->current_tick * i);
244
245 firework_xpoints[firework][i] = x;
246 firework_ypoints[firework][i] = y;
247
248 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE)
249 - FIREWORK_MOVEMENT_RANGE/2;
250 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE)
251 - FIREWORK_MOVEMENT_RANGE/2;
252
253#ifdef HAVE_LCD_COLOR
254 firework_color[firework][i] = rb->rand() % 7;
255#endif
256 }
257}
258
259/* called when a rocket is launched.
260 * prepares said rocket to start moving towards its destination. */
261static void init_rocket(int rocket)
262{
263 rb->srand(*rb->current_tick);
264
265 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
266 rocket_ypos[rocket] = LCD_HEIGHT;
267
268 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE)
269 - ROCKET_MOVEMENT_RANGE/2;
270 rocket_yspeed[rocket] = 3;
271
272 rocket_phase[rocket] = 0;
273 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR))
274 / rocket_yspeed[rocket];
275}
276
277/* startup/configuration menu. */
278static void fireworks_menu(void)
279{
280 int selected = 0, result;
281 bool menu_quit = false;
282
283 rb->lcd_setfont(FONT_UI);
284#ifdef HAVE_LCD_COLOR
285 rb->lcd_set_background(LCD_BLACK);
286 rb->lcd_set_foreground(LCD_WHITE);
287#endif
288 rb->lcd_clear_display();
289 rb->lcd_update();
290
291 rb->button_clear_queue();
292
293 while(!menu_quit)
294 {
295 result = rb->do_menu(&menu, &selected, NULL, false);
296
297 switch(result)
298 {
299 case 0:
300 rb->lcd_setfont(FONT_SYSFIXED);
301
302#ifdef HAVE_LCD_COLOR
303 rb->lcd_set_background(LCD_BLACK);
304 rb->lcd_set_foreground(LCD_WHITE);
305#endif
306
307 rb->lcd_clear_display();
308 rb->lcd_update();
309
310 init_all();
311 menu_quit = true;
312 break;
313
314 case 1:
315 rb->set_option("Auto-Fire", &autofire_delay, RB_INT,
316 autofire_delay_settings, 15, NULL);
317 break;
318
319 case 2:
320 rb->set_option("Particles Per Firework", &particles_per_firework,
321 RB_INT, particle_settings, 8, NULL);
322 break;
323
324 case 3:
325 rb->set_option("Particle Life", &particle_life, RB_INT,
326 particle_life_settings, 9, NULL);
327 break;
328
329 case 4:
330 rb->set_option("Gravity", &gravity, RB_INT,
331 gravity_settings, 4, NULL);
332 break;
333
334 case 5:
335 rb->set_option("Show Rockets", &show_rockets, RB_INT,
336 rocket_settings, 3, NULL);
337 break;
338
339 case 6:
340 rb->set_option("FPS (Speed)", &frames_per_second, RB_INT,
341 fps_settings, 9, NULL);
342 break;
343
344 case 7:
345 playback_control(NULL);
346 break;
347
348 case 8:
349 quit_plugin = true;
350 menu_quit = true;
351 break;
352 }
353 }
354}
355
356/* this is the plugin entry point */
357enum plugin_status plugin_start(const void* parameter)
358{
359 (void)parameter;
360
361 int j, i;
362 int thisrocket=0;
363 int start_tick, elapsed_tick;
364 int button;
365
366 /* set everything up.. no BL timeout, no backdrop,
367 white-text-on-black-background. */
368
369 backlight_ignore_timeout();
370
371#if LCD_DEPTH > 1
372 rb->lcd_set_backdrop(NULL);
373 rb->lcd_set_background(LCD_BLACK);
374 rb->lcd_set_foreground(LCD_WHITE);
375#endif
376
377#ifdef HAVE_ADJUSTABLE_CPU_FREQ
378 rb->cpu_boost(true);
379#endif
380
381 fireworks_menu();
382
383 start_tick = *rb->current_tick;
384
385 while(!quit_plugin)
386 {
387 rb->lcd_clear_display();
388
389 /* loop through every possible rocket */
390 for(j=0; j<MAX_ROCKETS; j++)
391 {
392 /* if the current rocket is actually moving/"alive" then go on and
393 * move/update/explode it */
394 if(rocket_phase[j] > -1)
395 {
396#ifdef HAVE_LCD_COLOR /* draw trail, if requested */
397 if(show_rockets==2)
398 {
399 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
400 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
401 ROCKET_SIZE, ROCKET_SIZE);
402 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
403 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j],
404 rocket_ypos[j]+rocket_yspeed[j],
405 ROCKET_SIZE, ROCKET_SIZE);
406 }
407#endif
408
409 /* move rocket */
410 rocket_xpos[j] += rocket_xspeed[j];
411 rocket_ypos[j] -= rocket_yspeed[j];
412
413#ifdef HAVE_LCD_COLOR
414 rb->lcd_set_foreground(LCD_WHITE);
415#endif
416 if(show_rockets==2 || show_rockets==1)
417 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
418 ROCKET_SIZE, ROCKET_SIZE);
419
420 /* if(rocket isn't "there" yet) keep moving
421 * if(rocket IS there) explode it. */
422 if(rocket_phase[j] < rocket_targetphase[j])
423 rocket_phase[j]++;
424 else
425 {
426 rocket_phase[j] = -1;
427
428 firework_phase[j] = 0;
429 init_explode(rocket_xpos[j], rocket_ypos[j], j,
430 particle_values[particles_per_firework]);
431 }
432 }
433
434 /* and now onto the fireworks for this particular rocket... */
435 if(firework_phase[j] > -1)
436 {
437 for(i=0; i<particle_values[particles_per_firework]; i++)
438 {
439 firework_xpoints[j][i] += firework_xspeed[j][i];
440 firework_ypoints[j][i] += firework_yspeed[j][i];
441
442 if(gravity != 0)
443 firework_ypoints[j][i] += firework_phase[j]
444 /gravity_values[gravity];
445
446#ifdef HAVE_LCD_COLOR
447 rb->lcd_set_foreground(
448 firework_darkest_colors[firework_color[j][i]]);
449 rb->lcd_fillrect(firework_xpoints[j][i]-1,
450 firework_ypoints[j][i]-1,
451 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
452
453 int phase_left = particle_life_values[particle_life]
454 - firework_phase[j];
455 if(phase_left > 10)
456 rb->lcd_set_foreground(
457 firework_colors[firework_color[j][i]]);
458 else if(phase_left > 7)
459 rb->lcd_set_foreground(
460 firework_dark_colors[firework_color[j][i]]);
461 else if(phase_left > 3)
462 rb->lcd_set_foreground(
463 firework_darker_colors[firework_color[j][i]]);
464 else
465 rb->lcd_set_foreground(
466 firework_darkest_colors[firework_color[j][i]]);
467#endif
468 rb->lcd_fillrect(firework_xpoints[j][i],
469 firework_ypoints[j][i],
470 FIREWORK_SIZE, FIREWORK_SIZE);
471/* WIP - currently ugly explosion effect
472#ifdef HAVE_LCD_COLOR
473 if(firework_phase[j] < 10)
474 {
475 rb->lcd_set_foreground(EXPLOSION_COLOR);
476 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
477 rocket_ypos[j]-firework_phase[j],
478 firework_phase[j]*2, firework_phase[j]*2);
479 }
480#endif */
481 }
482
483#ifdef HAVE_LCD_COLOR
484 rb->lcd_set_foreground(LCD_WHITE);
485#endif
486
487 /* firework at its destination age?
488 * no = keep aging; yes = delete it. */
489 if(firework_phase[j] < particle_life_values[particle_life])
490 firework_phase[j]++;
491 else
492 firework_phase[j] = -1;
493 }
494 }
495
496 /* is autofire on? */
497 if(autofire_delay != 0)
498 {
499 elapsed_tick = *rb->current_tick - start_tick;
500
501 if(elapsed_tick > autofire_delay_values[autofire_delay])
502 {
503 init_rocket(thisrocket);
504 if(++thisrocket == MAX_ROCKETS)
505 thisrocket = 0;
506
507 start_tick = *rb->current_tick;
508 }
509 }
510
511 rb->lcd_update();
512
513 button = pluginlib_getaction(HZ/fps_values[frames_per_second], plugin_contexts,
514 ARRAYLEN(plugin_contexts));
515
516 switch(button)
517 {
518 case BTN_EXIT: /* exit directly */
519 quit_plugin = true;
520 break;
521
522 case BTN_MENU: /* back to config menu */
523 fireworks_menu();
524 break;
525
526 case BTN_FIRE: /* fire off rockets manually */
527 case BTN_FIRE_REPEAT:
528 init_rocket(thisrocket);
529 if(++thisrocket == MAX_ROCKETS)
530 thisrocket=0;
531 break;
532 }
533 }
534
535 /* Turn on backlight timeout (revert to settings) */
536 backlight_use_settings();
537
538#ifdef HAVE_ADJUSTABLE_CPU_FREQ
539 rb->cpu_boost(false);
540#endif
541
542 return PLUGIN_OK;
543}