;Written by Iain McCurdy, 2010

<CsoundSynthesizer>

<CsOptions>
-odevaudio -b400
</CsOptions>

<CsInstruments>

sr 		= 	44100	;SAMPLE RATE
ksmps 		= 	100	;CONTROL PERIOD
nchnls 		= 	2	;NUMBER OF CHANNELS (2=STEREO)
0dbfs		=	1	;MAXIMUM AMPLITUDE

;FLTK INTERFACE CODE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FLcolor	180, 180, 255, 0, 0, 0
;		LABEL             | WIDTH | HEIGHT | X | Y
	FLpanel	"Bassline TB303",   1000,    320,    0,  0

;SWITCHES					ON | OFF | TYPE | WIDTH | HEIGHT | X | Y | OPCODE | INS | STARTTIM | IDUR
gkOnOff,ihOnOff	FLbutton	"Start/Stop",	1,   0,    22,    120,     40,     5,  5,    0,      3,      0,      -1
FLsetColor2	255, 255, 50, ihOnOff	;SET SECONDARY COLOUR TO YELLOW

;NUMBER DISPLAY BOXES 		WIDTH | HEIGHT | X | Y 
idVol		FLvalue "", 	70,       20,   30, 145
idCfBase	FLvalue "", 	70,       20,  105, 145
idCfEnv		FLvalue "", 	70,       20,  180, 145
idRes		FLvalue "", 	70,       20,  255, 145
idDecay		FLvalue "", 	70,       20,  330, 145
idDist		FLvalue "", 	70,       20,  405, 145

;KNOBS				LABEL     | MIN | MAX | EXP | TYPE |  DISP   | WIDTH | X | Y
gkVol, ihVol		FLknob	"Volume",    0,    5,    0,    1,   idVol,      70,    30, 60
gkCfBase, ihCfBase	FLknob	"Cutoff",    4,   12,    0,    1,   idCfBase,   70,   105, 60
gkCfEnv, ihCfEnv	FLknob	"Envelope",  0,   12,    0,    1,   idCfEnv,   70,   180, 60
gkRes, ihRes		FLknob	"Resonance", 0, 0.98,    0,    1,   idRes,      70,   255, 60
gkDecay, ihDecay	FLknob	"Decay",     -10, 10,    0,    1,   idDecay,    70,   330, 60
gkDist, ihDist		FLknob	"Distortion",0,    1,    0,    1,   idDist,     70,   405, 60

;COUNTERS					MIN | MAX | STEP1 | STEP2 | TYPE | WIDTH | HEIGHT |  X  | Y | OPCODE
gkTempo, ihTempo	FLcount  "Tempo",	1,    500,    1,    10,     1,    150,     30,   150,  5,   -1

FLsetVal_i	2, ihVol
FLsetVal_i	6, ihCfBase
FLsetVal_i	6.5, ihCfEnv
FLsetVal_i	0.5, ihRes
FLsetVal_i	0, ihDecay
FLsetVal_i	0.2, ihDist
FLsetVal_i	110, ihTempo

;DELAY----------------------------------------------------------------------------------------------------------------
;COUNTERS						MIN | MAX | STEP1 | STEP2 | TYPE | WIDTH | HEIGHT |  X  | Y | OPCODE
gkDelTimL, ihDelTimL	FLcount  "Delay Time L",	1,     8,    1,      10,     2,      85,     20,    520,  15,   -1
gkDelTimR, ihDelTimR	FLcount  "Delay Time R",	1,     8,    1,      10,     2,      85,     20,    615,  15,   -1

;KNOBS				LABEL        | MIN | MAX | EXP | TYPE | DISP | WIDTH | X | Y
gkDelSnd, ihDelSnd	FLknob	"Delay Send",   0,    1,    0,    1,     -1,    45,   525, 55
gkDelFB, ihDelFB	FLknob	"Delay F.B.",   0,    1,    0,    1,     -1,    45,   585, 55

FLsetVal_i	3, ihDelTimL
FLsetVal_i	5, ihDelTimR
FLsetVal_i	0.2, ihDelSnd
;---------------------------------------------------------------------------------------------------------------------

;REVERB---------------------------------------------------------------------------------------------------------------
;KNOBS				LABEL         | MIN | MAX | EXP | TYPE | DISP | WIDTH | X | Y
gkRvbSnd, ihRvbSnd	FLknob	"Reverb Send",   0,    1,    0,    1,     -1,    45,   725, 55
gkRvbFB, ihRvbFB	FLknob	"Reverb F.B.",   0,    1,    0,    1,     -1,    45,   785, 55

FLsetVal_i	0.2, ihRvbSnd
FLsetVal_i	0.8, ihRvbFB
;---------------------------------------------------------------------------------------------------------------------

#define	STEP(N)
#
;KNOBS				LABEL | MIN | MAX | EXP | TYPE | DISP | WIDTH | X | Y
gkNote$N, ihNote$N	FLknob	"$N",    5,   11,    0,    1,     -1,    50,   ix,  iy+10

;TEXT INPUT		 		MIN | MAX | STEP | TYPE | WIDTH | HEIGHT | X  | Y
gkpch$N, gihpch$N	FLtext	"", 	0,     20,    0,     1,     50,     20,  ix+2,  iy+75

;SWITCHES					ON | OFF | TYPE | WIDTH | HEIGHT |  X   |   Y    | OPCODE | INS | STARTTIM | IDUR
gkOn$N,ihOn$N		FLbutton	"",	1,    0,    3,     15,      15,   ix+17,  iy+100,    0,      1,      0,       45
gkHold$N,ihHold$N	FLbutton	"",	1,    0,    3,     15,      15,   ix+17,  iy+120,    0,      1,      0,       45

ix = ix + igap
;	FLsetVal_i	8, ihNote$N
#
ix = 45
iy = 170
igap = 60
$STEP(1)
$STEP(2)
$STEP(3)
$STEP(4)
$STEP(5)
$STEP(6)
$STEP(7)
$STEP(8)
$STEP(9)
$STEP(10)
$STEP(11)
$STEP(12)
$STEP(13)
$STEP(14)
$STEP(15)
$STEP(16)

;TEXT BOXES					TYPE | FONT | SIZE | WIDTH | HEIGHT | X |  Y
ih		 	FLbox  	"Pitch", 	1,       1,    13,     40,      20,   5,   iy+25
ih		 	FLbox  	"On/Off", 	1,       1,    13,     40,      20,  20,   iy+95
ih		 	FLbox  	"Hold", 	1,       1,    13,     40,      20,  20,   iy+117

FLsetVal_i	8.01, gihpch1
FLsetVal_i	8.06, gihpch2
FLsetVal_i	7.09, gihpch3
FLsetVal_i	8.10, gihpch4
FLsetVal_i	7.08, gihpch5
FLsetVal_i	7.11, gihpch6
FLsetVal_i	8.00, gihpch7
FLsetVal_i	8.02, gihpch8
FLsetVal_i	7.00, gihpch9
FLsetVal_i	8.09, gihpch10
FLsetVal_i	7.02, gihpch11
FLsetVal_i	7.03, gihpch12
FLsetVal_i	7.06, gihpch13
FLsetVal_i	8.05, gihpch14
FLsetVal_i	8.09, gihpch15
FLsetVal_i	8.01, gihpch16

FLsetVal_i	octpch(7.01), ihNote1
FLsetVal_i	octpch(8.06), ihNote2
FLsetVal_i	octpch(6.09), ihNote3
FLsetVal_i	octpch(8.10), ihNote4
FLsetVal_i	octpch(7.08), ihNote5
FLsetVal_i	octpch(7.11), ihNote6
FLsetVal_i	octpch(8.00), ihNote7
FLsetVal_i	octpch(6.02), ihNote8
FLsetVal_i	octpch(7.00), ihNote9
FLsetVal_i	octpch(6.09), ihNote10
FLsetVal_i	octpch(7.02), ihNote11
FLsetVal_i	octpch(8.03), ihNote12
FLsetVal_i	octpch(7.06), ihNote13
FLsetVal_i	octpch(6.05), ihNote14
FLsetVal_i	octpch(6.09), ihNote15
FLsetVal_i	octpch(6.01), ihNote16

FLsetVal_i	1, ihOn1
FLsetVal_i	0, ihOn2
FLsetVal_i	0, ihOn3
FLsetVal_i	1, ihOn4
FLsetVal_i	1, ihOn5
FLsetVal_i	1, ihOn6
FLsetVal_i	0, ihOn7
FLsetVal_i	1, ihOn8
FLsetVal_i	1, ihOn9
FLsetVal_i	0, ihOn10
FLsetVal_i	1, ihOn11
FLsetVal_i	0, ihOn12
FLsetVal_i	1, ihOn13
FLsetVal_i	1, ihOn14
FLsetVal_i	0, ihOn15
FLsetVal_i	1, ihOn16

FLsetVal_i	0, ihHold1
FLsetVal_i	1, ihHold2
FLsetVal_i	0, ihHold3
FLsetVal_i	0, ihHold4
FLsetVal_i	1, ihHold5
FLsetVal_i	1, ihHold6
FLsetVal_i	0, ihHold7
FLsetVal_i	0, ihHold8
FLsetVal_i	1, ihHold9
FLsetVal_i	0, ihHold10
FLsetVal_i	1, ihHold11
FLsetVal_i	0, ihHold12
FLsetVal_i	1, ihHold13
FLsetVal_i	1, ihHold14
FLsetVal_i	0, ihHold15
FLsetVal_i	0, ihHold16

;GENERAL_TEXT_SETTINGS			SIZE | FONT |  ALIGN | RED | GREEN | BLUE
			FLlabel		13,      1,      3,    180,   180,   255		;LABELS MADE INVISIBLE (I.E. SAME COLOR AS PANEL)

;BUTTON BANKS					TYPE | NUMX | NUMY | WIDTH | HEIGHT | X | Y | OPCODE | INS | STARTTIM | DUR
gkWaveform, ihWaveform		FLbutBank	14,     1,     2,     18,      40,   370,10,   -1

;GENERAL_TEXT_SETTINGS			SIZE | FONT |  ALIGN | RED | GREEN | BLUE
			FLlabel		13,      1,      3,     0,     0,     0			;LABELS MADE VISIBLE AGAIN

;TEXT BOXES						TYPE | FONT | SIZE | WIDTH | HEIGHT | X |  Y
ih		 	FLbox  	"Input", 		1,       5,    14,     50,      20, 320,  10
ih		 	FLbox  	"Sawtooth", 		1,       5,    12,     60,      20, 390,  10
ih		 	FLbox  	"Square  ", 		1,       5,    12,     60,      20, 390,  30

FLpanel_end

;INSTRUCTIONS AND INFO PANEL
				FLpanel	" ", 1000, 200, 0, 370
;TEXT BOXES												                                                      		TYPE | FONT | SIZE | WIDTH | HEIGHT | X | Y
ih		 	FLbox  	"                                             Bassline (requires Csound 5.12 or higher)                                      ", 	1,      5,     14,    995,    15,     5,   0
ih		 	FLbox  	"----------------------------------------------------------------------------------------------------------------------------", 	1,      5,     14,    995,    15,     5,  20
ih		 	FLbox  	"This example is an emulation of a Roland TB303 Bassline step synthesizer. It makes use of Csound's looping envelopes lpshold", 	1,      5,     14,    995,    15,     5,  40
ih		 	FLbox  	"loopseg and looptseg to loop pitch, note on/off status, hold status and filter envelopes for each note. The filter used is  ", 	1,      5,     14,    995,    15,     5,  60
ih		 	FLbox  	"Csound's moogladder filter (this can be swapped with moogvcf is real-time performance is an issue). Distortion is           ", 	1,      5,     14,    995,    15,     5,  80
ih		 	FLbox  	"implemented using the 'clip' opcode. The pitch of each step is controlled by its own knob, the values of these are expressed", 	1,      5,     14,    995,    15,     5, 100
ih		 	FLbox  	"in Csound 'pch' format - middle C = 8.00, G above middle C = 8.07, C above middle C = 9.00 and so on.                       ", 	1,      5,     14,    995,    15,     5, 120
ih		 	FLbox  	"Each step has its own on/off switch and 'hold' switch. The hold function defeats a retriggering of the filter envelope to   ", 	1,      5,     14,    995,    15,     5, 140
ih		 	FLbox  	"create a smoother transition between notes. A stereo delay and reverb effect are included. Delay times are defined in       ", 	1,      5,     14,    995,    15,     5, 160
ih		 	FLbox  	"semitones This example is a work in progress.                                                                               ", 	1,      5,     14,    995,    15,     5, 180
				FLpanel_end

FLrun	;RUN THE FLTK WIDGET THREAD

;END OF FLTK INTERFACE CODE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

gisine	ftgen	0, 0, 8192, 10, 1

instr	1	;SENSE NOTE KNOBS
	kporttime	linseg	0,0.01,0.1
#define	NOTE(N)
	#
	koct$N	=	int(gkNote$N)		;DERIVE OCTAVE
	ksemi$N	=	frac(gkNote$N) * 0.12	;DERIVE PCH FORMAT INTERVAL ABOVE OCTAVE
	ksemi$N	=	int(ksemi$N*100) * 0.01	;CLEAN TO 2 DECIMAL PLACES
	kNote$N	=	koct$N + ksemi$N	;ADD OCTAVE TO SEMITONES
	ktrig$N	changed	kNote$N			;IF PCH HAS CHANGED GENERATE A TRIGGER
	if ktrig$N=1	then			;IF PCH VALUE HAS CHANGED...
		event "i", 2, 0, 0.01, kNote$N, $N
		ktrig$N=0			;PREVENT REITERATION
	endif					;END OF THIS CONDITIONAL BRANCH
	#
	$NOTE(1)				;EXPAND MACRO
	$NOTE(2)				;EXPAND MACRO
	$NOTE(3)				;EXPAND MACRO
	$NOTE(4)				;EXPAND MACRO
	$NOTE(5)				;EXPAND MACRO
	$NOTE(6)				;EXPAND MACRO
	$NOTE(7)				;EXPAND MACRO
	$NOTE(8)				;EXPAND MACRO
	$NOTE(9)				;EXPAND MACRO
	$NOTE(10)				;EXPAND MACRO
	$NOTE(11)				;EXPAND MACRO
	$NOTE(12)				;EXPAND MACRO
	$NOTE(13)				;EXPAND MACRO
	$NOTE(14)				;EXPAND MACRO
	$NOTE(15)				;EXPAND MACRO
	$NOTE(16)				;EXPAND MACRO
endin

instr	2	;UPDATE NOTE VALUE DISPLAYS
#define	PRINTVAL(N)
	#
	if p5=$N then
		FLsetVal_i	p4, gihpch$N
	endif
	#
	$PRINTVAL(1)
	$PRINTVAL(2)
	$PRINTVAL(3)
	$PRINTVAL(4)
	$PRINTVAL(5)
	$PRINTVAL(6)	
	$PRINTVAL(7)	
	$PRINTVAL(8)	
	$PRINTVAL(9)	
	$PRINTVAL(10)	
	$PRINTVAL(11)	
	$PRINTVAL(12)	
	$PRINTVAL(13)	
	$PRINTVAL(14)	
	$PRINTVAL(15)	
	$PRINTVAL(16)
endin


instr 3	;Bassline instrument
	if gkOnOff=0 then
		turnoff
	endif
	kPhFreq   =            gkTempo/240	; frequency with which to repeat the entire phrase
	kBtFreq   =            (gkTempo)/15	; frequency of each 1/16th note
	
	;envelopes with held segments        note:1         2         3         4         5         6         7         8         9         10         11         12         13         14         15         16           DUMMY
	kPch      lpshold      kPhFreq, 0,        0,gkpch1, 1,gkpch2, 1,gkpch3, 1,gkpch4, 1,gkpch5, 1,gkpch6, 1,gkpch7, 1,gkpch8, 1,gkpch9, 1,gkpch10, 1,gkpch11, 1,gkpch12, 1,gkpch13, 1,gkpch14, 1,gkpch15, 1,gkpch16,   1,gkpch1; need an extra 'dummy' value
	kOn       lpshold      kPhFreq, 0,        0,gkOn1,  1,gkOn2,  1,gkOn3,  1,gkOn4,  1,gkOn5,  1,gkOn6,  1,gkOn7,  1,gkOn8,  1,gkOn9,  1,gkOn10,  1,gkOn11,  1,gkOn12,  1,gkOn13,  1,gkOn14,  1,gkOn15,  1,gkOn16,     1,1
	kHold     lpshold      kPhFreq, 0,        0,gkHold1,1,gkHold2,1,gkHold3,1,gkHold4,1,gkHold5,1,gkHold6,1,gkHold7,1,gkHold8,1,gkHold9,1,gkHold10,1,gkHold11,1,gkHold12,1,gkHold13,1,gkHold14,1,gkHold15,1,gkHold16,   1,0; need an extra 'dummy' value

	kHold     vdel_k       kHold, 1/kBtFreq, 1; offset hold by 1/2 note duration
	kOct      portk        octpch(kPch), 0.01*kHold; apply portamento to pitch changes - if note is not held, no portamento will be applied
	
	; amplitude envelope
	if kHold=0 then; if hold status is 'off'...
		;                                         attack     sustain        decay
		kAmpEnv   loopseg      kBtFreq, 0,   0,   0,0.1,  1, 60/gkTempo, 1,  0.1,0	; sustain segment duration (and therefore attack and decay segment durations) are dependent upon tempo
	else
		kAmpEnv   =            1	; if hold status is 'on', ignore amplitude envelope, replace with a constant value of '1'
	endif
	
	;filter envelope
	kCfOct    init         i(gkCfBase)	; give kCfOct and initial value
	if kHold=0 then				; if hold is off
		; create a filter cutoff frequency envelope
		kCfOct    looptseg      kBtFreq, 0, 0, gkCfBase+gkCfEnv, gkDecay, 1, gkCfBase	;5.12 OR GREATER OPTION
		;kCfOct    loopseg      kBtFreq, 0, 0, gkCfBase+gkCfEnv, 1, gkCfBase		;PRE-VERSION 5.12 OPTION - DECAY CONTROL WILL NOT WORK WITH THIS OPTION
	endif
	kCfOct    limit        kCfOct, 4, 14	; limit the cutoff frequency to be within sensible limits
	;kCfOct    port         kCfOct, 0.05	; smooth the cutoff frequency envelope with portamento
	
	kWavTrig  changed      gkWaveform		; generate a 'bang' if waveform selector changes
	if kWavTrig=1 then				; if a 'bang' has been generated...
		reinit REINIT_VCO			; begin a reinitialization pass from the label 'REINIT_VCO'
	endif
	REINIT_VCO:; a label
	aSig      vco2         0.2, cpsoct(kOct), i(gkWaveform)*2, 0.5	; generate audio using VCO oscillator
	rireturn							; return from initialization pass to performance passes

	aSig      moogladder   aSig, cpsoct(kCfOct), gkRes	; filter audio 
	;acf	interp	cpsoct(kCfOct)
	;aSig      moogvcf   aSig, cpsoct(kCfOct), gkRes 	;use moogvcf is CPU is struggling with moogladder
	;aSig      moogvcf   aSig, acf, gkRes 	;use moogvcf is CPU is struggling with moogladder
	
	; distortion
	iSclLimit ftgentmp     0, 0, 1024, -16, 1, 1024,  -8, 0.01	; rescaling curve for clip 'limit' parameter
	iSclGain  ftgentmp     0, 0, 1024, -16, 1, 1024,   4, 10	; rescaling curve for gain compensation
	kLimit    table        gkDist, iSclLimit, 1			; read Limit value from rescaling curve
	kGain     table        gkDist, iSclGain, 1			;  read Gain value from rescaling curve
	kTrigDist changed      kLimit					; if limit value changes generate a 'bang'
	if kTrigDist=1 then						; if a 'bang' has been generated...
		reinit REINIT_CLIP					; begin a reinitialization pass from label 'REINIT_CLIP'
	endif
	REINIT_CLIP:
	aSig      clip         aSig, 0, i(kLimit)		; clip distort audio signal
	rireturn						;
	aSig      =            aSig * kGain			; compensate for gain loss from 'clip' processing
	kOn       port         kOn, 0.006			;
	aSig	=	aSig * kAmpEnv * gkVol * kOn
		outs	aSig, aSig     	; audio sent to output, apply amp. envelope, volume control and note On/Off status
	gaDelSnd	=	aSig * gkDelSnd
	gaRvbSndL	=	aSig * gkRvbSnd
	gaRvbSndR	=	gaRvbSndL
endin

instr	10	;Delay
	kDelTimL	limit	(gkDelTimL * 60) / (gkTempo * 4), 0.001, 9.99
	kDelTimR	limit	(gkDelTimR * 60) / (gkTempo * 4), 0.001, 9.99
	aDelTimL	interp	kDelTimL
	aDelTimR	interp	kDelTimR
	aBuffer		delayr	10
	atapL		deltap3	aDelTimL
			delayw	gaDelSnd + (atapL * gkDelFB)
	aBuffer		delayr	10
	atapR		deltap3	aDelTimR
			delayw	gaDelSnd + (atapR * gkDelFB)
			outs	atapL, atapR
	gaRvbSndL	=	gaRvbSndL + (atapL * gkRvbSnd)
	gaRvbSndR	=	gaRvbSndR + (atapR * gkRvbSnd)
			clear	gaDelSnd			
endin

instr	11	;REVERB
	aRvbL, aRvbR	reverbsc	gaRvbSndL, gaRvbSndR, gkRvbFB, 10000
			outs		aRvbL, aRvbR
			clear		gaRvbSndL, gaRvbSndR
endin

</CsInstruments>

<CsScore>
i 1 0 3600	;SCAN FOR NOTE KNOB CHANGES
i 10 0 3600
i 11 0 3600
</CsScore>

</CsoundSynthesizer>
