Add spectrum analyzer theming and visualization styles
- Add 6 color themes: green, blue, purple, red, amber, rainbow - Add 3 visualization styles: bars, wave, dots - Add UI controls (dropdowns) to change theme and style - Persist user preferences in localStorage - Remove debug logging from parenscript serving and Icecast stats - Fix dots visualization with proper Math.PI handling
This commit is contained in:
parent
b6be0ebab1
commit
51387bddba
|
|
@ -558,11 +558,9 @@
|
|||
(cond
|
||||
;; Serve ParenScript-compiled auth-ui.js
|
||||
((string= path "js/auth-ui.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT auth-ui.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-auth-ui-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating auth-ui.js: ~a~%" e)
|
||||
|
|
@ -570,11 +568,9 @@
|
|||
|
||||
;; Serve ParenScript-compiled front-page.js
|
||||
((string= path "js/front-page.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT front-page.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-front-page-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating front-page.js: ~a~%" e)
|
||||
|
|
@ -582,11 +578,9 @@
|
|||
|
||||
;; Serve ParenScript-compiled profile.js
|
||||
((string= path "js/profile.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT profile.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-profile-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating profile.js: ~a~%" e)
|
||||
|
|
@ -594,11 +588,9 @@
|
|||
|
||||
;; Serve ParenScript-compiled users.js
|
||||
((string= path "js/users.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT users.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-users-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating users.js: ~a~%" e)
|
||||
|
|
@ -606,11 +598,9 @@
|
|||
|
||||
;; Serve ParenScript-compiled admin.js
|
||||
((string= path "js/admin.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT admin.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-admin-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating admin.js: ~a~%" e)
|
||||
|
|
@ -618,11 +608,9 @@
|
|||
|
||||
;; Serve ParenScript-compiled player.js
|
||||
((string= path "js/player.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT player.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-player-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating player.js: ~a~%" e)
|
||||
|
|
@ -630,11 +618,9 @@
|
|||
|
||||
;; Serve ParenScript-compiled recently-played.js
|
||||
((string= path "js/recently-played.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT recently-played.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-recently-played-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating recently-played.js: ~a~%" e)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
(response (drakma:http-request icecast-url
|
||||
:want-stream nil
|
||||
:basic-authorization '("admin" "asteroid_admin_2024"))))
|
||||
(format t "DEBUG: Fetching Icecast stats from ~a~%" icecast-url)
|
||||
(when response
|
||||
(let ((xml-string (if (stringp response)
|
||||
response
|
||||
|
|
@ -34,7 +33,6 @@
|
|||
(aref groups 0)
|
||||
"Unknown")))
|
||||
"Unknown")))
|
||||
(format t "DEBUG: Parsed title=~a, total-listeners=~a~%" title total-listeners)
|
||||
|
||||
;; Track recently played if title changed
|
||||
(when (and title
|
||||
|
|
|
|||
|
|
@ -14,6 +14,18 @@
|
|||
(defvar *animation-id* nil)
|
||||
(defvar *media-source* nil)
|
||||
(defvar *current-audio-element* nil)
|
||||
(defvar *current-theme* "green")
|
||||
(defvar *current-style* "bars")
|
||||
|
||||
;; Color themes for spectrum analyzer
|
||||
(defvar *themes*
|
||||
(ps:create
|
||||
"green" (ps:create "top" "#00ff00" "mid" "#00aa00" "bottom" "#005500")
|
||||
"blue" (ps:create "top" "#00ffff" "mid" "#0088ff" "bottom" "#0044aa")
|
||||
"purple" (ps:create "top" "#ff00ff" "mid" "#aa00aa" "bottom" "#550055")
|
||||
"red" (ps:create "top" "#ff0000" "mid" "#aa0000" "bottom" "#550000")
|
||||
"amber" (ps:create "top" "#ffaa00" "mid" "#ff6600" "bottom" "#aa3300")
|
||||
"rainbow" (ps:create "top" "#ff00ff" "mid" "#00ffff" "bottom" "#00ff00")))
|
||||
|
||||
(defun reset-spectrum-analyzer ()
|
||||
"Reset the spectrum analyzer to allow reconnection after audio element reload"
|
||||
|
|
@ -98,21 +110,60 @@
|
|||
(setf (ps:@ *canvas-ctx* |fillStyle|) "rgba(0, 0, 0, 0.2)")
|
||||
(ps:chain *canvas-ctx* (fill-rect 0 0 width height))
|
||||
|
||||
;; Draw bars
|
||||
(dotimes (i buffer-length)
|
||||
(setf bar-height (/ (* (aref data-array i) height) 256))
|
||||
|
||||
;; Create gradient for each bar
|
||||
(let ((gradient (ps:chain *canvas-ctx*
|
||||
(create-linear-gradient 0 (- height bar-height) 0 height))))
|
||||
(ps:chain gradient (add-color-stop 0 "#00ff00"))
|
||||
(ps:chain gradient (add-color-stop 0.5 "#00aa00"))
|
||||
(ps:chain gradient (add-color-stop 1 "#005500"))
|
||||
;; Get current theme colors
|
||||
(let ((theme (ps:getprop *themes* *current-theme*)))
|
||||
(cond
|
||||
;; Bar graph style
|
||||
((= *current-style* "bars")
|
||||
(setf x 0)
|
||||
(dotimes (i buffer-length)
|
||||
(setf bar-height (/ (* (aref data-array i) height) 256))
|
||||
|
||||
;; Create gradient for each bar using theme colors
|
||||
(let ((gradient (ps:chain *canvas-ctx*
|
||||
(create-linear-gradient 0 (- height bar-height) 0 height))))
|
||||
(ps:chain gradient (add-color-stop 0 (ps:@ theme top)))
|
||||
(ps:chain gradient (add-color-stop 0.5 (ps:@ theme mid)))
|
||||
(ps:chain gradient (add-color-stop 1 (ps:@ theme bottom)))
|
||||
|
||||
(setf (ps:@ *canvas-ctx* |fillStyle|) gradient)
|
||||
(ps:chain *canvas-ctx* (fill-rect x (- height bar-height) bar-width bar-height))
|
||||
|
||||
(incf x bar-width))))
|
||||
|
||||
;; Wave/line style
|
||||
((= *current-style* "wave")
|
||||
(setf x 0)
|
||||
(ps:chain *canvas-ctx* (begin-path))
|
||||
(setf (ps:@ *canvas-ctx* |lineWidth|) 2)
|
||||
(setf (ps:@ *canvas-ctx* |strokeStyle|) (ps:@ theme top))
|
||||
|
||||
(setf (ps:@ *canvas-ctx* |fillStyle|) gradient)
|
||||
(ps:chain *canvas-ctx* (fill-rect x (- height bar-height) bar-width bar-height))
|
||||
(dotimes (i buffer-length)
|
||||
(setf bar-height (/ (* (aref data-array i) height) 256))
|
||||
(let ((y (- height bar-height)))
|
||||
(if (= i 0)
|
||||
(ps:chain *canvas-ctx* (move-to x y))
|
||||
(ps:chain *canvas-ctx* (line-to x y)))
|
||||
(incf x bar-width)))
|
||||
|
||||
(incf x bar-width)))
|
||||
(ps:chain *canvas-ctx* (stroke)))
|
||||
|
||||
;; Dots/particles style
|
||||
((= *current-style* "dots")
|
||||
(setf x 0)
|
||||
(setf (ps:@ *canvas-ctx* |fillStyle|) (ps:@ theme top))
|
||||
(dotimes (i buffer-length)
|
||||
(let* ((value (aref data-array i))
|
||||
(normalized-height (/ (* value height) 256))
|
||||
(y (- height normalized-height))
|
||||
(dot-radius (ps:max 2 (/ normalized-height 20))))
|
||||
|
||||
(when (> value 0)
|
||||
(ps:chain *canvas-ctx* (begin-path))
|
||||
(ps:chain *canvas-ctx* (arc x y dot-radius 0 6.283185307179586))
|
||||
(ps:chain *canvas-ctx* (fill)))
|
||||
|
||||
(incf x bar-width))))))
|
||||
|
||||
;; Draw MUTED indicator if audio is muted
|
||||
(when is-muted
|
||||
|
|
@ -128,9 +179,47 @@
|
|||
(cancel-animation-frame *animation-id*)
|
||||
(setf *animation-id* nil)))
|
||||
|
||||
(defun set-spectrum-theme (theme-name)
|
||||
"Change the spectrum analyzer color theme"
|
||||
(when (ps:getprop *themes* theme-name)
|
||||
(setf *current-theme* theme-name)
|
||||
(ps:chain local-storage (set-item "spectrum-theme" theme-name))
|
||||
(ps:chain console (log (+ "Spectrum theme changed to: " theme-name)))))
|
||||
|
||||
(defun get-available-themes ()
|
||||
"Return array of available theme names"
|
||||
(ps:chain |Object| (keys *themes*)))
|
||||
|
||||
(defun set-spectrum-style (style-name)
|
||||
"Change the spectrum analyzer visualization style"
|
||||
(when (or (= style-name "bars") (= style-name "wave") (= style-name "dots"))
|
||||
(setf *current-style* style-name)
|
||||
(ps:chain local-storage (set-item "spectrum-style" style-name))
|
||||
(ps:chain console (log (+ "Spectrum style changed to: " style-name)))))
|
||||
|
||||
(defun get-available-styles ()
|
||||
"Return array of available visualization styles"
|
||||
(array "bars" "wave" "dots"))
|
||||
|
||||
;; Initialize when audio starts playing
|
||||
(ps:chain document (add-event-listener "DOMContentLoaded"
|
||||
(lambda ()
|
||||
;; Load saved theme and style preferences
|
||||
(let ((saved-theme (ps:chain local-storage (get-item "spectrum-theme")))
|
||||
(saved-style (ps:chain local-storage (get-item "spectrum-style"))))
|
||||
(when (and saved-theme (ps:getprop *themes* saved-theme))
|
||||
(setf *current-theme* saved-theme))
|
||||
(when (and saved-style (or (= saved-style "bars") (= saved-style "wave") (= saved-style "dots")))
|
||||
(setf *current-style* saved-style))
|
||||
|
||||
;; Update UI selectors if they exist
|
||||
(let ((theme-selector (ps:chain document (get-element-by-id "spectrum-theme-selector")))
|
||||
(style-selector (ps:chain document (get-element-by-id "spectrum-style-selector"))))
|
||||
(when theme-selector
|
||||
(setf (ps:@ theme-selector value) *current-theme*))
|
||||
(when style-selector
|
||||
(setf (ps:@ style-selector value) *current-style*))))
|
||||
|
||||
(let ((audio-element (or (ps:chain document (get-element-by-id "live-audio"))
|
||||
(ps:chain document (get-element-by-id "persistent-audio")))))
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,27 @@
|
|||
<!-- Spectrum Analyzer Canvas -->
|
||||
<div style="text-align: center; margin: 15px 0;">
|
||||
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
|
||||
<div style="margin-top: 8px; font-size: 0.9em;">
|
||||
<label style="margin-right: 10px;">
|
||||
Style:
|
||||
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="bars">Bars</option>
|
||||
<option value="wave">Wave</option>
|
||||
<option value="dots">Dots</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Theme:
|
||||
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="purple">Purple</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="amber">Amber</option>
|
||||
<option value="rainbow">Rainbow</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav">
|
||||
|
|
|
|||
|
|
@ -26,6 +26,27 @@
|
|||
<!-- Spectrum Analyzer Canvas -->
|
||||
<div style="text-align: center; margin: 15px 0;">
|
||||
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
|
||||
<div style="margin-top: 8px; font-size: 0.9em;">
|
||||
<label style="margin-right: 10px;">
|
||||
Style:
|
||||
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="bars">Bars</option>
|
||||
<option value="wave">Wave</option>
|
||||
<option value="dots">Dots</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Theme:
|
||||
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="purple">Purple</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="amber">Amber</option>
|
||||
<option value="rainbow">Rainbow</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav">
|
||||
|
|
|
|||
|
|
@ -20,6 +20,27 @@
|
|||
<!-- Spectrum Analyzer Canvas -->
|
||||
<div style="text-align: center; margin: 15px 0;">
|
||||
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
|
||||
<div style="margin-top: 8px; font-size: 0.9em;">
|
||||
<label style="margin-right: 10px;">
|
||||
Style:
|
||||
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="bars">Bars</option>
|
||||
<option value="wave">Wave</option>
|
||||
<option value="dots">Dots</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Theme:
|
||||
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="purple">Purple</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="amber">Amber</option>
|
||||
<option value="rainbow">Rainbow</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav">
|
||||
|
|
|
|||
Loading…
Reference in New Issue