Home

Mouse e tastiera¶

Indice¶

  • Intro
  • Mouse
    • Client side
    • Server side
  • Tastiera
    • Client side
    • Server side

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.

  1. view $\rightarrow$ la finestra sulla quale abbiamo cliccato
  2. x $\rightarrow$ il punto sull'asse x nella finestra in pixel (0 = punto più a sinistra)
  3. y $\rightarrow$ il punto sull'asse y nella finestra in pixel (0 = punto più in alto)
  4. modifiers $\rightarrow$ se c'è un tasto modificatore premuto
  5. botton n $\rightarrow$ numero del bottone premuto (0 = sinistro, 1 = destro, 2 = centrale)
  6. counter $\rightarrow$ il numero di click veloci (1 = singolo, 2 = doppio click)
In [ ]:
(
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.
In [ ]:
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.

In [ ]:
(
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.
In [ ]:
(
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.

In [ ]:
(
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.
In [ ]:
(
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.

In [ ]:
(
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)

In [ ]:
(
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
In [ ]:
{[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

In [ ]:
(
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
In [ ]:
(
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.

.keyDownAction¶

Valuta la funzione quando premiamo un tasto.

Restituisce un Array.

  1. view $\rightarrow$ la finestra che abbiamo creato
  2. char $\rightarrow$ il carattere del tasto premuto
  3. modifiers $\rightarrow$ se c'è un tasto modificatore premuto
  4. unicode $\rightarrow$ il valore unicode (ASCII)
  5. keycode $\rightarrow$ il valore keycode (JavaScript).
  6. key $\rightarrow$ in disuso.
In [ ]:
(
w = Window.new("key");
w.view.keyDownAction_({arg ...args;
	                          args.postln;
	                          args[1].postln
                       });
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free})
)

Mapping¶

Tre possibilità:

  • Due stati (if) - selettore singolo
In [ ]:
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
In [ ]:
// --------- 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
In [ ]:
(
~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.

In [ ]:
(
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.

In [ ]:
(
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.

In [ ]:
(
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

In [ ]:
(
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.

In [ ]:
(
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);