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