// --------------- MIDI Input

// MIDIClient.init

// MIDIIn.connect

// initialize and connect the first 8 MIDI IN ports
(
var ins;
MIDIClient.init(8,8);	
ins = MIDIClient.sources.size;	
ins.do({ arg i;  MIDIIn.connect(i, MIDIClient.sources.at(i)) });
)

// a function that responds to any MIDI ControlChange message:
MIDIIn.control = { arg src, chan, num, val; [chan, num, val].postln };

// a function that responds to any NoteOn message:
MIDIIn.noteOn = { arg src, chan, num, vel; ["NoteOn:" , chan, num, vel].postln };

// a function that responds to any NoteOff message:
MIDIIn.noteOff = { arg src, chan, num, vel; ["NoteOff:", chan, num, vel].postln };

// a ControlChange example:

(
SynthDef("testmidi", { arg freq=400, rf=800, amp=0.2;
	var sig, filt;
	sig = Saw.ar(freq*[0.997, 1.004], amp);
	filt = RLPF.ar(sig, rf, 0.1);
	Out.ar(0, filt);
	}, [0.1, 0.1, 0.1]).send(s);
)	

y = Synth.new("testmidi");
//y.set(\amp, 0.01);
//y.free

( // controller number 101 controls the freq of the synth:
MIDIIn.control = { arg src, chan, num, val; 
	[chan, num, val].postln; 
	if(num == 101) { y.set(\freq, val / 127 * 1000 + 100 ) }; 
	};
)	

y.free

y = Synth.new("testmidi");

( // different controllers for different parameters of this synth:
MIDIIn.control = { arg src, chan, num, val; 
	[chan, num, val].postln; 
	switch ( num )
		{ 101 }  { y.set(\freq, (val / 127 * 80).midicps ) } 
		{ 102 }  { y.set(\rf, val / 127 * 2000 + 100 ) } 
		{ 103 }  { y.set(\amp, (val * 0.5 - 60).dbamp  ) } 
		{ 104 } 	 { y.run( val >= 64 ) }
		};
)	

// an alternative way using MIDI responders:

~ccfr = CCResponder({ arg src, chan, num, val;  y.set(\freq, (val / 127 * 80).midicps ) }, nil, nil, 101, nil);

~ccrf = CCResponder({ arg src, chan, num, val;  y.set(\rf, val / 127 * 2000 + 100 ) }, nil, nil, 102, nil)

~ccamp = CCResponder({ arg src, chan, num, val;  y.set(\amp, (val * 0.5 - 60).dbamp ) }, nil, nil, 103, nil)

~ccmute = CCResponder({ arg src, chan, num, val;  y.run( val >= 64 ) }, nil, nil, 104, nil)

CCResponder.removeAll

// a NoteOn/NoteOff example:

( // preparations:
MIDIIn.connect;
//s = Server.local;
//s.boot;
s.latency = 0;

SynthDef("sik-goo", { arg freq=440,formfreq=100,gate=0.0,bwfreq=800;
	var x;
	x = Formant.ar(
			SinOsc.kr(0.02, 0, 10, freq), 
			formfreq,
			bwfreq
			);
	x = EnvGen.kr(Env.adsr, gate,Latch.kr(gate,gate)) * x;
	Out.ar(0, x);
}).send(s);
)

( // the synth and the MIDI-In functions:
x = Synth("sik-goo");

//set the action:
MIDIIn.noteOn = {arg src, chan, num, vel;
				x.set(\freq, num.midicps / 4.0);
				x.set(\gate, vel / 200 );
				x.set(\formfreq, vel / 127 * 1000);
			};
MIDIIn.noteOff = { arg src,chan,num,vel;
				x.set(\gate, 0.0);
			};
MIDIIn.bend = { arg src,chan,val;
				//(val * 0.048828125).postln;
				x.set(\bwfreq, val * 0.048828125 );
			};
)

// ----------------- MIDI Output

MIDIClient.init(8,8);

// m = MIDIOut(0, MIDIClient.destinations.at(1).uid);
m = MIDIOut.newByName("SimpleSynth virtual input", "SimpleSynth virtual input")

m.noteOn(0, 67, 60);
m.noteOn(0, 67, 00);
m.allNotesOff(0)

(
e = Pbind(
	\type, \midi,
	\midiout, m,
	\tempo, 130 * 1/60/4,
	\dur, Pfunc({ coin(0.9).if(1/16, 1/2) }),
	\midinote, Pxrand( (40, 45 .. 75), inf),
	\bend, 64,
	\legato, 1.0,
	\amp, 0.7
	).play;
)

e.stop;

( // strumming (try any guitar sound):
e = Pbind(
	\type, \midi,
	\midiout, m,
	\tempo, 130 * 1/60/4,
	\dur, 1.0,
	\strum, 0.02,
	\midinote, Pseq( [[ 40, 45, 50, 55, 59, 64]], inf),
	\bend, 64,
	\legato, 0.9,
	\amp, 0.7
	).play;
)

e.stop;

// ----------------- HID ------------

GeneralHID.scheme

// (OSX -> MXHID -> HIDDeviceService )

// initializing the device list:
GeneralHID.buildDeviceList;

// what do we have:
GeneralHID.postDevices
GeneralHID.postDevicesAndProperties

// open a device:
a = GeneralHID.open( GeneralHID.deviceList[2] )   // get a GeneralHIDDevice from the list

a.info; 		// ( product, 0, vendorID, productID, version )
a.caps;		// display the capabilities of the device	

a.slots.dump  // slots are the interface elements of the device
a.slots.do { arg sl;  sl.postln }

// start listening
GeneralHID.startEventLoop

// See if data is coming in:
a.debug_( true );   // [ slot.type, slot.code, slot.value ]

// Stop it:
a.debug_( false );

// applying an action to an element:
a.slots[1][1].action = { arg v; if(v.value > 0) { "button1 on".postln } { "button1 off".postln }    }

// using the elements to control synth arguments:
y = Synth("default").run(false);
// button 1 on the gamepad controls the run state of synth y:
a.slots[1][1].action = { arg v; if(v.value > 0) { y.run(true) } { y.run(false) }    }
// left joystick (y-axis) controls the freq of synth y:
a.slots[3][49].action = { arg v; y.set(\freq, v.value.linlin(0,1,200, 2000))    }

// stop the event loop:
GeneralHID.stopEventLoop
a.close;

/*  
device = GeneralHID.open( ..)
set device slot actions
GeneralHID.startEventLoop

GeneralHID.stopEventLoop
remove actions
device.close

*/