diff --git a/CMakeLists.txt b/CMakeLists.txt index 46805f3..1bb31e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ if(MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mstackrealign") endif() +add_subdirectory(src/filters) add_subdirectory(src/generators) add_subdirectory(src/pv) add_subdirectory(src/rubberband) @@ -113,10 +114,11 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) endif() endif() -install(TARGETS generators pv rubberband +install(TARGETS filters generators pv rubberband LIBRARY DESTINATION Plugins ) install(FILES + src/filters/filters.sc src/generators/generators.sc src/pv/pv.sc src/rubberband/rubberband.sc @@ -124,6 +126,7 @@ install(FILES PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ ) install(FILES + src/filters/FIR.schelp src/generators/ImpulseDropout.schelp src/generators/ImpulseJitter.schelp src/generators/LoopPhasor.schelp @@ -139,7 +142,7 @@ install(FILES PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ ) if(SUPERNOVA) - install(TARGETS generators_supernova pv_supernova rubberband_supernova + install(TARGETS filters_supernova generators_supernova pv_supernova rubberband_supernova LIBRARY DESTINATION Plugins ) endif() diff --git a/src/filters/CMakeLists.txt b/src/filters/CMakeLists.txt new file mode 100644 index 0000000..690ad3a --- /dev/null +++ b/src/filters/CMakeLists.txt @@ -0,0 +1,16 @@ +# Create the project library +add_library(filters MODULE filters.cpp) +add_library(fir STATIC fir.cpp) +set_target_properties(fir PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_link_libraries(filters PRIVATE + fir +) + +if(SUPERNOVA) + add_library(filters_supernova MODULE filters.cpp) + set_property(TARGET filters_supernova + PROPERTY COMPILE_DEFINITIONS SUPERNOVA) + target_link_libraries(filters_supernova PRIVATE + fir + ) +endif() diff --git a/src/filters/FIR.schelp b/src/filters/FIR.schelp new file mode 100644 index 0000000..0e25110 --- /dev/null +++ b/src/filters/FIR.schelp @@ -0,0 +1,56 @@ +class:: FIR +summary:: No-nonsense FIR filter +related:: Classes/Convolution3, Classes/FOS, Classes/SOS, Classes/OneZero +categories:: Libraries>FlexUGens, UGens>Filters>Linear + +Description:: +A no-nonsense FIR filter where you provide your own coefficients. FIR duplicates the functionality +of link::Classes/Convolution3::. The difference is that FIR does not require you to load your +filter coefficients into a link::Classes/Buffer::. Instead, you can provide them directly as a +link::Classes/Ref:: in the same way as is done in link::Classes/DynKlank::. + +classmethods:: + +method::ar + +argument::coefs +A link::Classes/Ref:: to a list of FIR filter coefficients. The number of coefficients can change +over time, but can never exceed the server block size (if it does, extra coefficients will be truncated). + +argument::in +The input signal to filter + +argument::mul +The signal will be multiplied by this scalar + +argument::add +This scalar will be added to the signal + +Examples:: + +A 5-tap lowpass filter +code:: +{ + var sig = Saw.ar(440); + sig = FIR.ar(`[0.2, 0.2, 0.2, 0.2, 0.2], sig); +}.play; +:: + +A 19-tap control signal smoother +code:: +{ + var sig; + sig = LFPulse.kr(10); + [sig, FIR.kr(`((1/19)!19), sig)]; +}.plot(0.3); +:: + +Changing coefficients +code:: +{ + var sig; + sig = LFPulse.kr(10); + [sig, FIR.kr(`[SinOsc.kr(10), SinOsc.kr(15)], sig)]; +}.plot(0.3); +:: + diff --git a/src/filters/filters.cpp b/src/filters/filters.cpp new file mode 100644 index 0000000..7877c63 --- /dev/null +++ b/src/filters/filters.cpp @@ -0,0 +1,33 @@ +/* +File: filters.cpp +Author: Jeff Martin + +Description: +A collection of SuperCollider filter UGens + +Copyright © 2026 by Jeffrey Martin. All rights reserved. +Website: https://www.jeffreymartincomposer.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "SC_PlugIn.h" +#include "fir.hpp" + +InterfaceTable *ft; + +PluginLoad(flex_filters) { + ft = inTable; + registerUnit(ft, "FIR", false); +} diff --git a/src/filters/filters.sc b/src/filters/filters.sc new file mode 100644 index 0000000..d0038bc --- /dev/null +++ b/src/filters/filters.sc @@ -0,0 +1,45 @@ +// File: filters.sc +// Author: Jeff Martin +// +// Description: +// A collection of SuperCollider filter UGens +// +// Copyright © 2026 by Jeffrey Martin. All rights reserved. +// Website: https://www.jeffreymartincomposer.com +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +FIR : UGen { + *ar { + arg coefs, in, mul=1, add=0; + coefs = coefs.multichannelExpandRef(1); + ^this.multiNewList(['audio', in, coefs]).madd(mul, add) + } + *kr { + arg coefs, in, mul=1, add=0; + coefs = coefs.multichannelExpandRef(1); + ^this.multiNewList(['control', in, coefs]).madd(mul, add) + } + *new1 { + arg rate, in, coefs; + var derefCoefs; + derefCoefs = coefs.dereference; + derefCoefs = derefCoefs.flop.flat; + ^super.new.rate_(rate).addToSynth.init([in] ++ derefCoefs) + } + init { + arg theInputs; + inputs = theInputs; + } +} \ No newline at end of file diff --git a/src/filters/fir.cpp b/src/filters/fir.cpp new file mode 100644 index 0000000..c4216e1 --- /dev/null +++ b/src/filters/fir.cpp @@ -0,0 +1,59 @@ +/* +File: fir.cpp +Author: Jeff Martin + +Description: +A raw FIR filter + +Copyright © 2026 by Jeffrey Martin. All rights reserved. +Website: https://www.jeffreymartincomposer.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "fir.hpp" +extern InterfaceTable *ft; + +FIR::FIR() { + m_z = static_cast(RTAlloc(mWorld, fullBufferSize() * sizeof(float))); + for (size_t i = 0; i < fullBufferSize(); i++) { + m_z[i] = 0.f; + } + set_calc_function(); + next(1); +} + +FIR::~FIR() { + if (m_z) RTFree(mWorld, m_z); +} + +void FIR::next(int inNumSamples) { + size_t numCoefs = static_cast(mNumInputs - 1); + numCoefs = sc_clip(numCoefs, 0, static_cast(fullBufferSize())); + const float *inBuf = in(0); + float *outBuf = out(0); + for (size_t xxi = 0; xxi < inNumSamples; xxi++) { + float convResult = 0.f; + // unit delay + for (size_t xxj = fullBufferSize() - 1; xxj > 0; xxj--) { + m_z[xxj] = m_z[xxj-1]; + } + m_z[0] = inBuf[xxi]; + // convolve + for (size_t xxk = 0; xxk < numCoefs; xxk++) { + convResult += in0(1+xxk) * m_z[xxk]; + } + outBuf[xxi] = convResult; + } +} \ No newline at end of file diff --git a/src/filters/fir.hpp b/src/filters/fir.hpp new file mode 100644 index 0000000..17368b7 --- /dev/null +++ b/src/filters/fir.hpp @@ -0,0 +1,38 @@ +/* +File: fir.hpp +Author: Jeff Martin + +Description: +A raw FIR filter + +Copyright © 2026 by Jeffrey Martin. All rights reserved. +Website: https://www.jeffreymartincomposer.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include "SC_PlugIn.hpp" + +class FIR : public SCUnit { +public: + FIR(); + ~FIR(); + +private: + void next(int inNumSamples); + float *m_z; + size_t m_delaySize; +}; \ No newline at end of file