Files
AV-Live/sound_algo/data_only/boot.scd
T
L'électron rare 088b233a48 feat(data-only): ingest /pose/hands into handFeat
Add OSCdef(\poseHands) to data_feeds.scd storing lx/ly/lopen/lspeed,
rx/ry/ropen/rspeed/dist into ~handFeat Dictionary (9 floats, pid=0).
Initialize ~handFeat alongside existing ~poseState/~poseKin.

Load scene_pose_action.scd in boot.scd after scenes.scd so ~mapPoseToFx
is defined at runtime (was silently dead before).

Document /pose/hands contract in data_only_viz/README.md.
2026-06-26 09:59:34 +02:00

161 lines
6.4 KiB
Plaintext

// =====================================================================
// data_only/boot.scd -- Entry point COMPLET du patch data-only.
//
// Tout-en-un, autonome, AUCUNE dependance vers live/_load.scd ni vers
// la palette de SynthDefs du systeme live principal :
//
// 1. Demarre oscope-of si pas deja en cours d'execution (best-effort)
// 2. Boot scsynth (Server.local)
// 3. Charge engine.scd (FX rack : reverb + comp + limiter)
// 4. Charge synthdefs.scd (10 SynthDefs \\do_*)
// 5. Charge control/data_feeds.scd (OSCdef /data/* + ~feeds)
// 6. Charge scenes.scd (9 scenes + selecteur ~doScene)
// 7. Lance la scene \\all
//
// Usage standalone :
// sclang /chemin/AV-Live/sound_algo/data_only/boot.scd
//
// Le launcher en mode Data-only utilise ce fichier automatiquement
// (cf. ProcessManager.swift, branche mode == .dataOnly).
// =====================================================================
(
~base = (thisProcess.nowExecutingPath !? { |p| p.dirname ++ "/" })
?? { File.getcwd ++ "/" };
~repoRoot = ~base ++ "../"; // sound_algo/
("[data-only/boot] ~base = " ++ ~base).postln;
("[data-only/boot] ~repoRoot = " ++ ~repoRoot).postln;
// -- 1a) Demarre le visualizer Python+Metal (data_only_viz) si absent.
// Remplace oscope-of dans le patch data-only.
{
var pgrep = "pgrep -f 'data_only_viz' >/dev/null 2>&1".systemCmd;
if(pgrep != 0) {
var vizDir = ~repoRoot ++ "../data_only_viz";
var uvCandidates = [
"/opt/homebrew/bin/uv", "/usr/local/bin/uv",
"~/.local/bin/uv".standardizePath,
];
var uv = uvCandidates.detect { |p| File.exists(p) };
if(uv.notNil and: { File.exists(vizDir ++ "/main.py") }) {
// --project pointe uv sur le pyproject.toml de data_only_viz/,
// cwd reste au parent pour resoudre `-m data_only_viz.main`.
var cmd = "cd '" ++ vizDir ++ "/..' && '" ++ uv ++ "' --project '"
++ vizDir ++ "' run python -m data_only_viz.main"
++ " >/tmp/avlive-metal_viz.log 2>&1 &";
("[data-only/boot] launching Python+Metal visualizer").postln;
(" logs: tail -f /tmp/avlive-metal_viz.log").postln;
cmd.unixCmd;
} {
"[data-only/boot] data_only_viz/main.py NOT launched (uv ou main.py manquant)".warn;
(" expected: " ++ vizDir).warn;
};
} {
"[data-only/boot] metal viz already running".postln;
};
}.value;
// -- 1b) Demarre le pont Python data_feeds (scraping opendata) si
// absent. Sans ce pont, ~feeds reste vide et les scenes sont
// muettes/statiques. Le launcher fait deja ca quand il pilote
// l'app, mais on couvre le cas standalone (sclang boot.scd).
{
var pgrep = "pgrep -f 'data_feeds/bridge.py' >/dev/null 2>&1".systemCmd;
if(pgrep != 0) {
var dfDir = ~repoRoot ++ "../data_feeds";
// Detecte uv (apple silicon brew prioritaire)
var uvCandidates = [
"/opt/homebrew/bin/uv", "/usr/local/bin/uv",
"~/.local/bin/uv".standardizePath,
];
var uv = uvCandidates.detect { |p| File.exists(p) };
if(uv.notNil and: { File.exists(dfDir ++ "/bridge.py") }) {
// Selectionne config.data-only.toml si present
var cfg = if(File.exists(dfDir ++ "/config.data-only.toml")) {
" -c config.data-only.toml"
} { "" };
var cmd = "cd '" ++ dfDir ++ "' && '" ++ uv ++ "' run python bridge.py"
++ cfg ++ " >/tmp/avlive-data_feeds.log 2>&1 &";
("[data-only/boot] launching data_feeds bridge...").postln;
(" logs: tail -f /tmp/avlive-data_feeds.log").postln;
cmd.unixCmd;
} {
"[data-only/boot] data_feeds bridge NOT launched (uv ou bridge.py manquant)".warn;
(" expected dir: " ++ dfDir).warn;
};
} {
"[data-only/boot] data_feeds bridge already running".postln;
};
}.value;
// -- 2) Boot scsynth + chargement asynchrone -------------------------
Routine({
var maxWait, waited;
// Ouvre le port 57121 pour recevoir les /control/* du launcher et
// de tout client externe (cf OSCSender swift). Echec silencieux
// si le port est deja pris (ex: web bridge actif) — dans ce cas
// sclang reste sur son langPort par defaut.
if(thisProcess.openUDPPort(57121).not) {
("[data-only/boot] WARN: port 57121 occupe, fallback sur langPort = "
++ NetAddr.langPort).postln;
} {
("[data-only/boot] sclang listening on " ++ NetAddr.langPort ++ " + 57121").postln;
};
// Boot le serveur (idempotent : si deja boot, retourne immediatement)
Server.local.options.numOutputBusChannels = 2;
Server.local.options.memSize = 65536;
"[data-only/boot] booting scsynth...".postln;
s.bootSync;
// -- 3) Engine (FX rack) -----
(~base ++ "engine.scd").load;
maxWait = 10; waited = 0;
while({ ~doEngineReady != true and: { waited < maxWait } }) {
0.2.wait; waited = waited + 0.2;
};
if(~doEngineReady != true) {
"[data-only/boot] WARN: engine pas pret apres 10s".warn;
};
// -- 4) SynthDefs -----
(~base ++ "synthdefs.scd").load;
s.sync;
0.5.wait;
// -- 5) OSCdef des flux data_feeds -----
(~repoRoot ++ "control/data_feeds.scd").load;
0.5.wait;
// -- 6) Bibliotheque de scenes -----
(~base ++ "scenes.scd").load;
0.5.wait;
// -- 6b. Pose/hand -> FX mapping -----
(~base ++ "scene_pose_action.scd").load;
0.5.wait;
// -- 7) Demarre la scene par defaut -----
if(~doScene.isNil) {
"[data-only/boot] ERREUR : ~doScene non defini".warn;
} {
"[data-only/boot] starting scene \\all".postln;
~doScene.(\all);
};
"".postln;
"===========================================".postln;
" AV-Live DATA-ONLY PATCH READY".postln;
"===========================================".postln;
(" bridge alive : " ++ ~feedAlive.value).postln;
" scenes : ~doScene.(\\name)".postln;
" available : \\cavity \\geo \\body \\weather".postln;
" \\flight \\pulse \\quiet \\all \\full \\stop".postln;
" OSC remote : /control/doScene <\\name>".postln;
" master gain : /control/doMaster <0..2>".postln;
"===========================================".postln;
}).play(AppClock);
)