• Volume Doesn’t Change

    General Questions
    3
    0 Votes
    3 Posts
    43 Views
    griffinboyG

    @justinfitzmusic

    The green marker is just showing you the velocity values the sampler is "receiving"

    It won't actually do anything with that received information, unless you do something like @Lindon said: add a velocity modulator to modulate something!

    For volume, just apply a velocity modulator to gain:
    602bdb89-e71b-4534-841d-4810c02eca1d-image.png

  • Custom filter graph output within a custom node?

    C++ Development
    4
    0 Votes
    4 Posts
    26 Views
    griffinboyG

    @Orvillain

    Yes that's right.
    By the way it won't work for nonlinear filters, it's designed to get the frequency response from simple analytic (linear) filters. And so if you want to draw any complex filter responses, you'll have to approximate the frequency response using a different cheap filter (see below)

    Also if its a polyphonic filter, you might want to make sure that only one persistent graphing filter instance exists, rather than having the graph be fed by a bank of polyphonic filters all fighting for attention. You can then use the hise voice system to decide which voices control the graph (first voice only, or latest voice only) that kind of thing.

    Here is how I do it currently for my filters, although there is talk of this graphing system being updated for Hise in the future, so this might all change...

    (simplified cut down code excerpt from one of my large experimental filter nodes)

    // ==============================| Griffin Poly EQ Filter |=================================== // // File: Griffin_EQFilter_Poly.h // Node: Griffin_EQFilter_Poly // Package: Griffin DSP Essentials for HISE // Author: Griffinboy // HISE Forum: https://forum.hise.audio/user/griffinboy // Copyright: Copyright (c) 2026 Griffinboy // // Description: // Minimum-phase EQ filter with per-voice state and frame processing. // Designed for sample-accurate modulation in synthesis contexts. // Uses more CPU than the block-rate Griffin_EQFilter. // // Has Mod slots for frequency, Q, and gain. // // License: // GNU General Public License v3.0 or later (GPL-3.0-or-later). // // This file is part of Griffin DSP Essentials for HISE. // // 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 <https://www.gnu.org/licenses/>. // // ================================================================================================ // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include <algorithm> #include <cmath> #include <JuceHeader.h> #include "src/griffinboy/modules/essentials/eq_filter/eq_filter.h" namespace project { using namespace juce; using namespace hise; using namespace scriptnode; template <int NV> struct Griffin_EQFilter_Poly: public data::filter_node_base { using VoiceState = griffin::modules::essentials::eq_filter::EQFilterFrameProcessor; using PlotModel = griffin::modules::essentials::eq_filter::EQPlotModel; using Defaults = griffin::modules::essentials::eq_filter::EQFilterDefaults; PolyData<VoiceState, NV> voices; PlotModel plotModel; SimpleReadWriteLock topologyLock; double plotSampleRate = 44100.0; SNEX_NODE(Griffin_EQFilter_Poly); struct MetadataClass { SN_NODE_ID("Griffin_EQFilter_Poly"); }; static constexpr bool isModNode() { return false; }; static constexpr bool isPolyphonic() { return NV > 1; }; static constexpr bool hasTail() { return true; }; static constexpr bool isSuspendedOnSilence() { return true; }; static constexpr int getFixChannelAmount() { return 2; }; static constexpr int NumTables = 0; static constexpr int NumSliderPacks = 0; static constexpr int NumAudioFiles = 0; static constexpr int NumFilters = 1; static constexpr int NumDisplayBuffers = 0; void prepare(PrepareSpecs specs) { SimpleReadWriteLock::ScopedWriteLock sl(topologyLock); voices.prepare(specs); for (auto& voice : voices) voice.prepare(specs.sampleRate, specs.blockSize); plotModel.prepare(specs.sampleRate); plotSampleRate = specs.sampleRate > 0.0 ? specs.sampleRate : 44100.0; if (auto fd = dynamic_cast<FilterDataObject*>(this->externalData.obj)) fd->setSampleRate(plotSampleRate); sendCoefficientUpdateMessage(); voiceManager.prepare(specs); voiceManager.setActive(1.0); } void reset() { SimpleReadWriteLock::ScopedWriteLock sl(topologyLock); for (auto& voice : voices) voice.reset(); voiceManager.reset(); } void handleHiseEvent(HiseEvent& e) { voiceManager.handleHiseEvent(e); } template <typename T> void process(T& data) { if (auto sl = SimpleReadWriteLock::ScopedTryReadLock(topologyLock)) { static constexpr int NumChannels = getFixChannelAmount(); auto& fixData = data.template as<ProcessData<NumChannels>>(); auto& voice = voices.get(); auto fd = fixData.toFrameData(); while (fd.next()) voice.processFrame(fd.toSpan()); voiceManager.process(data); } } template <typename T> void processFrame(T& data) { if (auto sl = SimpleReadWriteLock::ScopedTryReadLock(topologyLock)) voices.get().processFrame(data); } int handleModulation(double& value) { ignoreUnused(value); return 0; } double getPlotValue(int getMagnitude, double freqNorm) override { if (getMagnitude == 0) return 0.0; const auto frequency = std::clamp(freqNorm, 0.0, 0.5) * plotSampleRate; return plotModel.getMagnitudeAtFrequency(frequency); } void setExternalData(const ExternalData& data, int index) { data::filter_node_base::setExternalData(data, index); ignoreUnused(index); if (auto fd = dynamic_cast<FilterDataObject*>(data.obj)) fd->setSampleRate(plotSampleRate); sendCoefficientUpdateMessage(); } void createExternalModulationInfo(OpaqueNode::ModulationProperties& info) { modulation::ParameterProperties::ConnectionList list; auto addParameterSlot = [&list](int parameterIndex) { modulation::ConnectionInfo slot; slot.connectedParameterIndex = parameterIndex; slot.modColour = HiseModulationColours::ColourId::FX; slot.connectionMode = modulation::ConnectionMode::Parameter; slot.modulationMode = modulation::ParameterMode::ScaleAdd; list.push_back(slot); }; addParameterSlot(2); addParameterSlot(3); addParameterSlot(4); info.fromConnectionList(list); info.setModulationBlockSize(Defaults::modulationBlockSize); } template <int P> void setParameter(double v) { if constexpr (P == 0) { SimpleReadWriteLock::ScopedWriteLock sl(topologyLock); const auto nextShape = (int)std::round(v); applyToVoices( [nextShape](VoiceState& filter) { filter.setType(nextShape); }); updatePlotFromFirstVoice( [this, nextShape] { plotModel.setType(nextShape); }); } else if constexpr (P == 1) { SimpleReadWriteLock::ScopedWriteLock sl(topologyLock); const auto nextSlope = (int)std::round(v); applyToVoices( [nextSlope](VoiceState& filter) { filter.setSlopeMode(nextSlope); }); updatePlotFromFirstVoice( [this, nextSlope] { plotModel.setSlopeMode(nextSlope); }); } else if constexpr (P == 2) { const auto value = (float)v; applyToVoices( [value](VoiceState& filter) { filter.setFrequencyHz(value); }); updatePlotFromFirstVoice( [this, value] { plotModel.setFrequencyHz(value); }); } else if constexpr (P == 3) { const auto value = (float)v; applyToVoices( [value](VoiceState& filter) { filter.setQ(value); }); updatePlotFromFirstVoice( [this, value] { plotModel.setQ(value); }); } else if constexpr (P == 4) { const auto value = (float)v; applyToVoices( [value](VoiceState& filter) { filter.setGainDb(value); }); updatePlotFromFirstVoice( [this, value] { plotModel.setGainDb(value); }); } } void createParameters(ParameterDataList& data) { { parameter::data p("Shape", { 0.0, 6.0, 1.0 }); StringArray names; addTypeLabels(names); p.setParameterValueNames(names); registerCallback<0>(p); p.setDefaultValue((double)Defaults::type); data.add(std::move(p)); } { parameter::data p("Slope", { 0.0, 3.0, 1.0 }); StringArray names; addSlopeLabels(names); p.setParameterValueNames(names); registerCallback<1>(p); p.setDefaultValue((double)Defaults::slopeMode); data.add(std::move(p)); } { parameter::data p("Frequency", { 20.0, 20000.0, 0.01 }); p.setSkewForCentre(1000.0); p.info.textConverter = parameter::pod::TextValueConverters::Frequency; registerCallback<2>(p); p.setDefaultValue(Defaults::frequencyHz); data.add(std::move(p)); } { parameter::data p("Q", { 0.025, 25.0, 0.001 }); p.setSkewForCentre(0.70710678); registerCallback<3>(p); p.setDefaultValue(Defaults::q); data.add(std::move(p)); } { parameter::data p("Gain", { -30.0, 30.0, 0.01 }); p.info.textConverter = parameter::pod::TextValueConverters::Decibel; registerCallback<4>(p); p.setDefaultValue(Defaults::gainDb); data.add(std::move(p)); } } private: static constexpr int getNumTypes() noexcept { return griffin::modules::essentials::eq_filter::EQFilterDesign::getNumTypes(); } template <typename Function> void applyToVoices(Function&& function) { // PolyData iteration follows HISE's poly callback. // Each active voice receives the parameter update in its own DSP state. for (auto& voice : voices) function(voice); } template <typename Function> void updatePlotFromFirstVoice(Function&& function) { // The plot is shared UI state. The first HISE voice owns graph updates // so voices do not compete for the displayed response. if (! voices.isVoiceRenderingActive() || voices.isFirst()) { function(); sendCoefficientUpdateMessage(); } } static void addTypeLabels(StringArray& names) { names.add("Lowpass"); names.add("Highpass"); names.add("Bandpass"); names.add("Notch"); names.add("Bell"); names.add("Low Shelf"); names.add("High Shelf"); } static void addSlopeLabels(StringArray& names) { names.add("12 dB/oct"); names.add("24 dB/oct"); names.add("48 dB/oct"); names.add("96 dB/oct"); } envelope::silent_killer<NV> voiceManager; }; } // namespace project
  • 0 Votes
    1 Posts
    9 Views
    No one has replied
  • 0 Votes
    9 Posts
    96 Views
    O

    @David-Healey What errors do you mean?

  • Installing Hise on Mac Tahoe

    General Questions
    5
    0 Votes
    5 Posts
    35 Views
    S

    @David-Healey Of course! Silly me.....

  • [feature request] Network connectivity checker

    Feature Requests
    7
    5 Votes
    7 Posts
    537 Views
    ustkU

    @David-Healey so there's no way my server could be faster, especially since it is actually very slow...

  • Directory.hasWriteAccess()

    General Questions
    12
    0 Votes
    12 Posts
    82 Views
    LindonL

    @David-Healey yeah that seems more comprehensive, its more or less doing under the hood what Im doing in HISEScript... so both approaches have the advantage of getting a result back for the plugin itself (no matter how the user has set up to run it..)

    Thanks for looking and confirming what I'd found.

  • 0 Votes
    29 Posts
    415 Views
    Y

    @dannytaurus It working soo fine, I had to put engine.loadaudiofilesintopool earlier

  • Matrix modulation connection is broken in exported plugin

    Bug Reports
    47
    0 Votes
    47 Posts
    3k Views
    griffinboyG

    @ustk Thanks for your hard work!

  • 0 Votes
    29 Posts
    543 Views
    MorphoiceM

    @Orvillain ill give that a try thanks. I don't have claude code though. I cant afford paid services at the moment

  • Second fix for MIDI device reselect bug 🐞

    General Questions
    1
    1 Votes
    1 Posts
    40 Views
    No one has replied
  • 7 Votes
    4 Posts
    137 Views
    OrvillainO

    Very cool man!

  • Building Hise github

    General Questions
    14
    0 Votes
    14 Posts
    262 Views
    Y

    @David-Healey It was already default to 500 mb in 4.1

  • Control ScriptNode from UI knob AND envelope?

    ScriptNode
    6
    0 Votes
    6 Posts
    155 Views
    Christoph HartC

    It depends whether you want it to be a static connection or a dynamic modulation routing.

    If static, then a control.pma node would be the best candidate - connect the UI knob to the value and the envelope output (either from an extra_mod or an inbuilt scriptnode AHDSR) to the add output, this will combine the two with unipolar modulation mode.

    Scales and offsets a normalised modulation signal using a multiply-add formula with clamped output.
    The PMA node (Parameter Multiply Add) scales and offsets a normalised modulation signal. It takes a 0-1 input value, multiplies it by a configurable factor, adds a constant offset, and clamps the result to the 0-1 range. This is one of the most commonly used control nodes for adjusting modulation depth, inverting signals, or combining parameters.

    Each parameter change triggers an independent output update. If Value, Multiply, and Add all change in sequence, three separate output values are sent to connected targets.

  • sync faust delay times to host

    General Questions
    4
    0 Votes
    4 Posts
    119 Views
    Christoph HartC

    I'm not sure whether MIDI tempo information messages makes it through to the Faust node, but the tempo sync node is definitely the way to go. just build your Faust node with a absolute delay time parameter and then connect it to the tempo sync node - it automatically sends the correct time value matching the tempo and reacts to tempo changes etc.

    upcoming docs:

    Tempo Sync (control.tempo_sync)

    Converts a musical tempo value to a duration in milliseconds and sends it as a modulation signal.
    Tempo Sync converts a musical time value (such as 1/4 note or 1/8 triplet) to a duration in milliseconds based on the current DAW tempo. The output updates whenever the host tempo changes or any parameter is adjusted, making it suitable for driving time-based effects that need to lock to the beat.

    The output is an unnormalised modulation signal carrying the raw millisecond value. If the target parameter expects a different unit (such as frequency in Hz), place a control.converter between this node and the target. When Enabled is set to Off, the node outputs the manual UnsyncedTime value instead, allowing a smooth fallback for standalone operation or manual control.

    CPU: negligible, polyphonic.

    Signal Path

    Pseudo-code - hover highlighted terms for details // control.tempo_sync - musical time to milliseconds // BPM + parameters -> ms out (unnormalised) onParameterChange() { if Enabled: output = tempoToMs(bpm, Tempo) * Multiplier else: output = UnsyncedTime } onTempoChange(newBpm) { bpm = newBpm if Enabled: output = tempoToMs(bpm, Tempo) * Multiplier }
  • Verb Factory

    C++ Development
    8
    4 Votes
    8 Posts
    203 Views
    griffinboyG

    @Orvillain Nice work

  • Modulating ShapeFX Gain

    General Questions
    8
    0 Votes
    8 Posts
    184 Views
    dannytaurusD

    @HISEnberg @Christoph-Hart I hit a problem with controlling a ScriptNode macro with a UI knob AND an envelope.

    New post here: https://forum.hise.audio/topic/14793/control-scriptnode-from-ui-knob-and-envelope

  • Thoughts on Moonbase?

    General Questions
    18
    0 Votes
    18 Posts
    603 Views
    J

    @David-Healey cool cool
    I did make just a licensing system with traditional serial keys...so Ill probably just use that for now, and then update.
    and I finally actually fininshed making this instrument😁

    now just working on some vids and demos and final testing. but ill include some screenshots here.
    Its just a sample based synth, where i sampled the f-k out of a LYRA 8 synthesizer..
    recorded through some amps and speaker cab impulse responses etc.
    Screenshot 2026-05-29 at 2.41.06 PM.png Screenshot 2026-05-29 at 2.41.18 PM.png

  • Get Panel Attributes - Colours

    Solved Scripting
    11
    0 Votes
    11 Posts
    272 Views
    ustkU

    @David-Healey There might be a bad parsing at some point. Without really knowing, the hex might be first parsed as string by the property editor...

  • 32bit macOS flag

    General Questions
    3
    0 Votes
    3 Posts
    136 Views
    David HealeyD

    @Lurch Not sure if it affects compiling networks