Cabbage Logo
Back to Cabbage Site

Slew Limiter? Smoothing Out MIDI CC?

What would be the best way to smooth out a MIDI CC signal?
Been searching for Csound implementations of a “slew limiter” (with different keywords) and I’m not having much luck. It feels like it should be fairly obvious.

The situation is, I want to use the breath control from my Yamaha WX11 windcontroller (CC02) to modulate the cutoff frequency of a lowpass filter. With midicontrolchange, it works fairly well… except in higher registers where I get a “zipping” effect, sonically similar to aliasing.

Here’s a very simple version based on the default Cabbage synth:

<Cabbage>
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("def1")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

;instrument will be triggered by keyboard widget
instr 1
kBreath init 1
midicontrolchange 2, kBreath, 0, 1
kEnv madsr .1, .2, .6, .4
aOut vco2 p5, p4
aOut moogvcf aOut, kBreath*5000, 0
outs aOut*kEnv, aOut*kEnv
endin

</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
f0 z
</CsScore>
</CsoundSynthesizer>

I also created a legato version, using @iainmccurdy’s BasicMonophonicSynth.csd. The legato works well, the filter has the same issue.

<Cabbage>
form caption("Breath Solo") size(400, 300), colour(58, 110, 182), pluginid("def1")
keyboard bounds(8, 158, 381, 95)
rslider bounds(122,14,70,70), channel("cutoff"), range(0,22000,2000,.5,.01), text("Cutoff")
rslider bounds(200,14,70,70), channel("res"), range(0,1,0.7,1,.01), text("Resonance")

</Cabbage>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>

<CsoundSynthesizer>
<CsInstruments>
ksmps 	= 32	
nchnls 	= 2		
0dbfs	= 1		


;INITIALISE GLOBAL VARIABLES
    gkcps		init	0
    gkCutOff init 0
    gkRes init 0

instr	1
    kcont init 1
    
    midicontrolchange 2, kcont, 0, 1
    kCutOff chnget "cutoff"
    kRes chnget "res"
    gkCutOff=kcont*kCutOff
    gkRes=kcont*kRes
	gkcps		=		p4					
				schedkwhen      1,           0,       1,       2,      0,   3600
endin
			
instr	2

	kreleaseflag	release
	gkNumInstr1 	active 		1								
	if	gkNumInstr1!=0||kreleaseflag=1	kgoto KEEP_PLAYING		
															
	turnoff													
	KEEP_PLAYING:											
	kporttime		linseg	0, .01, 1, 1, 1
	kporttime		=		kporttime * 0.04
	kcps			portk	gkcps, kporttime
	
    asig vco2 0.3,	kcps
	aFilt moogladder asig, gkCutOff, gkRes
    aenv			linsegr	0,0.001,1,0.01,0					
	outs		aFilt * aenv, aFilt * aenv
endin
</CsInstruments>
<CsScore>
f 0 z
</CsScore>
</CsoundSynthesizer>

Again, I’m guessing it’s quite simple. I probably just don’t have the right keywords.

Thanks!

Have you tried using a tonek or portk opcode to smoothen the values?

1 Like

Nice!
Knew it was something simple. In fact, I was using portk in the legato version (from @iainmccurdy’s code). Didn’t fully realize how that worked.
I first put it in Inst 1 and it solved the filter issue but made the sound less legato (it’d attack, once in a while). It works as expected in Inst 2, the same way pitch is assigned.

There’s something slightly weird when I build the instrument, so I should probably change something in the initialization.
Otherwise, I now have a basic “breath lead” instrument that I can start tweaking.

Thanks a lot!

<Cabbage>
form caption("Breath Solo") size(400, 300), colour(58, 110, 182), pluginid("def1")
keyboard bounds(8, 158, 381, 95)
rslider bounds(122,14,70,70), channel("cutoff"), range(0,22000,2000,.5,.01), text("Cutoff")
rslider bounds(200,14,70,70), channel("res"), range(0,1,0.7,1,.01), text("Resonance")

</Cabbage>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>

<CsoundSynthesizer>
<CsInstruments>
ksmps 	= 32	
nchnls 	= 2		
0dbfs	= 1		


;INITIALISE GLOBAL VARIABLES
    gkcps		init	0
    gkCutOff init 0
    gkRes init 0

instr	1
    kcont init 1
    
    midicontrolchange 2, kcont, 0, 1
    kCutOff chnget "cutoff"
    kRes chnget "res"
    gkCutOff = kcont*kCutOff
    gkRes = kcont*kRes
	gkcps		=		p4					
				schedkwhen      1,           0,       1,       2,      0,   3600
endin
			
instr	2

	kreleaseflag	release
	gkNumInstr1 	active 		1								
	if	gkNumInstr1!=0||kreleaseflag=1	kgoto KEEP_PLAYING		
															
	turnoff													
	KEEP_PLAYING:											
	kporttime		linseg	0, .01, 1, 1, 1
	kporttime		=		kporttime * 0.04
	kcps			portk	gkcps, kporttime
	kCutOff portk gkCutOff, kporttime
	kRes portk gkRes, kporttime
	
    asig vco2 0.3,	kcps
	aFilt moogladder asig, kCutOff, kRes
    aenv			linsegr	0,0.001,1,0.01,0					
	outs		aFilt * aenv, aFilt * aenv
endin
</CsInstruments>
<CsScore>
f 0 z
</CsScore>
</CsoundSynthesizer>
1 Like