From 24856718261c475c080badc5d27eb2995baa8e8a Mon Sep 17 00:00:00 2001 From: Bart Brouns Date: Sat, 13 Jun 2026 18:50:40 +0200 Subject: [PATCH] quad-morph-filter: init at 1.0.0 --- .../filtervisualizer-clamp-width.patch | 38 ++++++ pkgs/by-name/qu/quad-morph-filter/package.nix | 126 ++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 pkgs/by-name/qu/quad-morph-filter/filtervisualizer-clamp-width.patch create mode 100644 pkgs/by-name/qu/quad-morph-filter/package.nix diff --git a/pkgs/by-name/qu/quad-morph-filter/filtervisualizer-clamp-width.patch b/pkgs/by-name/qu/quad-morph-filter/filtervisualizer-clamp-width.patch new file mode 100644 index 000000000000..0a223d02cb47 --- /dev/null +++ b/pkgs/by-name/qu/quad-morph-filter/filtervisualizer-clamp-width.patch @@ -0,0 +1,38 @@ +diff --git a/Source/UI/FilterVisualizer.cpp b/Source/UI/FilterVisualizer.cpp +index 103da54..c96a62a 100644 +--- a/Source/UI/FilterVisualizer.cpp ++++ b/Source/UI/FilterVisualizer.cpp +@@ -151,9 +151,15 @@ void FilterVisualizer::paint(juce::Graphics& g) + + wA = wMixArr[0]; wB = wMixArr[1]; wC = wMixArr[2]; wD = wMixArr[3]; + // ----- 周波数応答の計算 ----- +- int wInt = (int)w; ++ // The standalone window is resizable, and on HiDPI / large / tiled displays ++ // the visualizer is far wider than the design's assumed ~700px. Clamp the ++ // working width to the rawMag capacity: an unclamped px loop writes past the ++ // end of rawMag and corrupts the adjacent editor members (the next member, ++ // groupA.enableButton, is child #2 of the editor), which later crashes in ++ // Component::paintComponentAndChildren via a dangling affineTransform. ++ const int wInt = juce::jmin ((int) w, (int) rawMag.size() - 1); + // rawMag はメンバー変数 (paint()内allocを回避)。使用範囲だけゼロクリア。 +- std::fill(rawMag.begin(), rawMag.begin() + juce::jmin(wInt + 2, 1024), 0.0f); ++ std::fill(rawMag.begin(), rawMag.begin() + juce::jmin(wInt + 2, (int) rawMag.size()), 0.0f); + for (int px = 0; px <= wInt; ++px) + { + float freq = 20.0f * std::pow(1000.0f, (float)px / w); +diff --git a/Source/UI/FilterVisualizer.h b/Source/UI/FilterVisualizer.h +index 05a193b..4854851 100644 +--- a/Source/UI/FilterVisualizer.h ++++ b/Source/UI/FilterVisualizer.h +@@ -25,8 +25,9 @@ private: + QuadMorphFilterAudioProcessorEditor* editor = nullptr; + + // paint() 内アロケーション禁止 (CLAUDE.md §4) のためメンバーに移行。 +- // FilterVisualizer の幅は最大 ~700px なので 1024 で十分。 +- std::array rawMag {}; ++ // Sized to cover wide / HiDPI windows; paint() additionally clamps the ++ // working width to this capacity so the buffer can never be overrun. ++ std::array rawMag {}; + + // Background color randomization + juce::Colour bgColour = juce::Colour(0xff2a2a2a); // Default dark color diff --git a/pkgs/by-name/qu/quad-morph-filter/package.nix b/pkgs/by-name/qu/quad-morph-filter/package.nix new file mode 100644 index 000000000000..f3949e170a63 --- /dev/null +++ b/pkgs/by-name/qu/quad-morph-filter/package.nix @@ -0,0 +1,126 @@ +{ + lib, + stdenv, + fetchFromGitHub, + + cmake, + pkg-config, + + alsa-lib, + freetype, + juce, + xsimd, + + libx11, + libxcursor, + libxext, + libxinerama, + libxrandr, + + nix-update-script, +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "quad-morph-filter"; + version = "1.0.0"; + + src = fetchFromGitHub { + owner = "OTODESK4193"; + repo = "QuadMorphFilter"; + tag = "V${finalAttrs.version}"; + hash = "sha256-xz97h8j411r7g6fSVDo8E4ARLyIP++qK26aTvDqP+Yo="; + }; + + __structuredAttrs = true; + strictDeps = true; + + # FilterVisualizer writes a per-pixel response curve into a fixed + # std::array, assuming the editor is <=1024px wide. The standalone + # window is resizable (HiDPI/large/tiled displays make it far wider), so the + # write overruns the array and smashes adjacent editor members -> SIGSEGV on the + # first paint. Clamp the width to the buffer and size the buffer for wide windows. + # see https://github.com/OTODESK4193/QuadMorphFilter/issues/2 + patches = [ ./filtervisualizer-clamp-width.patch ]; + + postPatch = '' + # Use JUCE from nixpkgs instead of a hardcoded Windows path, and drop + # -mavx2/-mfma: baseline x86_64-linux has no AVX2 and other platforms + # reject the flags. The code contains no AVX intrinsics; it compiles + # and runs fine without them. + substituteInPlace CMakeLists.txt \ + --replace-fail 'set(JUCE_DIR "C:/JUCE")' 'find_package(JUCE CONFIG REQUIRED)' \ + --replace-fail 'add_subdirectory(''${JUCE_DIR} build_juce)' "" \ + --replace-fail ' -mavx2 -mfma' "" \ + --replace-fail ' -ffast-math' "" + + # The Linux WebBrowserComponent (gtk3/webkit) and curl are unused by the + # plugin; disable them instead of pulling the dependencies in. + printf '\ntarget_compile_definitions(QuadMorphFilter PUBLIC JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0)\n' >> CMakeLists.txt + ''; + + nativeBuildInputs = [ + cmake + pkg-config + ]; + buildInputs = [ + alsa-lib + freetype + juce # propagates fontconfig + libx11 + libxcursor + libxext + libxinerama + libxrandr + xsimd + ]; + + cmakeFlags = [ + # Upstream pulls xsimd via FetchContent; resolve it through + # find_package against the xsimd in buildInputs instead of the network. + (lib.cmakeFeature "FETCHCONTENT_TRY_FIND_PACKAGE_MODE" "ALWAYS") + ]; + + cmakeBuildType = "RelWithDebInfo"; + + # JUCE dlopens these at runtime, standalone executable crashes without them. + # Must go through `env`: with __structuredAttrs only `env` entries are + # exported, so a top-level NIX_LDFLAGS never reaches the ld wrapper. + env.NIX_LDFLAGS = toString [ + "-lX11" + "-lXext" + "-lXcursor" + "-lXinerama" + "-lXrandr" + ]; + + # Upstream has no install rules. + installPhase = '' + runHook preInstall + + install -Dm755 "QuadMorphFilter_artefacts/$cmakeBuildType/Standalone/Quad-Morph Filter" \ + "$out/bin/quad-morph-filter" + mkdir -p "$out/lib/vst3" + cp -r "QuadMorphFilter_artefacts/$cmakeBuildType/VST3/Quad-Morph Filter.vst3" "$out/lib/vst3/" + + runHook postInstall + ''; + + passthru.updateScript = nix-update-script { + extraArgs = [ + "--version-regex" + "V(.*)" + ]; + }; + + meta = { + description = "Morphing multi-filter audio plugin with 28 filter models controlled from an XY pad"; + homepage = "https://github.com/OTODESK4193/QuadMorphFilter"; + changelog = "https://github.com/OTODESK4193/QuadMorphFilter/releases/tag/V${finalAttrs.version}"; + # Stated in README.md ("GPLv3, inherited via the JUCE framework"); + # the repository ships no LICENSE file. + license = lib.licenses.gpl3Only; + maintainers = with lib.maintainers; [ magnetophon ]; + platforms = lib.platforms.linux; + mainProgram = "quad-morph-filter"; + }; +})