/*******************************************************************************
*  Copyright 2007 - 2011, Philip S. Considine                                  *
*  This program is free software: you can redistribute it and/or modify        *
*  it under the terms of the GNU General Public License as published by        *
*  the Free Software Foundation, either version 3 of the License, or           *
*  (at your option) any later version.                                         *
*                                                                              *
*  This program is distributed in the hope that it will be useful,             *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                *
*  GNU General Public License (http://www.gnu.org/licenses/)for more details.  *
*******************************************************************************/

desc: LFO Controlled MIDI CC Generator

////////////////////////////////////////////////////////////////////////////////
slider1:0<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>MIDI Channel
slider2:1<0,127,1{0 Bank Sel M,1 Mod Wheel M,2 Breath M,3,4 Foot P M,5 Porta M,6 Data Entry M,7 Vol M,8 Balance M,9,10 Pan M,11 Expression M,12 Ctrl 1 M,13 Ctrl 2 M,14,15,16 GP Slider 1,17 GP Slider 2,18 GP Slider 3,19 GP Slider 4,20,21,22,23,24,25,26,27,28,29,30,31,32 Bank Sel L,33 Mod Wheel L,34 Breath L,35,36 Foot P L,37 Porta L,38 Data Entry L,39 Vol L,40 Balance L,41,42 Pan L,43 Expression L,44 Ctrl 1 L,45 Ctrl 2 L,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 Hold P sw,65 Porta sw,66 Sustenuto sw,67 Soft P sw,68 Legato P sw,69 Hold 2 P sw,70 S.Variation,71 S.Timbre,72 S.Release,73 S.Attack,74 S.Brightness,75 S.Ctrl 6,76 S.Ctrl 7,77 S.Ctrl 8,78 S.Ctrl 9,79 S.Ctrl 10,80 GP B.1 sw,81 GP B.2 sw,82 GP B.3 sw,83 GP B.4 sw,84,85,86,87,88,89,90,91 Effects Lv,92 Trem Lv,93 Chorus Lv,94 Celeste Lv,95 Phaser Lv,96 Data B. Inc,97 Data B. Dec,98 NRP L,99 NRP M,100 RP L,101 RP M,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127}>Controller
slider3:0<0,127,1>Centre
slider4:0<0,127,1>Range (+/-)
slider5:0<0,127,1>Off Value
slider6:0<0,0,1{Sine}>LFO Shape
slider7:1<0,32,0.1>LFO Frequency
slider8:0<0,1,1{Hertz,Beats}>LFO Units
slider9:6<0,9,1{1,2,4,8,16,32,64,128,256,512}>Updates Per Beat
slider10:1<0,1,1{Off,On}>On/Off

////////////////////////////////////////////////////////////////////////////////
@init
statController	= $xB0;

////////////////////////////////////////////////////////////////////////////////
@slider

slider3 = min(max(slider3 | 0, 0), 127);	// Remove fractions and clamp to legal range
slider4 = min(max(slider4 | 0, 0), 127); 
slider5 = min(max(slider5 | 0, 0), 127);

slider7 = min(max(slider7, 0), 32);

channel = slider1;
target = slider2;
centre = slider3;
range = slider4;
offValue = slider5;
shape = slider6;
freq = slider7;
units = slider8;
updateFreq = 2 ^ slider9;

// Reset if necessary
slider10 != on ?
(
	on = slider10;
	t = 0;
	updateCounter = 0;
);

////////////////////////////////////////////////////////////////////////////////
@block
updateSamples = srate * 60 / tempo / updateFreq;

units == 0 ? // Hz
(
	oscTemp = 2 * $pi * freq;
)
:
units == 1 ? // Beats
(
	oscTemp = 2 * $pi * tempo / 60 / freq;
);

on ?
(
	active = 1;

	updateCounter + samplesblock >= updateSamples ?
	(	
		updateSamples <= samplesblock ?
		(			
			// Possibly more than one update in the block
			samplesLeft = samplesblock;
			offset = 0;
			while
			(
				offset += updateSamples - updateCounter;
			
				t += (updateSamples - updateCounter)/srate;
				
				// Sine
				shape == 0 ? value = centre + floor(range * sin(oscTemp * t));
				
				value = min(max(value, 0), 127);
				
				midisend(offset, statController + channel, target | (value << 8));
				samplesLeft -= updateSamples - updateCounter;
				updateCounter = 0;
				
				samplesLeft >= updateSamples; // Loop condition
			);
			updateCounter = samplesLeft;
			t += updateCounter / srate;
		)
		:
		(			
			// Block is smaller than updateSamples, just one update
			offset = updateCounter + samplesblock - updateSamples;
						
			t += offset / srate;
			
			// Sine
			shape == 0 ? value = centre + floor(range * sin(oscTemp * t));
			
			value = min(max(value, 0), 127);
				
			midisend(offset, statController + channel, target | (value << 8));
			updateCounter = samplesblock - offset;
			t += updateCounter/srate;
		);
	)
	:
	(
		// Just update counters
		updateCounter += samplesblock;
		t += samplesblock /srate;
	);
)
:
(
	// Send off value only if not already sent
	active ?
	(
		midisend(offset, statController + channel, target | (offValue << 8));
		active = 0;
	);
);