<Cabbage>
form size(655, 362), caption("Feedback processor"), pluginID("fbpr"), colour(20,30,20)
;image bounds(0, 0, 655, 362), file("background.jpg"), shape("round")

groupbox bounds(4, 2, 282, 100), text("Autolevel"), plant("Autolevel"){
rslider channel("aDryWet"), bounds(10, 25, 70, 70), text("DryWet"), range(0, 1, 0.25)
rslider channel("RefLevel"), bounds(75, 25, 70, 70), text("RefLevel"), range(-96, 0, -9)
rslider channel("MaxFact"), bounds(140, 25, 70, 70), text("MaxFact"), range(1, 20, 12)
rslider channel("LowThresh"), bounds(205, 25, 70, 70), text("LowThresh"), range(-96, 0, -50)
}

groupbox bounds(291, 2, 290, 100), text("AdaptiveEq"), plant("AdaptiveEq"){
rslider channel("NumBands"), bounds(10, 25, 70, 70), text("NumBands"), range(1, 20, 3, 1, 1)
rslider channel("Attenuation"), bounds(75, 25, 70, 70), text("Atten"), range(0, 1, 0.61)
rslider channel("ResponseTime"), bounds(140, 25, 70, 70), text("RespTime"), range(0.8, 4, 1.6)
rslider channel("SmoothTime"), bounds(205, 25, 70, 70), text("SmoothTime"), range(0.01, 0.5, 0.45)
}

groupbox bounds(4, 108, 577, 100), text("HomingFilter"), plant("HomingFilter"){
rslider channel("hDryWet"),        bounds(5, 25, 70, 70), text("DryWet"), range(0, 1, 1)
rslider channel("HomingRate"),    bounds(75, 25, 70, 70), text("HomingRate"), range(0.05, 2, 0.4)
rslider channel("FilterAmount1"), bounds(145, 25, 70, 70), text("Amount1"), range(0, 1, 0.4, 0.5,  0.001)
rslider channel("FilterQ1"),      bounds(215, 25, 70, 70), text("Q1"), range(0.01, 0.8, 0.55)
rslider channel("FilterAmount2"), bounds(285, 25, 70, 70), text("Amount2"), range(0, 1, 0.4, 0.5, 0.001)
rslider channel("FilterQ2"),      bounds(355, 25, 70, 70), text("Q2"), range(0.01, 0.8, 0.55)
rslider channel("FilterAmount3"), bounds(425, 25, 70, 70), text("Amount3"), range(0, 1, 0.4, 0.5, 0.001)
rslider channel("FilterQ3"),      bounds(495, 25, 70, 70), text("Q3"), range(0.01, 0.8, 0.55)
}

rslider channel("Master"),      bounds(583, 132, 70, 70), text("Master"), range(-10, 6, -5)

csoundoutput bounds(4, 212, 648, 150)
</Cabbage>

<CsoundSynthesizer>
<CsOptions>
-n -d
</CsOptions>
<CsInstruments>

	sr = 44100
	ksmps = 64
	nchnls = 2
	0dbfs=1

	giSine		ftgen	0, 0, 65537, 10, 1

; pvs ftables
	gifftsize 	= 256
	giFftTabSize	= (gifftsize / 2)+1
	gifna     	ftgen   0 ,0 ,giFftTabSize, 7, 0, giFftTabSize, 0   ; make ftable for pvs analysis
	gifnf     	ftgen   0 ,0 ,giFftTabSize, 7, 0, giFftTabSize, 0   ; make ftable for pvs analysis
	gi1		ftgen   0 ,0 ,giFftTabSize, 7, 1, giFftTabSize, 1   ; all 1's
	gifnaMod     	ftgen   0 ,0 ,giFftTabSize, 7, 0, giFftTabSize, 0   ; make ftable for modified pvs data
	gifnaMod1     	ftgen   0 ,0 ,giFftTabSize, 7, 0, giFftTabSize, 0   ; make ftable for modified pvs data
	gifnaMod2     	ftgen   0 ,0 ,giFftTabSize, 7, 0, giFftTabSize, 0   ; make ftable for modified pvs data
	gifnaMorf	ftgen	0 ,0 ,4, -2,  gifnaMod2, gifnaMod1, gifnaMod2, gifnaMod1

;***************************************************
; auto level adjust

	instr 91

	ain		inch	1

	kWet		chnget "aDryWet"
	kRefLevel	chnget "RefLevel"
	kRefLevel	= ampdbfs(kRefLevel)*0.66
	kLevelRate	= 2			; how fast can the level adjustments happen
	kMaxLevelFact	chnget "MaxFact"
	kLowThreshold	chnget "LowThresh"
	kLowThreshold	= ampdbfs(kLowThreshold)

	krmsOut		init 1
	kLevel		init 1
	aLevel		init 1
	krmsIn		rms	ain			
	krmsIn		= krmsIn * 2.5 					; empirical calibration
	kLevel		divz kRefLevel, krmsIn, 1       		; calculate new output level 
	kLevel		= (krmsIn < kLowThreshold ? 1 : kLevel)		; do not amplify more if input signal is below noise threshold
	kLevel		limit	kLevel, 0, kMaxLevelFact		; limit output level range
	kLevel		tonek kLevel, kLevelRate
	aLevel		interp	kLevel
	aLevel		tone	aLevel, kLevelRate
	aout		= ain * aLevel
	aout 		= (aout*kWet) + ain*(1-kWet)
			chnset aout, "audio1"

	endin

;***************************************************
; adaptive EQ
	instr 93

	ain		chnget "audio1"
	                denorm ain
	                
	iwtype 		= 1
	fsin 		pvsanal	ain, gifftsize, gifftsize/4, gifftsize, iwtype
	kflag   	pvsftw	fsin,gifna,gifnf          	; export  amps  and freqs to table,
	
	; input parameters
	kpvsNumBands		chnget "NumBands"		; how many frequency bands to filter
	kpvsAmpMod		chnget "Attenuation"		; max attenuation is 1.0
	kpvsResponseTime	chnget "ResponseTime"		; feeback filter iteration time
	kpvsSmoothTime		chnget "SmoothTime"		; feeback filter smoothing time on new iteration
	kpvsResponseCps         divz 1,kpvsResponseTime,1
	kmetro			metro kpvsResponseCps
	kdoflag			init 0
	kdoflag			= kdoflag + kmetro
	kswitch			init 1
	kswitch			= (kswitch == 1 ? kswitch + kmetro : kswitch - kmetro)

	; copy pvs data from table to array
	; modify amplitude of single bin
	; repeat the above N number of times
	; transfer array to pvs data

if (kdoflag > 0) && (kflag > 0) then
	; copy pvs data from table to array
	kArr2[]  	init    giFftTabSize-2
	kArrM[]  	init    giFftTabSize-2
        		copyf2array kArr2, gifna
        		copyf2array kArrM, gi1

	kcount 		= 0
  process:
	kMax ,kMaxIndx 	maxarray kArr2
	kArr2[kMaxIndx]	= 0
	kArrM[kMaxIndx]	= 0
	kcount = kcount + 1
  if kcount < kpvsNumBands then
  kgoto process
  endif

    if kswitch == 1 then
        		copya2ftab kArrM, gifnaMod2
    reinit morftable
    else
        		copya2ftab kArrM, gifnaMod1

    reinit morftable
    endif

	kdoflag 	= 0
endif

morftable:
	iswitch		= i(kswitch)
	kikinterp       = kpvsSmoothTime*kpvsResponseTime
	ikinterp	= i(kikinterp)
	kmorfindx	linseg iswitch,  ikinterp, iswitch+1, 1, iswitch+1
			ftmorf kmorfindx, gifnaMorf, gifnaMod
	rireturn

	; resynth unmodified
	aclean    	pvsynth    fsin
	; modify and resynth
	fsout 		pvsmaska fsin, gifnaMod, kpvsAmpMod
	amod    	pvsynth    fsout
			chnset amod, "audio1"

	endin

;***************************************************
; A homing filter, the filter's cutoff frequency slowly homes in on the most prominent frequency/pitch

	instr 95
	a1		chnget "audio1"
        adenorm         rnd31 1, 1
        a1              = a1 + (adenorm*0.00000000000000000001)

	kWet		chnget "hDryWet"
	kFiltFreq	chnget "HomingRate"
	kStrength1	chnget "FilterAmount1"
	kFiltQ1		chnget "FilterQ1"
	kStrength2	chnget "FilterAmount2"
	kFiltQ2		chnget "FilterQ2"
	kStrength3	chnget "FilterAmount3"
	kFiltQ3		chnget "FilterQ3"


	kamp		rms a1
	acps,alock	plltrack a1, 0.3, 20, 0.33, 20, 1500, 0.000000000000000000001
	kcps1		downsamp acps
	kcps		limit kcps1, 20, 15000
	kcps		tonek	kcps, kFiltFreq
	kamp		tonek	kamp*50, kFiltFreq
	;atest		oscil kamp, kcps, giSine
	kdamp		= 1-(kamp*kStrength1)
	kgain		limit kdamp, 0.01, 1
	a2		pareq	a1, kcps, kgain, kFiltQ1

	kamp		rms a2
	ihopsize	= 512
	imedianSize	= ihopsize
	kcps2a, kamp2 	ptrack a2, ihopsize
	kcps2b		init 100
	kcps2b		= (kcps2a > 1400 ? kcps2b : kcps2a) ; avoid high intermittencies
	kcps2		init 100
	kcps2		= (kamp < 0.04 ? kcps2 : kcps2b)  ; don't track (keep last value) at low amplitude
	kcps2		mediank	kcps2, imedianSize, imedianSize

;	acps,alock	plltrack a2, 0.3
;	kcps		downsamp acps
	kcps		limit kcps2, 20, 15000
	kcps		tonek	kcps, kFiltFreq
	kamp		tonek	kamp*50, kFiltFreq
	kdamp		= 1-(kamp*kStrength2)
	kgain		limit kdamp, 0.01, 1
	a3		pareq	a2, kcps, kgain, kFiltQ2

	acps,alock	plltrack a3, 0.3, 20, 0.33, 20, 1500, 0.000000000000000000001
	kcps3		downsamp acps

;	Stest		sprintfk "cps 1,2,3 %i %i %i", kcps1, kcps2, kcps3
;	ktick		metro 1
;			puts Stest, ktick+1

	kcps		limit kcps3, 20, 15000
	kcps		tonek	kcps, kFiltFreq
	kamp		tonek	kamp*50, kFiltFreq
	;atest		oscil kamp, kcps, giSine
	kdamp		= 1-(kamp*kStrength3)
	kgain		limit kdamp, 0.01, 1
	a4		pareq	a3, kcps, kgain, kFiltQ3
	aout 		= a4*kWet+(a1*(1-kWet))

			chnset aout, "audio1"
	endin

	instr 97
	a1		chnget "audio1"
	kvol		chnget "Master"
	kvol		tonek ampdbfs(kvol), 10
	avol		interp kvol
	avol		butterlp avol, 20 
			outch 1, a1*avol, 2, a1*avol
	endin

</CsInstruments>
<CsScore>
#define SCORELEN # 86400 #
i91	0	$SCORELEN	; autolevel
i93	0	$SCORELEN	; adaptive eq
i95	0	$SCORELEN	; homing filter
i97	0	$SCORELEN	; output
e
</CsScore>
</CsoundSynthesizer>

