Add multi-mode state-variable filter example plugin
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
		
							parent
							
								
									f76ec9a1ef
								
							
						
					
					
						commit
						8fc3663bc3
					
				
							
								
								
									
										8
									
								
								examples/multimode_filter.lv2/manifest.ttl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								examples/multimode_filter.lv2/manifest.ttl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:multimode-filter>
 | 
			
		||||
    a lv2:Plugin ;
 | 
			
		||||
    lv2:binary <libmultimode_filter.so> ;
 | 
			
		||||
    rdfs:seeAlso <multimode_filter.ttl> .
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										95
									
								
								examples/multimode_filter.lv2/multimode_filter.ttl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								examples/multimode_filter.lv2/multimode_filter.ttl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,95 @@
 | 
			
		||||
@prefix bufs: <http://lv2plug.in/ns/ext/buf-size#> .
 | 
			
		||||
@prefix doap: <http://usefulinc.com/ns/doap#> .
 | 
			
		||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
 | 
			
		||||
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
 | 
			
		||||
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
 | 
			
		||||
@prefix props: <http://lv2plug.in/ns/ext/port-props#> .
 | 
			
		||||
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
@prefix unit: <http://lv2plug.in/ns/extensions/units#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:multimode-filter>
 | 
			
		||||
    a lv2:Plugin, lv2:AmplifierPlugin , doap:Project ;
 | 
			
		||||
 | 
			
		||||
    lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
 | 
			
		||||
 | 
			
		||||
    opts:supportedOption bufs:nominalBlockLength ,
 | 
			
		||||
                         bufs:maxBlockLength ,
 | 
			
		||||
                         params:sampleRate ;
 | 
			
		||||
 | 
			
		||||
    lv2:port [
 | 
			
		||||
        a lv2:InputPort, lv2:AudioPort ;
 | 
			
		||||
        lv2:index 0 ;
 | 
			
		||||
        lv2:name "Audio In" ;
 | 
			
		||||
        lv2:symbol "in" ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:OutputPort, lv2:AudioPort ;
 | 
			
		||||
        lv2:index 1 ;
 | 
			
		||||
        lv2:name "Audio Out" ;
 | 
			
		||||
        lv2:symbol "out" ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
        lv2:index 2 ;
 | 
			
		||||
        lv2:name "Cutoff" ;
 | 
			
		||||
        lv2:symbol "cutoff" ;
 | 
			
		||||
        lv2:default 7000.0 ;
 | 
			
		||||
        lv2:minimum 16.0 ;
 | 
			
		||||
        lv2:maximum 7000.0 ;
 | 
			
		||||
        lv2:portProperty props:logarithmic;
 | 
			
		||||
        unit:unit unit:hz ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
        lv2:index 3 ;
 | 
			
		||||
        lv2:name "Q" ;
 | 
			
		||||
        lv2:symbol "q" ;
 | 
			
		||||
        lv2:default 0.8 ;
 | 
			
		||||
        lv2:minimum 0.8 ;
 | 
			
		||||
        lv2:maximum 10.0 ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
        lv2:index 4 ;
 | 
			
		||||
        lv2:name "Filter mode" ;
 | 
			
		||||
        lv2:symbol "mode" ;
 | 
			
		||||
        lv2:default 0 ;
 | 
			
		||||
        lv2:minimum 0 ;
 | 
			
		||||
        lv2:maximum 3 ;
 | 
			
		||||
        lv2:portProperty lv2:enumeration, lv2:integer ;
 | 
			
		||||
        lv2:scalePoint [
 | 
			
		||||
            rdfs:label "Lowpass" ;
 | 
			
		||||
            rdf:value 0 ;
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            rdfs:label "Highpass" ;
 | 
			
		||||
            rdf:value 1 ;
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            rdfs:label "Bandpass" ;
 | 
			
		||||
            rdf:value 2;
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            rdfs:label "Bandreject" ;
 | 
			
		||||
            rdf:value 3 ;
 | 
			
		||||
        ] ;
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    rdfs:comment """
 | 
			
		||||
A multi-mode (LPF/HPF/BPF/Notch) audio filter.
 | 
			
		||||
""" ;
 | 
			
		||||
 | 
			
		||||
    doap:name "nymph multi-mode filter" ;
 | 
			
		||||
    doap:license <https://spdx.org/licenses/MIT> ;
 | 
			
		||||
 | 
			
		||||
    doap:maintainer [
 | 
			
		||||
        foaf:name "Christopher Arndt" ;
 | 
			
		||||
        foaf:mbox <mailto:info@chrisarndt.de> ;
 | 
			
		||||
        foaf:homepage <https://gitlab.com/SpotlightKid/nymph> ;
 | 
			
		||||
    ] ;
 | 
			
		||||
 | 
			
		||||
    lv2:microVersion 0 ;
 | 
			
		||||
    lv2:minorVersion 1 .
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										97
									
								
								examples/multimode_filter.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								examples/multimode_filter.nim
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
			
		||||
## A simple multi-mode audio filter LV2 plugin
 | 
			
		||||
 | 
			
		||||
import nymph
 | 
			
		||||
 | 
			
		||||
import svf
 | 
			
		||||
 | 
			
		||||
const PluginUri = "urn:nymph:examples:multimode-filter"
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    SampleBuffer = UncheckedArray[cfloat]
 | 
			
		||||
 | 
			
		||||
    PluginPort {.pure.} = enum
 | 
			
		||||
        Input, Output, Cutoff, Q, Mode
 | 
			
		||||
 | 
			
		||||
    SVFPlugin = object
 | 
			
		||||
        input: ptr SampleBuffer
 | 
			
		||||
        output: ptr SampleBuffer
 | 
			
		||||
        cutoff: ptr cfloat
 | 
			
		||||
        q: ptr cfloat
 | 
			
		||||
        mode: ptr cfloat
 | 
			
		||||
        svf: FilterSV
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
 | 
			
		||||
                 bundlePath: cstring; features: ptr ptr Lv2Feature):
 | 
			
		||||
                 Lv2Handle {.cdecl.} =
 | 
			
		||||
     var plug = createShared(SVFPlugin)
 | 
			
		||||
     plug.svf = initFilterSV(fmLowPass, sampleRate)
 | 
			
		||||
     return plug
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc connectPort(instance: Lv2Handle; port: cuint;
 | 
			
		||||
                 dataLocation: pointer) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr SVFPlugin](instance)
 | 
			
		||||
    case cast[PluginPort](port)
 | 
			
		||||
    of PluginPort.Input:
 | 
			
		||||
        plug.input = cast[ptr SampleBuffer](dataLocation)
 | 
			
		||||
    of PluginPort.Output:
 | 
			
		||||
        plug.output = cast[ptr SampleBuffer](dataLocation)
 | 
			
		||||
    of PluginPort.Cutoff:
 | 
			
		||||
        plug.cutoff = cast[ptr cfloat](dataLocation)
 | 
			
		||||
    of PluginPort.Q:
 | 
			
		||||
        plug.q = cast[ptr cfloat](dataLocation)
 | 
			
		||||
    of PluginPort.Mode:
 | 
			
		||||
        plug.mode = cast[ptr cfloat](dataLocation)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc activate(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr SVFPlugin](instance)
 | 
			
		||||
    plug.svf.reset()
 | 
			
		||||
    plug.svf.setMode(fmLowPass)
 | 
			
		||||
    plug.svf.setCutoff(7_000.0)
 | 
			
		||||
    plug.svf.setQ(0.8)
 | 
			
		||||
    plug.svf.calcCoef()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr SVFPlugin](instance)
 | 
			
		||||
    plug.svf.setMode(plug.mode[].int.clamp(0, 4).FilterMode)
 | 
			
		||||
    plug.svf.setCutoff(plug.cutoff[].clamp(16.0, 7_000.0))
 | 
			
		||||
    plug.svf.setQ(plug.q[].clamp(0.8, 10.0))
 | 
			
		||||
    plug.svf.calcCoef()
 | 
			
		||||
 | 
			
		||||
    for pos in 0 ..< nSamples:
 | 
			
		||||
        plug.output[pos] = plug.svf.process(plug.input[pos])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc deactivate(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    discard
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc cleanup(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    freeShared(cast[ptr SVFPlugin](instance))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc extensionData(uri: cstring): pointer {.cdecl.} =
 | 
			
		||||
    return nil
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc NimMain() {.cdecl, importc.}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc lv2Descriptor(index: cuint): ptr Lv2Descriptor {.
 | 
			
		||||
                   cdecl, exportc, dynlib, extern: "lv2_descriptor".} =
 | 
			
		||||
    NimMain()
 | 
			
		||||
 | 
			
		||||
    if index == 0:
 | 
			
		||||
        result = createShared(Lv2Descriptor)
 | 
			
		||||
        result.uri = cstring(PluginUri)
 | 
			
		||||
        result.instantiate = instantiate
 | 
			
		||||
        result.connectPort = connectPort
 | 
			
		||||
        result.activate = activate
 | 
			
		||||
        result.run = run
 | 
			
		||||
        result.deactivate = deactivate
 | 
			
		||||
        result.cleanup = cleanup
 | 
			
		||||
        result.extensionData = extensionData
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										95
									
								
								examples/svf.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								examples/svf.nim
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,95 @@
 | 
			
		||||
## State Variable Filter after Hal Chamberlin, Musical Applications of Microprocessors
 | 
			
		||||
## Only stable up to fc ~= fs / 6.5 !
 | 
			
		||||
 | 
			
		||||
import std/math
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    FilterMode* = enum
 | 
			
		||||
        fmLowPass, fmHighPass, fmBandPass, fmBandReject
 | 
			
		||||
 | 
			
		||||
    FilterSV* = object
 | 
			
		||||
        mode: FilterMode
 | 
			
		||||
        cutoff: float
 | 
			
		||||
        q: float
 | 
			
		||||
        lowPass: float
 | 
			
		||||
        hiPass: float
 | 
			
		||||
        bandPass: float
 | 
			
		||||
        bandReject: float
 | 
			
		||||
        a: float
 | 
			
		||||
        b: float
 | 
			
		||||
        maxCutoff: float
 | 
			
		||||
        sampleRate: float
 | 
			
		||||
        needs_update: bool
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc reset*(self: var FilterSV) =
 | 
			
		||||
    self.lowPass = 0
 | 
			
		||||
    self.hiPass = 0
 | 
			
		||||
    self.bandPass = 0
 | 
			
		||||
    self.bandReject = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc initFilterSV*(mode: FilterMode = fmLowPass, sampleRate: float = 48_000): FilterSV =
 | 
			
		||||
    result.mode = mode
 | 
			
		||||
    result.sampleRate = sampleRate
 | 
			
		||||
    result.reset()
 | 
			
		||||
    result.a = 0
 | 
			
		||||
    result.b = 0
 | 
			
		||||
    result.maxCutoff = sampleRate / 6.0
 | 
			
		||||
    result.needs_update = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc calcCoef*(self: var FilterSV) =
 | 
			
		||||
    if self.needs_update:
 | 
			
		||||
        self.a = 2.0 * sin(PI * self.cutoff / self.sampleRate)
 | 
			
		||||
 | 
			
		||||
        if self.q > 0.0:
 | 
			
		||||
            self.b = 1.0 / self.q
 | 
			
		||||
        else:
 | 
			
		||||
            self.b = 0.0
 | 
			
		||||
 | 
			
		||||
        self.needs_update = false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setCutoff*(self: var FilterSV, cutoff: float) =
 | 
			
		||||
    let fc = if cutoff > self.maxCutoff: self.maxCutoff else: cutoff
 | 
			
		||||
 | 
			
		||||
    if fc != self.cutoff:
 | 
			
		||||
        self.cutoff = fc
 | 
			
		||||
        self.needs_update = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setQ*(self: var FilterSV, q: float) =
 | 
			
		||||
    if q != self.q:
 | 
			
		||||
        self.q = q
 | 
			
		||||
        self.needs_update = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setMode*(self: var FilterSV, mode: FilterMode) =
 | 
			
		||||
    self.mode = mode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setSampleRate*(self: var FilterSV, sampleRate: float) =
 | 
			
		||||
    if sampleRate != self.sampleRate:
 | 
			
		||||
        self.sampleRate = sampleRate
 | 
			
		||||
        self.needs_update = true
 | 
			
		||||
        self.reset()
 | 
			
		||||
        self.calcCoef()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc process*(self: var FilterSV, sample: float): float =
 | 
			
		||||
    self.lowPass += self.a * self.bandPass
 | 
			
		||||
    self.hiPass = sample - (self.lowPass + (self.b * self.bandPass))
 | 
			
		||||
    self.bandPass += self.a * self.hiPass
 | 
			
		||||
    self.bandReject = self.hiPass + self.lowPass
 | 
			
		||||
 | 
			
		||||
    case self.mode:
 | 
			
		||||
        of fmLowPass:
 | 
			
		||||
            return self.lowPass
 | 
			
		||||
        of fmHighPass:
 | 
			
		||||
            return self.hiPass
 | 
			
		||||
        of fmBandPass:
 | 
			
		||||
            return self.bandPass
 | 
			
		||||
        of fmBandReject:
 | 
			
		||||
            return self.bandReject
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,8 @@ type Example = tuple
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const examples = to_table({
 | 
			
		||||
    "amp": "urn:nymph:examples:amp"
 | 
			
		||||
    "amp": "urn:nymph:examples:amp",
 | 
			
		||||
    "multimode_filter": "urn:nymph:examples:multimode-filter",
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user