feat(sound): concert engine + hypno drift morceau
Sequential techno setlist engine with crossed-arms gesture to advance tracks. Adds ~poseCenter OSCdef, scene_concert.scd engine, morceaux/ directory, first morceau (Hypno Drift), and wires into boot/scenes.
This commit is contained in:
@@ -494,6 +494,7 @@ OSCdef(\poseLeave, { |msg|
|
||||
var pid = msg[1];
|
||||
~poseState.removeAt(pid);
|
||||
~poseKin.removeAt(pid);
|
||||
~poseCenter.removeAt(pid);
|
||||
}, '/pose/leave');
|
||||
|
||||
OSCdef(\poseHands, { |msg|
|
||||
@@ -508,6 +509,20 @@ OSCdef(\poseHands, { |msg|
|
||||
~handFeat[\dist] = msg[10];
|
||||
}, '/pose/hands');
|
||||
|
||||
~poseCenter = ~poseCenter ? Dictionary.new;
|
||||
OSCdef(\poseCenter, { |msg|
|
||||
var pid = msg[1].asInteger;
|
||||
var e = ~poseCenter[pid] ? ();
|
||||
e[\cx] = msg[2]; e[\cy] = msg[3];
|
||||
~poseCenter[pid] = e;
|
||||
}, '/pose/center');
|
||||
OSCdef(\poseShoSpan, { |msg|
|
||||
var pid = msg[1].asInteger;
|
||||
var e = ~poseCenter[pid] ? ();
|
||||
e[\depth] = msg[2]; // shoulder span proxy: bigger = closer
|
||||
~poseCenter[pid] = e;
|
||||
}, '/pose/sho_span');
|
||||
|
||||
"[OK] action-head OSC handlers".postln;
|
||||
|
||||
"[data_feeds] OSCdef installes (USGS, SWPC, NETZ, RTE, BLITZ, OPENSKY, BSKY, MEMPOOL, GCN).".postln;
|
||||
|
||||
@@ -160,6 +160,14 @@ Routine({
|
||||
(~base ++ "scene_pose_play.scd").load;
|
||||
0.5.wait;
|
||||
|
||||
// -- 6d. Concert engine + morceaux -----
|
||||
(~base ++ "scene_concert.scd").load;
|
||||
0.3.wait;
|
||||
[
|
||||
"morceaux/01_hypno_drift.scd",
|
||||
].do { |f| (~base ++ f).load; 0.1.wait; };
|
||||
s.sync;
|
||||
|
||||
// -- 7) Demarre la scene par defaut -----
|
||||
if(~doScene.isNil) {
|
||||
"[data-only/boot] ERREUR : ~doScene non defini".warn;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Morceau 1 -- Hypno Drift (hypnotic). Drone filtre + sub pulse + kick sparse.
|
||||
// mains hautes -> ouvre le filtre ; cx -> pan ; kick lent quand corps present.
|
||||
(
|
||||
SynthDef(\cc_drift_drone, { |out=0, freq=55, cutoff=300, amp=0.0, pan=0, gate=1|
|
||||
var sig = Mix(Saw.ar(freq * [1, 1.003, 0.5])) * 0.25;
|
||||
sig = RLPF.ar(sig, cutoff.clip(100, 6000), 0.4);
|
||||
sig = sig * EnvGen.kr(Env.asr(3, 1, 4), gate, doneAction: 2);
|
||||
Out.ar(out, Pan2.ar(sig, pan.clip(-1, 1)) * amp.lag(1.0));
|
||||
}).add;
|
||||
SynthDef(\cc_drift_sub, { |out=0, freq=55, amp=0.0, gate=1|
|
||||
var sig = SinOsc.ar(freq.clip(20, 200)) * LFPulse.kr(0.5, 0, 0.35);
|
||||
sig = sig * EnvGen.kr(Env.asr(0.5, 1, 1), gate, doneAction: 2);
|
||||
Out.ar(out, Pan2.ar(sig, 0) * amp.lag(0.5));
|
||||
}).add;
|
||||
SynthDef(\cc_drift_kick, { |out=0, amp=0.5|
|
||||
var env = EnvGen.kr(Env.perc(0.001, 0.4), doneAction: 2);
|
||||
var fenv = EnvGen.kr(Env([120, 45], [0.08], \exp));
|
||||
var sig = SinOsc.ar(fenv) * env;
|
||||
Out.ar(out, Pan2.ar(sig.tanh, 0) * amp);
|
||||
}).add;
|
||||
|
||||
~concertAdd.((
|
||||
name: "Hypno Drift", style: \hypnotic,
|
||||
start: { |ctx|
|
||||
ctx[\drone] = Synth(\cc_drift_drone, [\freq, 55, \cutoff, 300, \amp, 0.22]);
|
||||
ctx[\sub] = Synth(\cc_drift_sub, [\freq, 55, \amp, 0.18]);
|
||||
ctx[\cut] = 300; ctx[\pan] = 0;
|
||||
},
|
||||
update: { |ctx, pose|
|
||||
var hf = pose[\hands];
|
||||
var ctr = pose[\center];
|
||||
var hi = (1 - (((hf[\ry] ? 1) + (hf[\ly] ? 1)) * 0.5)).clip(0, 1);
|
||||
var tgt = hi.linexp(0, 1, 250, 4500);
|
||||
var panT = ((ctr[\cx] ? 0.5) * 2 - 1).clip(-1, 1);
|
||||
ctx[\cut] = ctx[\cut] + ((tgt - ctx[\cut]) * 0.05);
|
||||
ctx[\pan] = ctx[\pan] + ((panT - ctx[\pan]) * 0.05);
|
||||
ctx[\drone].set(\cutoff, ctx[\cut], \pan, ctx[\pan]);
|
||||
},
|
||||
tick: { |ctx, pose, beat|
|
||||
if(((beat % (0.5 / ~ccCtlDur).round.max(1)) == 0) and: { pose[\hasBody] }) {
|
||||
Synth(\cc_drift_kick, [\amp, 0.5]);
|
||||
};
|
||||
},
|
||||
stop: { |ctx|
|
||||
ctx[\drone] !? { |s| s.set(\gate, 0) };
|
||||
ctx[\sub] !? { |s| s.set(\gate, 0) };
|
||||
},
|
||||
));
|
||||
"[morceau] Hypno Drift loaded".postln;
|
||||
)
|
||||
@@ -0,0 +1,122 @@
|
||||
// =====================================================================
|
||||
// data_only/scene_concert.scd -- moteur de concert (setlist techno).
|
||||
// Setlist sequentielle, geste bras-croises = morceau suivant, crossfade.
|
||||
// Active via ~doScene.(\concert). Les morceaux s'enregistrent via
|
||||
// ~concertAdd depuis morceaux/NN_*.scd (charges apres ce fichier).
|
||||
// =====================================================================
|
||||
(
|
||||
~ccCtlDur = 0.05; // periode du tick de controle, en BEATS
|
||||
~ccCrossHold = 1.5; // duree de maintien du geste bras-croises (s)
|
||||
~ccCrossCool = 3.0; // cooldown apres declenchement (s)
|
||||
|
||||
// -- snapshot pose (premiere personne), tout nil-garde --
|
||||
~ccPose = {
|
||||
var kin = ~poseKin.notNil.if({ ~poseKin.values.detect({ |v| v.notNil }) });
|
||||
var st = ~poseState.notNil.if({ ~poseState.values.detect({ |v| v.notNil }) });
|
||||
var ctr = ~poseCenter.notNil.if({ ~poseCenter.values.detect({ |v| v.notNil }) });
|
||||
(kin: (kin ? ()), state: (st ? ()), center: (ctr ? ()),
|
||||
hands: (~handFeat ? ()), hasBody: kin.notNil);
|
||||
};
|
||||
|
||||
// -- registre des morceaux --
|
||||
~concert = ~concert ? ();
|
||||
~concert[\morceaux] = [];
|
||||
~concertAdd = { |m| ~concert[\morceaux] = ~concert[\morceaux].add(m) };
|
||||
|
||||
~concert[\running] = false;
|
||||
~concert[\idx] = 0;
|
||||
~concert[\clock] = nil;
|
||||
~concert[\routine] = nil;
|
||||
~concert[\ctx] = nil; // (m: module, ctx: scratch) du morceau courant
|
||||
~concert[\crossT] = 0.0;
|
||||
~concert[\lastFire] = -100.0;
|
||||
|
||||
~concert[\startMorceau] = { |i|
|
||||
var m = ~concert[\morceaux][i];
|
||||
var ctx = ();
|
||||
m[\start].(ctx);
|
||||
~concert[\ctx] = (m: m, ctx: ctx);
|
||||
("[concert] " ++ (i + 1) ++ "/" ++ ~concert[\morceaux].size
|
||||
++ " : " ++ m[\name]).postln;
|
||||
};
|
||||
|
||||
~concert[\stopMorceau] = {
|
||||
~concert[\ctx] !? { |c| c[\m][\stop].(c[\ctx]) };
|
||||
~concert[\ctx] = nil;
|
||||
};
|
||||
|
||||
~concert[\next] = {
|
||||
if(~concert[\idx] < (~concert[\morceaux].size - 1)) {
|
||||
~concert[\stopMorceau].(); // fade out (stop releve les gates)
|
||||
~concert[\idx] = ~concert[\idx] + 1;
|
||||
~concert[\startMorceau].(~concert[\idx]); // fade in
|
||||
} {
|
||||
"[concert] derniere piste (fin de set)".postln;
|
||||
};
|
||||
};
|
||||
|
||||
// geste bras-croises : main D a gauche de main G, a mi-hauteur, maintenu
|
||||
~concert[\detectCross] = { |pose, now|
|
||||
var hf = pose[\hands];
|
||||
var crossed = pose[\hasBody] and: {
|
||||
((hf[\rx] ? 0.5) < ((hf[\lx] ? 0.5) - 0.08))
|
||||
and: { (hf[\ry] ? 0).inRange(0.25, 0.75) }
|
||||
and: { (hf[\ly] ? 0).inRange(0.25, 0.75) }
|
||||
};
|
||||
if(crossed) {
|
||||
if(~concert[\crossT] <= 0) { ~concert[\crossT] = now };
|
||||
if(((now - ~concert[\crossT]) >= ~ccCrossHold)
|
||||
and: { (now - ~concert[\lastFire]) > ~ccCrossCool }) {
|
||||
~concert[\lastFire] = now;
|
||||
~concert[\crossT] = 0;
|
||||
~concert[\next].();
|
||||
};
|
||||
} { ~concert[\crossT] = 0 };
|
||||
};
|
||||
|
||||
~concert[\start] = {
|
||||
if(~concert[\running]) { "[concert] deja en cours".postln } {
|
||||
if(~concert[\morceaux].size < 1) {
|
||||
"[concert] aucun morceau enregistre".warn;
|
||||
} {
|
||||
~concert[\running] = true;
|
||||
~concert[\idx] = 0;
|
||||
~concert[\clock] = TempoClock(126/60).permanent_(true);
|
||||
~concert[\startMorceau].(0);
|
||||
~concert[\routine] = Routine({
|
||||
var beat = 0;
|
||||
inf.do {
|
||||
var pose = ~ccPose.value;
|
||||
var now = thisThread.seconds;
|
||||
var c;
|
||||
~concert[\detectCross].(pose, now);
|
||||
c = ~concert[\ctx]; // relire (next a pu permuter)
|
||||
c !? { |cc|
|
||||
cc[\m][\update].(cc[\ctx], pose);
|
||||
cc[\m][\tick] !? { |t| t.(cc[\ctx], pose, beat) };
|
||||
};
|
||||
beat = beat + 1;
|
||||
~ccCtlDur.wait;
|
||||
};
|
||||
}).play(~concert[\clock]);
|
||||
"[concert] STARTED".postln;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
~concert[\stop] = {
|
||||
~concert[\routine] !? { |r| r.stop };
|
||||
~concert[\stopMorceau].();
|
||||
~concert[\clock] !? { |c| c.stop };
|
||||
~concert[\clock] = nil;
|
||||
~concert[\running] = false;
|
||||
"[concert] STOPPED".postln;
|
||||
};
|
||||
|
||||
~doSceneConcert = {
|
||||
~concert[\start].();
|
||||
~doRegisterPriv.(\concert, { ~concert[\stop].() });
|
||||
};
|
||||
|
||||
"[data-only/scene_concert] engine ready".postln;
|
||||
)
|
||||
@@ -319,6 +319,7 @@
|
||||
\quiet, { ~doSceneQuiet.value },
|
||||
\live_pose, { ~doSceneLivePose.value },
|
||||
\play, { ~doScenePlay.value },
|
||||
\concert, { ~doSceneConcert.value },
|
||||
\all, {
|
||||
~doSceneCavity.value;
|
||||
~doSceneGeo.value;
|
||||
|
||||
Reference in New Issue
Block a user