// Evaluate the block below to start the mapping MIDI -> OSC.
var on, off, cc;
var osc;


// Send OSC messages to Hydra
//var hydra = NetAddr.new("127.0.0.1", 3333);
//OSCFunc({ |msg, time, tidalAddr|
//	var latency = time - Main.elapsedTime;
//		msg = msg ++ ["time", time, "latency", latency];
//			msg.postln;
//				hydra.sendBundle(latency, msg)
//}, '/play2').fix;


osc = NetAddr.new("127.0.0.1", 6010);

MIDIClient.init;
MIDIIn.connectAll;

// Initial: sends velocity on note's channel
// Disabled in favor of launchpad control.
// on = MIDIFunc.noteOn({ |val, num, chan, src|
// 	osc.sendMsg("/ctrl", num.asString, val/127);
// });
// on = MIDIFunc.noteOn({ |val, num, chan, src|
// 	// it will be passed the arguments val, num, chan, and src, corresponding to the message value (e.g. velocity, control value, etc.)
// 	osc.sendMsg("/ctrl", "note", (num - 60));
// });
// 
// off = MIDIFunc.noteOff({ |val, num, chan, src|
// 	osc.sendMsg("/ctrl", "note", 0);
// });
// 
// cc = MIDIFunc.cc({ |val, num, chan, src|
// 	osc.sendMsg("/ctrl", num.asString, val/127);
// });
// 

// Simple MIDI->OSC bridge - passes CC numbers directly as control names
on = MIDIFunc.noteOn({ |val, num, chan, src|
	osc.sendMsg("/ctrl", num.asString, val/127);
});

off = MIDIFunc.noteOff({ |val, num, chan, src|
	osc.sendMsg("/ctrl", num.asString, 0);
});

cc = MIDIFunc.cc({ |val, num, chan, src|
	osc.sendMsg("/ctrl", num.asString, val/127);
});

if (~stopMidiToOsc != nil, {
	~stopMidiToOsc.value;
});

~stopMidiToOsc = {
	on.free;
	off.free;
	cc.free;
};

// Evaluate the line below to stop it.
// ~stopMidiToOsc.value;

// MIDI OUT
//~midiOut = MIDIOut.newByName("iO4", "iO4 iO4"); // Alesis iO4
//~midiOut = MIDIOut.newByName("HAPAX", "HAPAX MIDI 1");

// Load/compile all SynthDef files in the synthdefs directory
PathName("~/.local/share/SuperCollider/synthdefs").entries.do({ |file|
    if(file.extension == "scd", { file.fullPath.load });
});


(
// configure the sound server: here you could add hardware specific options
// see http://doc.sccode.org/Classes/ServerOptions.html
// Increased from 1024 * 256
s.options.numBuffers = 1024 * 256; // increase this if you need to load more samples
// Increased from 8192 * 32
s.options.memSize = 8192 * 32; // increase this if you get "alloc failed" messages
s.options.numWireBufs = 128; // increase if "exceeded number of wire buffer" messages
s.options.maxNodes = 1024 * 32; // increase this if you are getting drop outs and the message "too many nodes"
s.options.numOutputBusChannels = 12; // set this to your hardware output channel size, if necessary
s.options.numOutputBusChannels = 28; // MULTICHANNEL + tidal-ears test orbit
s.options.numInputBusChannels = 2; // set this to your hardware output channel size, if necessary
// boot the server and start SuperDirt
s.waitForBoot {
	~dirt = SuperDirt(2, s); // two output channels, increase if you want to pan across more channels
	~dirt.doNotReadYet = true; // Lazy-loading https://club.tidalcycles.org/t/superdirt-lazy-samples-loading/3148
	//~dirt.loadSoundFiles;   // load samples (path containing a wildcard can be passed in)
	// for example: ~dirt.loadSoundFiles("/Users/myUserName/Dirt/samples/*");
	~dirt.loadSoundFiles("/home/pln/.local/share/SuperCollider/downloaded-quarks/Dirt-Samples/*");
	~dirt.loadSoundFiles("/home/pln/Work/Sound/Samples/extra/*");

	// DRUM MACHINES
	~drumMachinesDir = PathName.new("/home/pln/Work/Sound/Samples/tidal-drum-machines/machines");
	~machines = ~drumMachinesDir.folders; //All drum machines
  	//*~machines = ['Linn9000','RolandTR909']; //Selected drum machines


	(
		~machines.do({
			arg machine;
			var folders = machine.folders;
			//postln("Loading machine: "++machine.folderName);
			folders.do({
				arg folder;
				~dirt.loadSoundFiles(folder.fullPath,namingFunction: { |x| x.basename.replace("-","")});
			});
		});
	);

	// s.sync; // optionally: wait for samples to be read
	//~dirt.start(57120, 0 ! 12);   // start listening on port 57120, create two busses each sending audio to channel 0
	~dirt.start(57120, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26]);   // +orbit 13 (bus 26) = tidal-ears test

	//~dirt.soundLibrary.addMIDI(\midi, ~midiOut); // Connect `midi` synth to iO4

	//~looper = TidalLooper(~dirt); // Start TidalLooper on top :)
	// ~looper.linput = 1;

	// optional, needed for convenient access from sclang:
	(
		~d1 = ~dirt.orbits[0]; ~d2 = ~dirt.orbits[1]; ~d3 = ~dirt.orbits[2];
		~d4 = ~dirt.orbits[3]; ~d5 = ~dirt.orbits[4]; ~d6 = ~dirt.orbits[5];
		~d7 = ~dirt.orbits[6]; ~d8 = ~dirt.orbits[7]; ~d9 = ~dirt.orbits[8];
		~d10 = ~dirt.orbits[9]; ~d11 = ~dirt.orbits[10]; ~d12 = ~dirt.orbits[11];
	);

  // Setup SCLOrK Synths (installed via Quark)
  SCLOrkSynths.load;

	// =============================================
	// Mutable Instruments Global Effects
	// https://club.tidalcycles.org/t/mutable-instruments-ugens/2730/22
	// =============================================

	// MiVerb - global reverb per orbit
	(1..SuperDirt.maxSampleNumChannels).do { |numChannels|
		SynthDef("global_mi_verb" ++ numChannels, { |dryBus, effectBus, verbwet=0, verbtime=0.7, verbdamp=0.5, verbhp=0.05, verbfreeze=0, verbdiff=0.625, verbgain=0|
			var signal = In.ar(dryBus, ~dirt.numChannels);
			signal = MiVerb.ar(signal, verbtime, verbwet, verbdamp, verbhp, verbfreeze, verbdiff);
			Out.ar(effectBus, signal * verbgain);
		}, [\ir, \ir]).add;
	};

	// MiClouds - global granular processor per orbit
	(1..SuperDirt.maxSampleNumChannels).do { |numChannels|
		SynthDef("global_mi_clouds" ++ numChannels, { |dryBus, effectBus, cloudspitch=0, cloudspos=0.5, cloudssize=0.25, cloudsdens=0.4, cloudstex=0.5, cloudswet=0, cloudsgain=0, cloudsspread=0.5, cloudsrvb=0, cloudsfb=0, cloudsfreeze=0, cloudsmode=0, cloudslofi=0|
			var signal = In.ar(dryBus, ~dirt.numChannels);
			signal = MiClouds.ar(signal, cloudspitch, cloudspos, cloudssize, cloudsdens, cloudstex, cloudswet, 1, cloudsspread, cloudsrvb, cloudsfb, cloudsfreeze, cloudsmode, cloudslofi);
			Out.ar(effectBus, signal * cloudsgain);
		}, [\ir, \ir]).add;
	};

	// MiRipples - global resonant filter per orbit
	(1..SuperDirt.maxSampleNumChannels).do { |numChannels|
		SynthDef("global_mi_ripples" ++ numChannels, { |dryBus, effectBus, ripplescf=0.5, ripplesreson=0, ripplesdrive=1, ripplesgain=0|
			var signal = In.ar(dryBus, ~dirt.numChannels);
			signal = MiRipples.ar(signal, ripplescf, ripplesreson, ripplesdrive);
			Out.ar(effectBus, signal * ripplesgain);
		}, [\ir, \ir]).add;
	};

	// Register global effects on all orbits
	~dirt.orbits.do { |x|
		var verb = GlobalDirtEffect(\global_mi_verb, [\verbwet, \verbtime, \verbdamp, \verbhp, \verbfreeze, \verbdiff, \verbgain]);
		var clouds = GlobalDirtEffect(\global_mi_clouds, [\cloudspitch, \cloudspos, \cloudssize, \cloudsdens, \cloudstex, \cloudswet, \cloudsgain, \cloudsspread, \cloudsrvb, \cloudsfb, \cloudsfreeze, \cloudsmode, \cloudslofi]);
		var ripples = GlobalDirtEffect(\global_mi_ripples, [\ripplescf, \ripplesreson, \ripplesdrive, \ripplesgain]);
		x.globalEffects = x.globalEffects.addFirst(verb).addFirst(clouds).addFirst(ripples);
		x.initNodeTree;
	};
};

s.volume = 2; // +6dB — SuperDirt defaults are conservative
StageLimiter.activate; // BatLib safety limiter on master out
s.latency = 0.3; // increase this if you get "late" messages
);
