A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 543 lines 17 kB view raw
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}