Skip to content

k35lpf

Zero-delay feedback implementation of Korg35 resonant low-pass filter.

⚠ Deprecated alias

K35_lpf is a deprecated alias of k35lpf in Csound 7. Use k35lpf in new code. The old name still works for compatibility.

This filter design is found in the Korg MS10, early MS20, and Monotron series.

Syntax

asig = k35lpf(ain, xcf, xQ [, inlp, isaturation, istor])
asig K35_lpf ain, xcf, xQ [, inlp, isaturation, istor]

Initialization

istor --initial disposition of internal data space. Since filtering incorporates a feedback loop of previous output, the initial status of the storage space used is significant. A zero value will clear the space; a non-zero value will allow previous information to remain. The default value is 0.

Performance

asig -- output signal.

ain -- input signal.

xcf -- filter cutoff frequency (i-, k-, or a-rate).

xQ -- filter Q value (i-, k-, or a-rate). Range 1.0-10.0 (clamped by opcode). Self-oscillation occurs at 10.0.

knlp (optional, default=0) -- Non-linear processing method. 0 = no processing, 1 = non-linear processing. Method 1 uses tanh(ksaturation * input). Enabling NLP may increase the overall output of filter above unity and should be compensated for outside of the filter.

ksaturation (optional, default=1) -- saturation amount to use for non-linear processing. Values > 1 increase the steepness of the NLP curve.

Examples

Here is an example of the k35lpf opcode. It uses the file k35-modern.csd.

Example of the k35lpf opcode.
<CsoundSynthesizer>
<CsOptions>
  -o dac
</CsOptions>
; ==============================================
<CsInstruments>

sr     = 48000
ksmps  = 1
nchnls = 2
0dbfs  = 1

;; test instruments to demo filter cutoff sweep with high resonance

instr 1 
  sig:a = vco2(0.5, cps2pch(6.00, 12))
  sig = k35lpf(sig, expseg:a(10000, p3, 30), 9.9, 0, 1)
  sig *= 0.25
  sig = limit(sig, -1.0, 1.0)
  out(sig, sig)
endin

instr 2 
  sig:a = vco2(0.5, cps2pch(6.00, 12))
  sig = k35lpf(sig, expseg:k(10000, p3, 30), 9.9, 0, 1)
  sig *= 0.25
  sig = limit(sig, -1.0, 1.0)
  out(sig, sig)
endin

instr 3 
  sig:a = vco2(0.5, cps2pch(6.00, 12))
  sig = k35hpf(sig, expseg:a(10000, p3, 30), 9.9, 0, 1)
  sig *= 0.25
  sig = limit(sig, -1.0, 1.0)
  out(sig, sig)
endin

instr 4 
  sig:a = vco2(0.5, cps2pch(6.00, 12))
  sig = k35hpf(sig, expseg:k(10000, p3, 30), 9.9, 0, 1)
  sig *= 0.25
  sig = limit(sig, -1.0, 1.0)
  out(sig, sig)
endin

;; beat instruments

instr ms20_drum
  pch:i = cps2pch(p4, 12)
  amp:i = ampdbfs(p5)
  env:a = expseg(10000, 0.05, pch, p3 - 0.05, pch)

  sig:a = rand(-1.0, 1.0)
  sig = k35hpf(sig, 60, 7, 1, 1)
  sig = k35lpf(sig, env, 9.8, 1, 1)

  sig = tanh(sig * 16)
  sig *= expon(amp, p3, 0.0001)
  out(sig, sig)
endin

instr ms20_bass 
  pch:i = cps2pch(p4, 12)
  amp:i = ampdbfs(p5)
  env:a = expseg(1000, 0.1, pch * 2, p3 - 0.05, pch * 2)

  sig:a = vco2(1.0, pch)
  sig = k35hpf(sig, pch, 5, 0, 1)
  sig = k35lpf(sig, env, 8, 0, 1)

  sig *= expon(amp, p3, 0.0001) * 0.8
  out(sig, sig)
endin

;; perf code

Tempo@global:k = init(122)

opcode beat_dur():(i)
  xout(60 / i(Tempo)) 
endop

instr bass_player
  dur:i = beat_dur() / int(random(1, 3)) 
  pch:i = 6.00 + int(random(1,3)) + int(random(1,3)) / 100

  schedule("ms20_bass", 0, dur, pch, -11) 

  if (p2 < 37.5) then
    schedule("bass_player", dur, 0.1)
  endif
  turnoff()
endin

instr beat_player 
  step_total:i = p4 
  step:i = step_total % 16

  if step % 4 == 0 then
    pch:i = ((step_total % 128) < 112) ? 4.00 : 8.00
    amp:i = (step == 0)  ? -9 : -12
    schedule("ms20_drum", 0, 0.5, pch, amp)
  endif

  schedule("ms20_drum", 0, 0.125, 14.00, 
           (step % 4 == 0) ? -12 : -18)

  if p2 < 37.5 then
    schedule("beat_player", beat_dur() / 4, 0.1, step_total + 1)
  endif
  turnoff()
endin

;; start play of beats

instr start_beats
  schedule("beat_player", 0, 0.1, 0)
  schedule("bass_player", 0, 0.1)
endin

</CsInstruments>
; ==============================================
<CsScore>
i1 0 5.0
i2 5 5.0
i3 10 5.0
i4 15 5.0

i "start_beats" 22 0.5 0

</CsScore>
</CsoundSynthesizer>

Here is an example of the K35_lpf opcode. It uses the file k35.csd.

Example of the K35_lpf opcode.
<CsoundSynthesizer>
<CsOptions>
  -o dac
</CsOptions>
; ==============================================
<CsInstruments>

sr      =       48000
ksmps   =       1
nchnls  =       2
0dbfs   =       1

;; test instruments to demo filter cutoff sweep with high resonance

instr 1 

asig = vco2(0.5, cps2pch(6.00, 12))
asig = K35_lpf(asig, expseg:a(10000, p3, 30), 9.9, 0, 1)
asig *= 0.25
asig  = limit(asig, -1.0, 1.0)

outc(asig, asig)

endin


instr 2 

asig = vco2(0.5, cps2pch(6.00, 12))
asig = K35_lpf(asig, expseg:k(10000, p3, 30), 9.9, 0, 1)
asig *= 0.25
asig  = limit(asig, -1.0, 1.0)

outc(asig, asig)

endin

instr 3 

asig = vco2(0.5, cps2pch(6.00, 12))
asig = K35_hpf(asig, expseg:a(10000, p3, 30), 9.9, 0, 1)
asig *= 0.25
asig  = limit(asig, -1.0, 1.0)

outc(asig, asig)

endin


instr 4 

asig = vco2(0.5, cps2pch(6.00, 12))
asig = K35_hpf(asig, expseg:k(10000, p3, 30), 9.9, 0, 1)
asig *= 0.25
asig  = limit(asig, -1.0, 1.0)

outc(asig, asig)

endin

;; beat instruments

instr ms20_drum

  ipch = cps2pch(p4, 12)
  iamp = ampdbfs(p5)
  aenv = expseg:a(10000, 0.05, ipch, p3 - .05, ipch)

  asig = rand:a(-1.0, 1.0)
  asig = K35_hpf(asig, 60, 7, 1, 1)
  asig = K35_lpf(asig, aenv, 9.8, 1, 1)

  asig = tanh(asig * 16)

  asig *= expon(iamp, p3, 0.0001)

  outc(asig, asig)

endin

instr ms20_bass 
  ipch = cps2pch(p4, 12)
  iamp = ampdbfs(p5)
  aenv = expseg(1000, 0.1, ipch * 2, p3 - .05, ipch * 2)

  asig = vco2(1.0, ipch)
  asig = K35_hpf(asig, ipch, 5, 0, 1)
  asig = K35_lpf(asig, aenv, 8, 0, 1)

  asig *= expon:a(iamp, p3, 0.0001) * 0.8

  outc(asig, asig)
endin

;; perf code

gktempo init 122

opcode beat_dur,i,0
  xout 60 / i(gktempo) 
endop

instr bass_player
  idur = beat_dur() / int(random(1,3)) 
  ipch = 6.00 + int(random(1,3)) + int(random(1,3)) / 100

  schedule("ms20_bass", 0, idur, ipch, -11) 

  if(p2 < 37.5) then
    schedule("bass_player", idur, 0.1)
  endif
  turnoff
endin

instr beat_player 
  istep_total = p4 
  istep = istep_total % 16

  if(istep % 4 == 0) then
    ipch = ((istep_total % 128) < 112) ? 4.00 : 8.00
    iamp = (istep == 0)  ? -9 : -12
    schedule("ms20_drum", 0, 0.5, ipch, iamp)
  endif

  schedule("ms20_drum", 0, 0.125, 14.00, 
           (istep % 4 == 0) ? -12 : -18)

  if(p2 < 37.5) then
    schedule("beat_player", beat_dur() / 4, 0.1, istep_total + 1)
  endif
  turnoff
endin

;; start play of beats

instr start_beats
  schedule("beat_player", 0, 0.1, 0)
  schedule("bass_player", 0, 0.1)
endin


</CsInstruments>
; ==============================================
<CsScore>
i1 0 5.0
i2 5 5.0
i3 10 5.0
i4 15 5.0

i "start_beats" 22 0.5 0

</CsScore>
</CsoundSynthesizer>

References

This filter is based on the work of Will Pirkle that employs Vadim Zavalishin's work with bilinear tranforms to create topology-preserving transform (TPT) implementations of analog filters.

  1. Pirkle, Will. Designing Software Synthesizer Plug-ins in C++: For RackAFX, VST3, and Audio Units. CRC Press, 2014.
  2. Pirkle, Will. AN-5: Virtual Analog (VA) Korg35 Lowpass Filter v3.5. 2013.
  3. Zavalishin, Vadim. "The Art of VA filter design." Native Instruments, 2012.

See also

Standard filters: Zero-delay Feedback Filters (Virtual Analog)

Credits

Author: Steven Yi
April 2017

New in Csound 6.09.0