Mouse e tastiera¶
Intro ¶
Human Interface Device (HID)
Strumenti progettati per l'interazione uomo-macchina $\rightarrow$ mouse, tastiera, joystick, tavoletta grafica, etc.
Tre tipologie di controllo:
- trigger - un'azione, e un solo stato (click).
- switch - due azioni e due stati (0-1).
- continuo - i valori sono incrementali senza discontinuità.
In SuperCollider due modalità:
- Client side - valori recuperati nell'Interprete come nel caso delle GUI.
- Server side - valori recuperati nel Server e impiegati come segnali di controllo.
Mouse ¶
Permette tutte e tre le tipologie di controllo.
Client side ¶
Necessario creare una Window (GUI) con la quale interagire.
- Evitare interazioni indesiderate.
- Delimitare uno spazio (range) per i valori.
Diversi metodi per diverse azioni.
.mouseDownAction¶
Valuta la funzione al click su uno dei bottoni.
Restituisce un Array.
- view $\rightarrow$ la finestra sulla quale abbiamo cliccato
- x $\rightarrow$ il punto sull'asse x nella finestra in pixel (0 = punto più a sinistra)
- y $\rightarrow$ il punto sull'asse y nella finestra in pixel (0 = punto più in alto)
- modifiers $\rightarrow$ se c'è un tasto modificatore premuto
- botton n $\rightarrow$ numero del bottone premuto (0 = sinistro, 1 = destro, 2 = centrale)
- counter $\rightarrow$ il numero di click veloci (1 = singolo, 2 = doppio click)
(
w = Window.new("mouse");
w.view.mouseDownAction_({arg ...args;
args.postln; // tutte le info
args[1].postln; // solo posizione x
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free});
)
Esempio audio
- tasto sinistro $\rightarrow$ trigger sine.
- tasto destro $\rightarrow$ trigger noise.
s.boot;
s.scope(1);
s.plotTree;
(
SynthDef.new(\sine, {arg freq=0, amp=0, t_gate=0;
var sig,env; // Con inviluppo d'ampiezza
sig = SinOsc.ar(freq);
env = EnvGen.kr(Env.perc,t_gate,doneAction:2);
Out.ar(0,sig*env*amp);
}).add;
SynthDef.new(\noise, {arg amp=0;
var sig;
sig = PinkNoise.ar;
Out.ar(0,sig*amp.lag(3)); // fade dell'ampiezza di 3 secondi
}).add;
w = Window.new("mouse");
w.view.mouseDownAction_({arg ...args;
if(args[4] == 0,
{Synth.new(\sine,[\freq, args[1].linlin(0,400,300,2000), // Se clicco tasto sx...
\amp,1-args[2].linlin(0,400,0,1.0),
\t_gate, 1])},
{a.set(\amp, 1-args[2].linlin(0,400,0,1.0))}) // Se clicco tasto dx...
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;s.freeAll});
)
a = Synth.new(\noise);
.mouseUpAction¶
Come il precedente ma valuta la funzione al rilascio di uno dei bottoni.
(
w = Window.new("mouse");
w.view.mouseUpAction_({arg ...args;
args.postln; // tutte le info
args[1].postln; // solo posizione x
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free});
)
Esempio audio
- tasto sinistro $\rightarrow$ trigger sine.
- tasto destro $\rightarrow$ switch noise.
(
SynthDef.new(\sine, {arg freq=0, amp=0, t_gate=0;
var sig,env;
sig = SinOsc.ar(freq);
env = EnvGen.kr(Env.perc,t_gate,doneAction:2);
Out.ar(0,sig*env*amp);
}).add;
SynthDef.new(\noise, {arg amp=0;
var sig;
sig = PinkNoise.ar;
Out.ar(0,sig*amp.lag(0.2)); // fade di 0.2 secondi
}).add;
w = Window.new("mouse");
w.view.mouseDownAction_({arg ...args;
if(args[4] == 0,
{Synth.new(\sine,[\freq, args[1].linlin(0,400,300,2000),
\amp,1-args[2].linlin(0,400,0,1.0),
\t_gate, 1])},
{a.set(\amp, args[2].linlin(0,400,0,1.0))}) // note On (proporzioni invertite)
});
w.view.mouseUpAction_({arg ...args;
args.postln;
a.set(\amp, 0) // note Off
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;s.freeAll});
)
a = Synth.new(\noise)
.mouseMoveAction¶
Come .mouseDownAction ma se teniamo premuto il tasto riporta valori continui.
Valori in pixels.
Angolo in alto a sinistra $\rightarrow$ (0, 0).
Se usciamo dalla Windows $\rightarrow$ valori negativi o positivi.
(
w = Window.new("mouse");
w.view.mouseMoveAction_({arg ...args;
args.postln; // tutte le info
args[1].postln; // solo posizione x
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free});
)
Esempio audio
- click e rilascio $\rightarrow$ switch (noteOn e noteOff).
- trascina $\rightarrow$ segnale continuo.
(
SynthDef.new(\spectra, {arg freq=0, amp=0, harm=1, gate=0;
var sig,env;
sig = Blip.ar(freq,harm);
env = EnvGen.kr(Env.asr(0.3,1,5),gate,doneAction:2);
Out.ar(0,sig*env*amp);
}).add;
w = Window.new("mouse");
w.view.mouseDownAction_({arg ...args; // note On
a = Synth.new(\spectra, [\freq, args[1].linlin(0,400,300,2000),
\gate, 1]);
});
w.view.mouseMoveAction_({arg ...args; // controllo continuo
a.set(\amp,1-args[2].linlin(0,400,0,1.0),
\harm, args[1].linlin(0,400,1,20))
});
w.view.mouseUpAction_({arg ...args;
a.set(\gate,0) // note Off
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;s.freeAll});
)
.mouseOverAction¶
Come il precedente ma riporta i valori x, y semplicemente passando sulla Window senza cliccare.
(
w = Window.new("mouse").acceptsMouseOver_(true);// bisogna specificare;
w.view.mouseOverAction_({arg ...args;
args.postln; // tutte le info
args[1].postln; // solo posizione x
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free});
)
Esempio audio (segnale continuo)
(
b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
SynthDef.new(\scratch, {arg pos=0, amp=0, buf=0, smooth=0.2;
var sig, punt;
punt = BufFrames.ir(buf)*K2A.ar(pos).varlag(smooth);
sig = BufRd.ar(1,b,punt);
Out.ar(0,sig*amp.lag(0.2))
}).add;
w = Window.new("mouse").acceptsMouseOver_(true);
w.view.mouseOverAction_({arg ...args;
a.set(\pos, args[1].linlin(0,400,0,1), // posizione x
\amp,1-args[2].linlin(0,400,0,1.0)) // posizione y
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;a.free;b.free});
)
a = Synth.new(\scratch, [\buf,b]);
Server side ¶
Non è necessario creare una Window.
Tre UGen dedicate (solo control rate):
- MouseX - min, max, curva, tempo di lag
- MouseY - min, max, curva, tempo di lag
- MouseButton - min, max, tempo di lag
{[MouseX.kr(0,1,0,0.2), MouseX.kr(0.0001,1,1,0.2)]}.scope; // lineare vs esponenziale
{[MouseY.kr(0,1,0,5), MouseY.kr(0.0001,1,1,5)]}.scope; // lag time di 5 secondi...
{[MouseButton.kr(0,1,0.1), MouseButton.kr(0,1,)]}.scope; // lag time di 3 secondi...
Esempio audio
(
b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
SynthDef.new(\scratch, {arg gate=1, buf;
var sig, speed, env;
env = Env.new([0,1,0], [0.1, 0.1], \sin, 1).kr(20,gate);
speed = MouseX.kr(-10, 10);
speed = speed - DelayN.kr(speed, 0.1, 0.1);
speed = MouseButton.kr(1, 0, 0.3) + speed ;
sig = PlayBuf.ar(1, buf, speed * BufRateScale.kr(buf), loop: 1);
Out.ar(0, sig * env );
}).add;
)
a = Synth.new(\scratch, [\buf, b]);
a.set(\gate, 0); a.free; b.free;
Se vogliamo recuperare i valori nell'Interprete dobbiamo:
- sottocampionarli nel Server
- inviarli via OSC
(
SynthDef.new(\mouseval, {var x, y, click, sig;
x = MouseX.kr(0,1); // Panpot...
y = MouseY.kr(0.0001,1,1); // Ampiezza...no 0...
click = MouseButton.kr.round; // Note on/off
SendReply.kr(Impulse.kr(50), // rata di sottocampionamento
'/pos', // indirizzo o nome
[x, y, click]); // segnali da campionare
sig = Pan2.ar(SinOsc.ar*y*click,x*2-1); // Audio
Out.ar(0,sig)
}).add;
// ------------------------------ GUI
w = Window.new("mouse", Rect(0,0,210,210));
h = Slider2D.new(w, Rect(5,5,200,200));
h.knobColor_(Color.green);
w.front;
w.alwaysOnTop_(true);
w.onClose_({a.free;h.free;w.free});
// ------------------------------ OSC
OSCFunc.new({arg msg;
msg.postln; // stampa il messaggio OSC
{h.x_(msg[3]); // assegna i singoli valori
h.y_(msg[4]);
if(msg[5] == 1,
{h.knobColor_(Color.red)},
{h.knobColor_(Color.green)})
}.defer; // AppCLock per messaggi dinamici alla GUI
},
'/pos', // indirizzo o nome
s.addr); // eventuale NetAddr
)
a = Synth.new(\mouseval);
Tastiera ¶
Due modalità di controllo dei parametri che corrispondono alle tipologie di inviluppi.
- trigger - premendo e rilasciando un tasto velocemente (senza sostegno).
- switch - tenendo premuto un tasto per rilasciarlo successivamente (sostegno).
Client side ¶
Necessario creare una Window (GUI) con la quale interagire per evitare di scrivere involontariamente nel codice.
Diversi metodi per diverse azioni.
(
w = Window.new("key");
w.view.keyDownAction_({arg ...args;
args.postln;
args[1].postln
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free})
)
s.boot;
s.meter;
s.plotTree;
(
SynthDef.new(\if_1, {arg t_gate=0;
var sig, env;
sig = Saw.ar;
env = EnvGen.ar(Env.perc,t_gate);
Out.ar(0,sig*env);
}).add;
SynthDef.new(\if_2, {arg gate=0;
var sig, env;
sig = Pulse.ar;
env = EnvGen.ar(Env.adsr,gate);
Out.ar(0,sig*env);
}).add;
)
// Selettore singolo (senza sostegno)
(
w = Window.new("key");
w.view.keyDownAction_({arg ...args;
if(args[1]==$q, // uguale a...
{a.set(\t_gate,1)}) // se schiaccio 'q' (char)
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;a.free});
a = Synth(\if_1);
)
// Selettore doppio (con sostegno)
(
w = Window.new("key");
w.view.keyDownAction_({arg ...args;
if(args[1]!=$p, // diverso da...
{a.set(\gate,0)}, // se schiaccio qualsiasi altro tasto
{a.set(\gate,1)}) // se schiaccio 'p' (char)
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;a.free});
a = Synth.new(\if_2);
)
Tutte le possibilità offerte dalle strutture di controllo sono valide.
- Più tasti (switch) - selettore multiplo
// --------- switch(valore da comparare,
// valore_selettore, {func},
// valore_selettore, {func},
// valore_selettore, {func},
// ..., {...});
(
b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
SynthDef.new(\noise, {Out.ar(0,ClipNoise.ar*EnvGen.ar(Env.perc,doneAction:2))}).add;
SynthDef.new(\blip, {Out.ar(0,Blip.ar*EnvGen.ar(Env.triangle,doneAction:2))}).add;
)
(
w = Window.new("key");
w.view.keyDownAction_({arg ...args;
switch(args[1],
$q, {b.play; "SoundFile".postln},
$w, {Synth.new(\noise); "Noise".postln},
$e, {Synth.new(\blip); "Blip".postln});
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free});
)
- Counter - selettore sequenziale
(
~count = 0; // inizializza la variabile
w = Window.new("key");
w.view.keyDownAction_({arg ...args;
switch(args[3],
32, {~count = ~count + 1}, // tasto 'spazio' + 1
13, {~count = 0}); // tasto 'enter' resetta
~count.postln;
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free})
)
.keyUpAction¶
Identico al precedente ma valuta la funzione al rilascio di un tasto.
(
w = Window.new("key");
w.view.keyUpAction_({arg ...args;
args.postln;
args[1].postln
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free})
)
Se tasti tastiera $\rightarrow$ tasti pianoforte (tenendoli premuti) otteniamo dati ridondanti.
(
SynthDef.new(\if_1, {arg t_gate=0;
var sig, env;
sig = Saw.ar;
env = EnvGen.ar(Env.perc,t_gate);
Out.ar(0,sig*env);
}).add;
)
(
w = Window.new("key");
w.view.keyDownAction_({arg ...args;
if(args[1]==$q, {a.set(\t_gate,1)}); // 'q' = Note On
});
w.view.keyUpAction_({arg ...args;
if(args[1]==$q,{a.set(\t_gate,0)}); // Note Off
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;a.free});
a = Synth.new(\if_1);
)
Soluzione $\rightarrow$ filtro dei dati ridondanti.
(
var ora, prec = 0; // inizializza la variabile
w = Window.new("key");
w.view.keyDownAction_({arg ...args;
ora = args[3];
if((ora != prec), // se diverso dal precedente
{"noteOn".postln; // esegue e
prec = ora }); // riassegna 'prec' all'ultimo valore ricevuto
});
w.view.keyUpAction_({
prec = 0; // reset Array (altrimenti se si rischiaccia lo stesso tasto non funziona...)
"noteOff".postln; // Azione
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free})
)
Esempio sonoro
(
SynthDef.new(\if_1, {arg gate=0;
var sig, env;
sig = Saw.ar;
env = EnvGen.ar(Env.adsr,gate);
Out.ar(0,sig*env);
}).add;
)
(
~ora;
~prec = 0;
w = Window.new("key");
w.view.keyDownAction_({arg ...args; // 'q' = Note On
~ora = args[1];
if( // se uguale a 'q' AND diverso dal precedente
(~ora == $q).and(~ora != ~prec), {a.set(\gate,1);
~prec = ~ora})
});
w.view.keyUpAction_({ // Note Off
if(
~ora==$q, {a.set(\gate,0);
~prec = 0}); // Reset
});
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;a.free});
a = Synth.new(\if_1);
)
Server side ¶
Non è necessario creare una Window (ma consigliato).
- KeyState - keycode, minval, maxval, tempo_di_Lag
Dobbiamo conoscere prima il keycode.
(
w = Window.new;
w.front;
SynthDef.new(\key, {var amp,sig;
amp = KeyState.kr(38,0,1.0); // tasto 'j'
sig = SinOsc.ar * amp;
Out.ar(0,sig)
}).add;
)
Synth.new(\key);