A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 423 lines 15 kB view raw
1--[[ 2 __________ __ ___. 3 Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 \/ \/ \/ \/ \/ 8 $Id$ 9 10 Copyright (C) 2024 William Wilgus 11 This program is free software; you can redistribute it and/or 12 modify it under the terms of the GNU General Public License 13 as published by the Free Software Foundation; either version 2 14 of the License, or (at your option) any later version. 15 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 16 KIND, either express or implied. 17]]-- 18--https://nullprogram.com/blog/2011/06/13/ [Infinite Parallax Starfield] 19 20-- Imports 21local _clr = require("color") -- clrset, clrinc provides device independent colors 22local _lcd = require("lcd") -- lcd helper functions 23local _draw = require("draw") -- draw all the things (primitives) 24local _poly = require("draw_poly") -- vector drawing with tables of coords 25require("actions") 26--'CONSTANTS' (in lua there really is no such thing as all vars are mutable) 27-------------------------------------------------------- 28--colors for fg/bg ------------------------ 29--first number of each quad is fallback for monochrome devices, excluded columns default to 0 30local WHITE = _clr.set(-1, 255, 255, 255) 31local BLACK = _clr.set(0, 0, 0, 0) 32local RED = _clr.set(WHITE, 100) 33local GREEN = _clr.set(WHITE, 0, 100) 34local BGREEN = _clr.set(WHITE, 0, 255) 35local BLUE = _clr.set(WHITE, 0, 0, 255) 36 37local STAR_SEED = 0x811C9DC5; 38local STAR_TILE_SIZE = math.max(rb.LCD_WIDTH, rb.LCD_HEIGHT) * 4; 39local bxor, band, rshift, lshift, arshift = bit.bxor, bit.band, bit.rshift, bit.lshift, bit.arshift 40local random, randomseed = math.random, math.randomseed 41local start_x, start_y, start_z, scale_x, scale_y 42 43-- load users coords from file if it exists 44local fname = rb.PLUGIN_DATA_DIR .. "/stars.pos" 45file = io.open(fname, "r") 46if file then 47 local v = 0 48 for line in file:lines() do 49 v = v + 1 50 if v == 1 then 51 start_x = tonumber(line) or 0 52 elseif v == 2 then 53 start_y = tonumber(line) or 0 54 elseif v == 3 then 55 start_z = tonumber(line) or 0 56 elseif v == 4 then 57 scale_x = tonumber(line) or 1 58 elseif v == 5 then 59 scale_y = tonumber(line) or 1 60 else 61 break; 62 end 63 end 64 io.close( file ) 65end 66 67-- Robert Jenkins' 96 bit Mix Function. 68local function mix (a, b, c) 69 a=a-b; a=a-c; a=bxor(a, (rshift(c, 13))) 70 b=b-c; b=b-a; b=bxor(b, (lshift(a, 8))) 71 c=c-a; c=c-b; c=bxor(c, (rshift(b, 13))) 72 a=a-b; a=a-c; a=bxor(a, (rshift(c, 12))) 73 b=b-c; b=b-a; b=bxor(b, (lshift(a, 16))) 74 c=c-a; c=c-b; c=bxor(c, (rshift(b, 5))) 75 a=a-b; a=a-c; a=bxor(a, (rshift(c, 3))) 76 b=b-c; b=b-a; b=bxor(b, (lshift(a, 10))) 77 c=c-a; c=c-b; c=bxor(c, (rshift(b, 15))) 78 79 return c 80end 81 82-- given 32 bit number returns a table of 8 nibbles (4 bits) 83local function s_bytes_nib(bits, value) 84 -- bits must be multiples of 8 (sizeof byte) 85 local bbuffer = {} 86 local byte 87 local nbytes = bit.rshift(bits, 3) 88 for b = 1, nbytes do 89 if value > 0 then 90 byte = value % 256 91 value = (value - byte) / 256 92 else 93 byte = 0 94 end 95 bbuffer[#bbuffer + 1] = band(byte,0xF) 96 bbuffer[#bbuffer + 1] = band(rshift(byte, 2), 0xF) 97 end 98 return bbuffer 99end 100 101--[[ given table t and total elems desired uses random elems of t 102 and random numbers between 1 and max_v if #t < total_elems]] 103function randomize_table(t, total_elems, max_v) 104 local rand_t = {} 105 local i = 1 106 repeat 107 local v = t[random(i, total_elems)] 108 if v then 109 rand_t[i] = v 110 else 111 rand_t[i] = random(1, max_v) 112 end 113 i = i + 1 114 until i > total_elems 115 return rand_t 116end 117 118local function drawship(img, ship_t) 119 --_poly.polyline(img, x, y, ship_t, color, true, true) 120 _poly.polygon(img, ship_t.x, ship_t.y, ship_t.disp_t, ship_t.color, ship_t.fillcolor, true) 121end 122 123local function draw_astroid(img, x, y, shape, color, fillcolor, scale_x, scale_y) 124 --the random number generator gets seeded with the hash so we get the same figure each time 125 randomseed(shape) 126 local move_x, move_y 127 -- we also use the 4 bytes of the hash as 4 coord pairs and randomly generate 8 more (16) half the size (8) 128 local uniq_t = randomize_table(s_bytes_nib(32, shape), 16, 8) 129 move_x, move_y = _poly.polyline(img, 0, 0, uniq_t, color, true, true, scale_x or 1, scale_y or 1, true) 130 x = x - move_x / 2 131 y = y - move_y / 2 132 if fillcolor then 133 _poly.polygon(img, x, y, uniq_t, color, fillcolor, true, scale_x or 1, scale_y or 1) --filled figures 134 else 135 _poly.polyline(img, x, y, uniq_t, color, true, true, scale_x or 1, scale_y or 1) 136 end 137 138 return x, y, move_x, move_y 139end 140 141local function drawStars(img, drawFn, xoff, yoff, starscale, color, scale_x, scale_y) 142 local size = STAR_TILE_SIZE / starscale 143 local s_x, s_y = scale_x, scale_y 144 local w, h = rb.LCD_WIDTH, rb.LCD_HEIGHT 145 146 -- Top-left tile's top-left position 147 local sx = ((xoff - w/2) / size) * size - size; 148 local sy = ((yoff - h/2) / size) * size - size; 149 150 --Draw each tile currently in view. 151 for i = sx, w + sx + size*3, size do 152 for j = sy, h + sy + size*3, size do 153 local hash = mix(STAR_SEED, (i / size), (j / size)) 154 for n = 0, 2 do 155 local px = (hash % size) + (i - xoff) 156 hash = arshift(hash, 3) 157 158 local py = (hash % size) + (j - yoff) 159 hash = arshift(hash, 3) 160 if px > 0 and px < w and py > 0 and py < h then 161 drawFn(img, px, py, color, n, hash) 162 end 163 end 164 end 165 end 166end 167 168local function update_lcd() 169 rb.lcd_puts(0,0, "[Infinite Starfield]") 170 _lcd:update() 171 rb.sleep(100) 172 update_lcd = _lcd.update 173end 174 175local backlight_on 176local function turn_on_backlight() 177 rb.backlight_force_on(); 178 backlight_on = function() end 179end 180backlight_on = turn_on_backlight 181 182do 183 local act = rb.actions 184 local quit = false 185 --local last_action = 0 186 local x,y,z = start_x or 0, start_y or 0, start_z or 8 187 local s_x, s_y = scale_x or 1, scale_y or 1 188 local ship_t = {x = (rb.LCD_WIDTH - 0xF) / 2, 189 y = rb.LCD_HEIGHT - (rb.LCD_HEIGHT / 3), 190 color = BGREEN, 191 fillcolor = BLACK, 192 -- ship vector coords x,y, x,y,... 193 lt_t = {0,7, 15,0, 9,7, 15,15, 0,7}, 194 rt_t = {0,0, 5,7, 0,15, 15,7, 0,0}, 195 up_t = {0,15, 7,0, 15,15, 7,9, 0,15}, 196 dn_t = {0,0, 7,15, 15,0, 7,5, 0,0} 197 } 198 ship_t.disp_t = ship_t.up_t 199 200 local fast = {x = 1, y = 1, count = 0, inc_x = rb.LCD_WIDTH / 16, inc_y = rb.LCD_HEIGHT / 16} 201 202 local last = {sx = s_x, sy = s_y, dx = 0, dy = 0, inc_x = 0, inc_y = 0} 203 204 local function draw_points(img, x, y, color, n, hash) 205 if s_x > s_y then 206 img:line(x, y, x + s_x, y, color, true) 207 elseif s_y > s_x then 208 img:line(x, y, x, y + s_y, color, true) 209 else 210 img:set(x, y, color, true) 211 end 212 end 213 214 function action_drift() 215 if last.dx > 0 then 216 last.dx = last.dx - 1 217 x = x + last.dx 218 elseif last.dx < 0 then 219 last.dx = last.dx + 1 220 x = x + last.dx 221 end 222 if last.dy > 0 then 223 last.dy = last.dy - 1 224 y = y + last.dy 225 elseif last.dy < 0 then 226 last.dy = last.dy + 1 227 y = y + last.dy 228 end 229 if last.dx == 0 and last.dy == 0 then 230 rockev.suspend("timer") 231 end 232 rockev.trigger("action", true, act.ACTION_REDRAW) 233 end 234 235 function action_event(action) 236 backlight_on() 237 if action == act.PLA_EXIT or action == act.PLA_CANCEL then 238 quit = true 239 start_x, start_y, start_z = x, y, z 240 scale_x, scale_y = last.sx, last.sy 241 elseif action == act.PLA_RIGHT_REPEAT then 242 fast.count = fast.count + 1 243 if fast.count % 10 == 0 then 244 fast.x = fast.x + fast.inc_x 245 if fast.count > 100 then s_x = s_x + 1 end 246 end 247 x = x + fast.x + last.inc_x 248 s_y = last.sy 249 last.dx = fast.x 250 ship_t.disp_t = ship_t.rt_t 251 elseif action == act.PLA_LEFT_REPEAT then 252 fast.count = fast.count + 1 253 if fast.count % 10 == 0 then 254 fast.x = fast.x + fast.inc_x 255 if fast.count > 100 then s_x = s_x + 1 end 256 end 257 x = x - fast.x + last.inc_x 258 s_y = last.sy 259 last.dx = -fast.x 260 ship_t.disp_t = ship_t.lt_t 261 elseif action == act.PLA_UP_REPEAT then 262 fast.count = fast.count + 1 263 if fast.count % 10 == 0 then 264 fast.y = fast.y + fast.inc_y 265 if fast.count > 100 then s_y = s_y + 1 end 266 end 267 y = y - fast.y + last.inc_y 268 s_x = last.sx 269 last.dy = -fast.y 270 ship_t.disp_t = ship_t.up_t 271 elseif action == act.PLA_DOWN_REPEAT then 272 fast.count = fast.count + 1 273 if fast.count % 10 == 0 then 274 fast.y = fast.y + fast.inc_y 275 if fast.count > 100 then s_y = s_y + 1 end 276 end 277 y = y + fast.y + last.inc_y 278 s_x = last.sx 279 last.dy = fast.y 280 ship_t.disp_t = ship_t.dn_t 281 elseif action == act.PLA_RIGHT then 282 last.inc_x = last.inc_x + 1 283 x = x + last.dx + 1 284 if last.inc_x < 0 then 285 last.inc_x = 0 286 end 287 last.dx = last.inc_x 288 ship_t.disp_t = ship_t.rt_t 289 elseif action == act.PLA_LEFT then 290 last.inc_x = last.inc_x - 1 291 x = x + last.dx - 1 292 if last.inc_x > 0 then 293 last.inc_x = 0 294 end 295 last.dx = last.inc_x 296 ship_t.disp_t = ship_t.lt_t 297 elseif action == act.PLA_UP then 298 last.inc_y = last.inc_y - 1 299 y = y + last.dy - 1 300 if last.inc_y > 0 then 301 last.inc_y = 0 302 end 303 last.dy = last.inc_y 304 ship_t.disp_t = ship_t.up_t 305 elseif action == act.PLA_DOWN then 306 last.inc_y = last.inc_y + 1 307 y = y + last.dy + 1 308 if last.inc_y < 0 then 309 last.inc_y = 0 310 end 311 last.dy = last.inc_y 312 ship_t.disp_t = ship_t.dn_t 313 elseif action == act.PLA_SELECT_REPEAT then 314 rockev.suspend("timer", true) 315 if s_x < 10 and s_y < 10 then 316 s_x = last.sx + 1 317 s_y = last.sy + 1 318 last.sx = s_x 319 last.sy = s_y 320 end 321 elseif action == act.PLA_SELECT then 322 s_x = last.sx + 1 323 s_y = last.sy + 1 324 if s_x > 10 or s_y > 10 then 325 s_x = 1 326 s_y = 1 327 end 328 last.sx = s_x 329 last.sy = s_y 330 elseif action == act.ACTION_NONE then 331 if fast.count > 100 then 332 z = (random(0, 400) / 100) * 4 333 end 334 fast.count = 0 335 fast.x = fast.inc_x 336 fast.y = fast.inc_y 337 s_x = last.sx 338 s_y = last.sy 339 backlight_on = turn_on_backlight 340 rb.backlight_use_settings() 341 if last.dx ~= 0 or last.dy ~= 0 then 342 rockev.suspend("timer", false) 343 else 344 last.inc_x = 0 345 last.inc_y = 0 346 end 347 end 348 349 _lcd:clear(BLACK) 350 for i = 0, z, 4 do 351 drawStars(_LCD, draw_points, x, y, i+1, RED, s_x, s_y) 352 drawStars(_LCD, draw_points, x, y, i+2, GREEN, s_x, s_y) 353 drawStars(_LCD, draw_points, x, y, i+3, BLUE, s_x, s_y) 354 drawStars(_LCD, draw_points, x, y, i+4, WHITE, s_x, s_y) 355 end 356 357 local hit_t = {} 358 local SHIP_X, SHIP_Y = ship_t.x + 8, ship_t.y + 8 --center the ship coords 359 local function draw_asteroids(img, x, y, color, n, hash) 360 if n > 0 then 361 local x0, y0, w0, h0 362 x0,y0,w0,h0 = draw_astroid(img, x, y, hash, color, false, s_x, s_y) 363 --check bounds 364 if s_x == s_y and x0 <= SHIP_X and x0+w0 >= SHIP_X and y0+h0 >= SHIP_Y and y0 <= SHIP_Y then 365 local r_t = {x = x0, y = y0, w = w0, h= h0, hash = hash, color = color} 366 hit_t[#hit_t + 1] = r_t 367 end 368 end 369 end 370 371 drawStars(_LCD, draw_asteroids, x, y, 1, RED, s_x, s_y) 372 drawStars(_LCD, draw_asteroids, x, y, 2, GREEN, s_x, s_y) 373 drawStars(_LCD, draw_asteroids, x, y, 3, BLUE, s_x, s_y) 374 drawStars(_LCD, draw_asteroids, x, y, 4, WHITE, s_x, s_y) 375 if fast.count < 10 and last.dx == last.dy then 376 local seen = {} -- might have multiple hits but only show unique hashes 377 for i, v in ipairs(hit_t) do 378 if i < 4 then 379 draw_astroid(_LCD, v.x + v.w / 2, v.y + v.h / 2, v.hash, WHITE, v.color, s_x, s_y) 380 end 381 end 382 for i, v in ipairs(hit_t) do 383 if not seen[v.hash] then 384 rb.lcd_puts(0, (i - 1), string.format("[%x]", v.hash)) 385 end 386 seen[v.hash] = i 387 end 388 end 389 390 drawship(_LCD, ship_t) 391 update_lcd() 392 393 --last_action = action 394 end 395 396 function action_set_quit(bQuit) 397 quit = bQuit 398 end 399 400 function action_quit() 401 return quit 402 end 403end 404 405if not rb.backlight_force_on then 406 rb.backlight_force_on = function() end 407end 408 409if not rb.backlight_use_settings then 410 rb.backlight_use_settings = function() end 411end 412 413action_event(rb.actions.ACTION_NONE) -- we can call this now but not after registering.. 414local eva = rockev.register("action", action_event) 415local evc = rockev.register("timer", action_drift, rb.HZ/7) 416 417while not action_quit() do rb.sleep(rb.HZ) end 418 419if start_x and start_y then 420 file = io.open(fname, "w") 421 file:write(start_x, "\n", start_y, "\n", start_z or 0, "\n", scale_x or 1, "\n", scale_y or 1, "\n") 422 io.close( file ) 423end