FOSFENO: motor de visuales audio-reactivas para Raspberry Pi

Primera version. Cinco motores (projectM, Butterchurn, Hydra, Shaders GLSL y mezclador VJ con camara y video), panel de control web, deteccion de BPM propia, pantalla de conexion con codigo QR, instalador robusto para Raspberry Pi 4 y 5 y documentacion completa en docs/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hacklab 2026-05-22 14:18:19 +02:00
commit 30a09fdee6
31 changed files with 3478 additions and 0 deletions

29
scripts/build-projectm.sh Executable file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Compila projectM v4 + su frontend SDL2 (projectMSDL) desde el codigo fuente.
# Lo invoca install.sh. Tarda bastante en una Raspberry Pi.
set -e
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
cd "$WORK"
echo "==> [projectM] Clonando y compilando la libreria projectM v4..."
git clone --depth 1 --recurse-submodules \
https://github.com/projectM-visualizer/projectm.git
cd projectm
cmake -B build -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_GLES=ON
cmake --build build --parallel "$(nproc)"
sudo cmake --install build
sudo ldconfig
cd "$WORK"
echo "==> [projectM] Clonando y compilando el frontend SDL2 (projectMSDL)..."
git clone --depth 1 --recurse-submodules \
https://github.com/projectM-visualizer/frontend-sdl2.git
cd frontend-sdl2
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build --parallel "$(nproc)"
sudo cmake --install build
echo "==> [projectM] Instalado correctamente."

View file

@ -0,0 +1,7 @@
[Desktop Entry]
Type=Application
Name=FOSFENO
Comment=Motor de visuales audio-reactivas
Exec=__DIR__/scripts/start-fosfeno.sh
X-GNOME-Autostart-enabled=true
NoDisplay=true

71
scripts/lib.sh Executable file
View file

@ -0,0 +1,71 @@
#!/usr/bin/env bash
# ===========================================================================
# FOSFENO :: lib.sh - funciones comunes para los scripts de instalacion
# Se carga con: source "$(dirname "$0")/scripts/lib.sh"
# ===========================================================================
# --- Colores (se desactivan si la salida no es un terminal) ----------------
if [ -t 1 ]; then
C_RED=$'\e[31m'; C_GRN=$'\e[32m'; C_YEL=$'\e[33m'
C_CYA=$'\e[36m'; C_BLD=$'\e[1m'; C_RST=$'\e[0m'
else
C_RED=''; C_GRN=''; C_YEL=''; C_CYA=''; C_BLD=''; C_RST=''
fi
# --- Logging ----------------------------------------------------------------
log_step() { printf '\n%s==> %s%s\n' "$C_CYA$C_BLD" "$*" "$C_RST"; }
log_ok() { printf ' %s[ OK ]%s %s\n' "$C_GRN" "$C_RST" "$*"; }
log_warn() { printf ' %s[ !! ]%s %s\n' "$C_YEL" "$C_RST" "$*"; }
log_fail() { printf ' %s[ XX ]%s %s\n' "$C_RED" "$C_RST" "$*"; }
log_info() { printf ' %s-%s %s\n' "$C_CYA" "$C_RST" "$*"; }
# Contador global de errores no fatales
FOSFENO_WARNINGS=0
warn() { log_warn "$*"; FOSFENO_WARNINGS=$((FOSFENO_WARNINGS + 1)); }
# --- Versiones --------------------------------------------------------------
# ver_of "salida cualquiera v1.2.3 bla" -> "1.2.3"
ver_of() { printf '%s' "$1" | grep -oE '[0-9]+(\.[0-9]+){1,2}' | head -n1; }
# ver_ge "1.2.3" "1.2.0" -> exit 0 si $1 >= $2
ver_ge() {
[ "$1" = "$2" ] && return 0
local menor
menor=$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)
[ "$menor" = "$2" ]
}
# require_version <nombre> <comando --version> <version_minima>
# Comprueba que un comando existe y cumple la version minima.
require_version() {
local nombre="$1" cmd="$2" minima="$3" salida actual
if ! command -v "${cmd%% *}" >/dev/null 2>&1; then
log_fail "$nombre no esta instalado"
return 1
fi
salida=$($cmd 2>&1 | head -n3)
actual=$(ver_of "$salida")
if [ -z "$actual" ]; then
warn "$nombre instalado pero no se pudo leer la version"
return 0
fi
if ver_ge "$actual" "$minima"; then
log_ok "$nombre $actual (minimo $minima)"
return 0
else
log_fail "$nombre $actual es anterior al minimo requerido $minima"
return 1
fi
}
# --- Hardware ---------------------------------------------------------------
pi_model() {
if [ -r /proc/device-tree/model ]; then
tr -d '\0' < /proc/device-tree/model
else
echo "desconocido"
fi
}
# --- Utilidades -------------------------------------------------------------
need_cmd() { command -v "$1" >/dev/null 2>&1; }

17
scripts/start-fosfeno.sh Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Arranque de FOSFENO. Lo ejecuta el escritorio de Raspberry Pi OS al iniciar
# (ver scripts/fosfeno-autostart.desktop, copiado a ~/.config/autostart/).
DIR="$(cd "$(dirname "$0")/.." && pwd)"
# Oculta el cursor del raton tras 1s de inactividad
command -v unclutter >/dev/null 2>&1 && unclutter -idle 1 &
# Evita que la pantalla se apague o entre en ahorro de energia (sesion X11)
xset s off 2>/dev/null
xset -dpms 2>/dev/null
xset s noblank 2>/dev/null
# Espera a que la red este lista (para que el panel sea accesible)
sleep 5
exec "$DIR/.venv/bin/python3" "$DIR/backend/server.py"

13
scripts/start-projectm.sh Executable file
View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Lanza projectM nativo a pantalla completa. Lo invoca el servidor cuando
# se selecciona el motor "projectM" desde el panel.
DIR="$(cd "$(dirname "$0")/.." && pwd)"
PRESETS="$DIR/data/presets-projectm"
# projectMSDL (frontend SDL2 de projectM v4). La ruta de presets se puede
# fijar tambien en ~/.config/projectMSDL/projectMSDL.properties
if [ -d "$PRESETS" ]; then
exec projectMSDL --presetPath "$PRESETS"
else
exec projectMSDL
fi