HISE Logo Forum
    • Categories
    • Register
    • Login

    Custom Parametric EQ Display using DraggableFilterPanel

    Scheduled Pinned Locked Moved General Questions
    5 Posts 2 Posters 80 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • P
      pandaz21
      last edited by

      I am trying to design a parametric EQ plugin from scratch. The DSP network has nine biquad filters, corresponding to up to nine allowed bands in the EQ. Each parameter (frequency, gain, Q, Enabled) in each filter has a corresponding knob in the overarching parameter hub. The UI has nine buttons corresponding to up to nine bands, frequency, gain, and Q knobs, Create and Delete buttons for adding a removing bands from the EQ display, and a ComboBox which I will later turn into a dropdown menu for selecting the band shape. I have gotten all of the UI components to work (except the dropdown menu, which I will worry about later), but I have been struggling to get the EQ points and curve to display nicely in the DraggableFilterPanel. I've attached pictures of my UI and DSP network, as well as my code in onInit and onControl tabs below.
      Screenshot 2025-08-25 193117.png Screenshot 2025-08-25 194811.png Screenshot 2025-08-25 194833.png Screenshot 2025-08-25 194848.png
      Some technical notes about the code:

      1. The tile is intentionally kept in IdPattern mode so that it binds directly to the Parameter Hub attributes (FrequencyN, GainN, QN, EnabledN). I do not use the custom data block that creates default bands; instead, I only set ParameterOrder, DragActions, and Range via setDraggableFilterData so that the axes match the knob ranges and the mapping is correct (X = Frequency, Y = Gain).
      2. The hub resolver works by scanning fx.getAttributeId(i) to find the correct indices for each band’s parameters (FrequencyN, GainN, QN, and EnabledN). This method is more reliable across different HISE builds than calling getAttributeIndex directly.
      3. The Create and Delete buttons simply flip the EnabledN parameter in the hub, which in turn makes the panel show or hide the corresponding points.
      4. The knobs update the Parameter Hub directly using helper functions that write into FrequencyN, GainN, and QN. Because the panel is bound via IdPatterns, the dots move in response to knob changes. When switching between bands, an updateKnobs() function reads back the current hub values so the knobs always reflect the active point’s state.
      5. If needed, a sync timer could also be added so that dragging points on the panel updates the knobs in real time.

      I've only started using HISE about 2 weeks ago, but I have been learning how the different modules interact. I've tried my best to draw from the DraggableFilterPanel example snippet, but so far I've been unsuccessful in getting the EQ curve display to work. I'd greatly appreciate any advice/tips for troubleshooting!

      // ============================================================================
      // Parametric EQ UI (9 bands) wired to a Parameter Hub + DraggableFilterPanel
      // - Hub exposes: Frequency1..9, Gain1..9, Q1..9, Enabled1..9
      // - The FloatingTile DraggableFilterPanel binds via IdPatterns
      // - UI radio buttons select an active band; Create/Delete toggle EnabledN
      // - Knobs write to hub (Freq/Gain/Q) and the panel follows
      // ============================================================================

      Content.makeFrontInterface(800, 600);

      // Reference to the Script FX that owns the Parameter Hub attributes
      const var fx = Synth.getEffect("Script FX1");

      // --- UI Components -----------------------------------------------------------
      const var buttons = [];
      for (i = 0; i < 9; i++) buttons[i] = Content.getComponent("Button" + (i + 1));

      const var freqKnob = Content.getComponent("Knob1");
      const var gainKnob = Content.getComponent("Knob2");
      const var qKnob = Content.getComponent("Knob3");
      const var create = Content.getComponent("Button10");
      const var delBtn = Content.getComponent("Button11");

      // Knob ranges must match panel ranges (see setDraggableFilterData below)
      freqKnob.set("min", 20); freqKnob.set("max", 20000); freqKnob.set("defaultValue", 1000); freqKnob.set("mode", "Frequency"); freqKnob.set("suffix", " Hz");
      gainKnob.set("min", -24); gainKnob.set("max", 24); gainKnob.set("defaultValue", 0); gainKnob.set("mode", "Linear"); gainKnob.set("suffix", " dB");
      qKnob.set("min", 0.1); qKnob.set("max", 10); qKnob.set("defaultValue", 1); qKnob.set("mode", "Linear");

      // --- Local model of band state (for button visuals + selection) -------------
      var activeBand = 0;
      var bandParameters = [];
      for (i = 0; i < 9; i++)
      bandParameters[i] = { freq: 1000, gain: 0, q: 1, active: (i == 0), Enabled: false };

      // --- Hub attribute resolver (by name) ---------------------------------------
      const var KIND = ["Frequency","Gain","Q","Enabled"];
      const var KIND_FREQ = 0, KIND_GAIN = 1, KIND_Q = 2, KIND_ENABLED = 3;

      // Cache of resolved attribute indices: paramIdx[band][kind]
      var paramIdx = [];
      for (i = 0; i < 9; i++) { paramIdx[i] = []; for (j = 0; j < 4; j++) paramIdx[i][j] = -1; }

      // Resolve "" to numeric attribute index by scanning attribute names.
      // (Robust across HISE versions where getAttributeIndex(name) may differ)
      inline function resolveIndex(b, k)
      {
      reg bandNum, wanted, N, ii;
      bandNum = b + 1;
      wanted = KIND[k] + bandNum; // e.g. "Frequency1"
      N = fx.getNumAttributes();

      for (ii = 0; ii < N; ii++)
          if (fx.getAttributeId(ii) == wanted) { paramIdx[b][k] = ii; return ii; }
      
      return -1; // don't cache a miss to allow re-try later
      

      }

      inline function idxOf(b, k)
      {
      reg idx;
      idx = paramIdx[b][k];
      if (idx == -1) idx = resolveIndex(b, k);
      return idx;
      }

      inline function setP(b, k, v) { reg id = idxOf(b, k); if (id != -1) fx.setAttribute(id, v); }
      inline function getP(b, k) { reg id = idxOf(b, k); if (id != -1) return fx.getAttribute(id); return undefined; }

      // --- DraggableFilterPanel binding -------------------------------------------
      // Keep the tile in IdPattern mode but align axes/ranges to the knobs.
      fx.setDraggableFilterData({
      ParameterOrder: ["Gain","Freq","Q","Enabled"], // logical param order
      DragActions: { "DragX":"Freq", "DragY":"Gain", "ShiftDrag":"Q", "DoubleClick":"", "RightClick":"" },
      Range: { // visible grid ranges
      "Gain": { "min": -24.0, "max": 24.0 },
      "Frequency": { "min": 20.0, "max": 20000.0 },
      "Q": { "min": 0.1, "max": 10.0 }
      }
      });

      const var eqPanel = Content.getComponent("FloatingTile2");
      const var ft_data = {
      "Type": "DraggableFilterPanel",
      "ProcessorId": "Script FX1", // must match the Script FX name
      "ItemCount": 9,
      "FrequencyIdPattern": "Frequency$N",
      "GainIdPattern": "Gain$N",
      "QIdPattern": "Q$N",
      "EnabledIdPattern": "Enabled$N",
      "AllowAdd": false,
      "AllowRemove": false
      };
      eqPanel.setContentData(ft_data);

      // --- UI helpers --------------------------------------------------------------
      function updateButtonAppearance()
      {
      for (i = 0; i < 9; i++)
      {
      if (bandParameters[i].Enabled)
      {
      buttons[i].set("enabled", 1);
      buttons[i].setColour(0, 0xFFFFFFFF);
      buttons[i].setColour(1, 0xFF888888);
      }
      else
      {
      buttons[i].set("enabled", 0); // ignore clicks on disabled bands
      buttons[i].setValue(0);
      buttons[i].setColour(0, 0xFF444444);
      buttons[i].setColour(1, 0xFF666666);
      }
      }
      }

      inline function setBandEnabled(idx, on)
      {
      setP(idx, KIND_ENABLED, on ? 1 : 0); // EnabledN in the hub
      bandParameters[idx].Enabled = on;
      updateButtonAppearance();
      }

      inline function initBandParams(idx, f, g, qv)
      {
      setP(idx, KIND_FREQ, f);
      setP(idx, KIND_GAIN, g);
      setP(idx, KIND_Q, qv);
      bandParameters[idx].freq = f;
      bandParameters[idx].gain = g;
      bandParameters[idx].q = qv;
      }

      inline function findFirstInactiveBand()
      {
      for (i = 0; i < 9; i++) if (!bandParameters[i].Enabled) return i;
      return -1;
      }

      inline function findNextActiveBand(fromIdx)
      {
      reg k, idx;
      for (k = 1; k <= 9; k++)
      {
      idx = (fromIdx + k) % 9;
      if (bandParameters[idx].Enabled) return idx;
      }
      return -1;
      }

      // Read the current hub values into the knobs when switching bands
      inline function updateKnobs()
      {
      reg f, g, qv;
      f = getP(activeBand, KIND_FREQ);
      g = getP(activeBand, KIND_GAIN);
      qv = getP(activeBand, KIND_Q);

      if (f != undefined)  freqKnob.setValue(f);
      if (g != undefined)  gainKnob.setValue(g);
      if (qv != undefined) qKnob.setValue(qv);
      
      bandParameters[activeBand].freq = freqKnob.getValue();
      bandParameters[activeBand].gain = gainKnob.getValue();
      bandParameters[activeBand].q    = qKnob.getValue();
      

      }

      function storeBandParameters()
      {
      bandParameters[activeBand].freq = freqKnob.getValue();
      bandParameters[activeBand].gain = gainKnob.getValue();
      bandParameters[activeBand].q = qKnob.getValue();
      }

      function switchToBand(bandIndex)
      {
      if (bandIndex < 0 || bandIndex >= 9) return;
      storeBandParameters();
      for (i = 0; i < 9; i++) { bandParameters[i].active = (i == bandIndex); buttons[i].setValue(i == bandIndex ? 1 : 0); }
      activeBand = bandIndex;
      updateKnobs();
      }

      // --- Initial state: pull hub values and sync buttons/knobs -------------------
      for (i = 0; i < 9; i++)
      {
      // resolve & read once (ok if some bands don't exist yet)
      var idF = idxOf(i, KIND_FREQ);
      var idG = idxOf(i, KIND_GAIN);
      var idQ = idxOf(i, KIND_Q);
      var idE = idxOf(i, KIND_ENABLED);

      if (idF != -1) bandParameters[i].freq    = fx.getAttribute(idF);
      if (idG != -1) bandParameters[i].gain    = fx.getAttribute(idG);
      if (idQ != -1) bandParameters[i].q       = fx.getAttribute(idQ);
      if (idE != -1) bandParameters[i].Enabled = fx.getAttribute(idE) > 0;
      

      }

      updateButtonAppearance();
      switchToBand(0);


      function onControl(component, value)
      {
      // Radio buttons: only allow selecting enabled bands
      for (i = 0; i < 9; i++)
      {
      if (component == buttons[i])
      {
      if (!bandParameters[i].Enabled) { buttons[i].setValue(0); return; }
      if (value > 0) { switchToBand(i); }
      return;
      }
      }

      // Knobs write directly to the Parameter Hub (panel follows)
      if (component == freqKnob)      { setP(activeBand, KIND_FREQ, value); bandParameters[activeBand].freq = value; }
      else if (component == gainKnob) { setP(activeBand, KIND_GAIN, value); bandParameters[activeBand].gain = value; }
      else if (component == qKnob)    { setP(activeBand, KIND_Q,    value); bandParameters[activeBand].q    = value; }
      
      // Create = find first disabled band, enable it, set defaults, select it
      if (component == create && value > 0)
      {
          create.setValue(0);
          local freeSlot = findFirstInactiveBand();
          if (freeSlot != -1)
          {
              setBandEnabled(freeSlot, true);
              initBandParams(freeSlot, 1000, 0, 1);
              switchToBand(freeSlot);
      
              // Optional: one-time UI nudge during wiring
              // eqPanel.setContentData(ft_data);
          }
          return;
      }
      
      // Delete = disable current band and select next active (if any)
      if (component == delBtn && value > 0)
      {
          delBtn.setValue(0);
      
          if (bandParameters[activeBand].Enabled)
          {
              setBandEnabled(activeBand, false);
      
              local nextBand = findNextActiveBand(activeBand);
              if (nextBand != -1) switchToBand(nextBand);
              else { buttons[activeBand].setValue(0); bandParameters[activeBand].active = false; }
      
              // Optional: one-time UI nudge during wiring
              // eqPanel.setContentData(ft_data);
          }
          return;
      }
      

      }

      ustkU 1 Reply Last reply Reply Quote 1
      • ustkU
        ustk @pandaz21
        last edited by ustk

        @pandaz21 Nice! It would be even nicer if you could post a snippet instead of the code so people can load it without having to recreate the whole beast ☺

        Hise made me an F5 dude, browser just suffers...

        P 1 Reply Last reply Reply Quote 0
        • P
          pandaz21
          last edited by

          Here's the full code again, since it got pasted awkwardly the original post.

          // ============================================================================
          // Parametric EQ UI (9 bands) wired to a Parameter Hub + DraggableFilterPanel
          // - Hub exposes: Frequency1..9, Gain1..9, Q1..9, Enabled1..9
          // - The FloatingTile DraggableFilterPanel binds via IdPatterns
          // - UI radio buttons select an active band; Create/Delete toggle EnabledN
          // - Knobs write to hub (Freq/Gain/Q) and the panel follows
          // ============================================================================
          
          Content.makeFrontInterface(800, 600);
          
          // Reference to the Script FX that owns the Parameter Hub attributes
          const var fx = Synth.getEffect("Script FX1");
          
          // --- UI Components -----------------------------------------------------------
          const var buttons = [];
          for (i = 0; i < 9; i++) buttons[i] = Content.getComponent("Button" + (i + 1));
          
          const var freqKnob = Content.getComponent("Knob1");
          const var gainKnob = Content.getComponent("Knob2");
          const var qKnob    = Content.getComponent("Knob3");
          const var create   = Content.getComponent("Button10");
          const var delBtn   = Content.getComponent("Button11");
          
          // Knob ranges must match panel ranges (see setDraggableFilterData below)
          freqKnob.set("min", 20);      freqKnob.set("max", 20000); freqKnob.set("defaultValue", 1000); freqKnob.set("mode", "Frequency"); freqKnob.set("suffix", " Hz");
          gainKnob.set("min", -24);     gainKnob.set("max", 24);    gainKnob.set("defaultValue", 0);    gainKnob.set("mode", "Linear");    gainKnob.set("suffix", " dB");
          qKnob.set("min", 0.1);        qKnob.set("max", 10);       qKnob.set("defaultValue", 1);       qKnob.set("mode", "Linear");
          
          // --- Local model of band state (for button visuals + selection) -------------
          var activeBand = 0;
          var bandParameters = [];
          for (i = 0; i < 9; i++)
              bandParameters[i] = { freq: 1000, gain: 0, q: 1, active: (i == 0), Enabled: false };
          
          // --- Hub attribute resolver (by name) ---------------------------------------
          const var KIND = ["Frequency","Gain","Q","Enabled"];
          const var KIND_FREQ = 0, KIND_GAIN = 1, KIND_Q = 2, KIND_ENABLED = 3;
          
          // Cache of resolved attribute indices: paramIdx[band][kind]
          var paramIdx = [];
          for (i = 0; i < 9; i++) { paramIdx[i] = []; for (j = 0; j < 4; j++) paramIdx[i][j] = -1; }
          
          // Resolve "<Kind><N>" to numeric attribute index by scanning attribute names.
          // (Robust across HISE versions where getAttributeIndex(name) may differ)
          inline function resolveIndex(b, k)
          {
              reg bandNum, wanted, N, ii;
              bandNum = b + 1;
              wanted  = KIND[k] + bandNum;          // e.g. "Frequency1"
              N       = fx.getNumAttributes();
          
              for (ii = 0; ii < N; ii++)
                  if (fx.getAttributeId(ii) == wanted) { paramIdx[b][k] = ii; return ii; }
          
              return -1; // don't cache a miss to allow re-try later
          }
          
          inline function idxOf(b, k)
          {
              reg idx;
              idx = paramIdx[b][k];
              if (idx == -1) idx = resolveIndex(b, k);
              return idx;
          }
          
          inline function setP(b, k, v) { reg id = idxOf(b, k); if (id != -1) fx.setAttribute(id, v); }
          inline function getP(b, k)    { reg id = idxOf(b, k); if (id != -1) return fx.getAttribute(id); return undefined; }
          
          // --- DraggableFilterPanel binding -------------------------------------------
          // Keep the tile in IdPattern mode but align axes/ranges to the knobs.
          fx.setDraggableFilterData({
            ParameterOrder: ["Gain","Freq","Q","Enabled"],              // logical param order
            DragActions: { "DragX":"Freq", "DragY":"Gain", "ShiftDrag":"Q", "DoubleClick":"", "RightClick":"" },
            Range: {                                                    // visible grid ranges
              "Gain": { "min": -24.0, "max": 24.0 },
              "Frequency": { "min": 20.0, "max": 20000.0 },
              "Q": { "min": 0.1, "max": 10.0 }
            }
          });
          
          const var eqPanel = Content.getComponent("FloatingTile2");
          const var ft_data = {
            "Type": "DraggableFilterPanel",
            "ProcessorId": "Script FX1",          // must match the Script FX name
            "ItemCount": 9,
            "FrequencyIdPattern": "Frequency$N",
            "GainIdPattern": "Gain$N",
            "QIdPattern": "Q$N",
            "EnabledIdPattern": "Enabled$N",
            "AllowAdd": false,
            "AllowRemove": false
          };
          eqPanel.setContentData(ft_data);
          
          // --- UI helpers --------------------------------------------------------------
          function updateButtonAppearance()
          {
              for (i = 0; i < 9; i++)
              {
                  if (bandParameters[i].Enabled)
                  {
                      buttons[i].set("enabled", 1);
                      buttons[i].setColour(0, 0xFFFFFFFF);
                      buttons[i].setColour(1, 0xFF888888);
                  }
                  else
                  {
                      buttons[i].set("enabled", 0);   // ignore clicks on disabled bands
                      buttons[i].setValue(0);
                      buttons[i].setColour(0, 0xFF444444);
                      buttons[i].setColour(1, 0xFF666666);
                  }
              }
          }
          
          inline function setBandEnabled(idx, on)
          {
              setP(idx, KIND_ENABLED, on ? 1 : 0); // EnabledN in the hub
              bandParameters[idx].Enabled = on;
              updateButtonAppearance();
          }
          
          inline function initBandParams(idx, f, g, qv)
          {
              setP(idx, KIND_FREQ, f);
              setP(idx, KIND_GAIN, g);
              setP(idx, KIND_Q,    qv);
              bandParameters[idx].freq = f;
              bandParameters[idx].gain = g;
              bandParameters[idx].q    = qv;
          }
          
          inline function findFirstInactiveBand()
          {
              for (i = 0; i < 9; i++) if (!bandParameters[i].Enabled) return i;
              return -1;
          }
          
          inline function findNextActiveBand(fromIdx)
          {
              reg k, idx;
              for (k = 1; k <= 9; k++)
              {
                  idx = (fromIdx + k) % 9;
                  if (bandParameters[idx].Enabled) return idx;
              }
              return -1;
          }
          
          // Read the current hub values into the knobs when switching bands
          inline function updateKnobs()
          {
              reg f, g, qv;
              f = getP(activeBand, KIND_FREQ);
              g = getP(activeBand, KIND_GAIN);
              qv = getP(activeBand, KIND_Q);
          
              if (f != undefined)  freqKnob.setValue(f);
              if (g != undefined)  gainKnob.setValue(g);
              if (qv != undefined) qKnob.setValue(qv);
          
              bandParameters[activeBand].freq = freqKnob.getValue();
              bandParameters[activeBand].gain = gainKnob.getValue();
              bandParameters[activeBand].q    = qKnob.getValue();
          }
          
          function storeBandParameters()
          {
              bandParameters[activeBand].freq = freqKnob.getValue();
              bandParameters[activeBand].gain = gainKnob.getValue();
              bandParameters[activeBand].q    = qKnob.getValue();
          }
          
          function switchToBand(bandIndex)
          {
              if (bandIndex < 0 || bandIndex >= 9) return;
              storeBandParameters();
              for (i = 0; i < 9; i++) { bandParameters[i].active = (i == bandIndex); buttons[i].setValue(i == bandIndex ? 1 : 0); }
              activeBand = bandIndex;
              updateKnobs();
          }
          
          // --- Initial state: pull hub values and sync buttons/knobs -------------------
          for (i = 0; i < 9; i++)
          {
              // resolve & read once (ok if some bands don't exist yet)
              var idF = idxOf(i, KIND_FREQ);
              var idG = idxOf(i, KIND_GAIN);
              var idQ = idxOf(i, KIND_Q);
              var idE = idxOf(i, KIND_ENABLED);
          
              if (idF != -1) bandParameters[i].freq    = fx.getAttribute(idF);
              if (idG != -1) bandParameters[i].gain    = fx.getAttribute(idG);
              if (idQ != -1) bandParameters[i].q       = fx.getAttribute(idQ);
              if (idE != -1) bandParameters[i].Enabled = fx.getAttribute(idE) > 0;
          }
          
          updateButtonAppearance();
          switchToBand(0);
          
          // ========================================================================
          
          // onControl Tab
          
          // ========================================================================
          
          function onControl(component, value)
          {
              // Radio buttons: only allow selecting enabled bands
              for (i = 0; i < 9; i++)
              {
                  if (component == buttons[i])
                  {
                      if (!bandParameters[i].Enabled) { buttons[i].setValue(0); return; }
                      if (value > 0) { switchToBand(i); }
                      return;
                  }
              }
          
              // Knobs write directly to the Parameter Hub (panel follows)
              if (component == freqKnob)      { setP(activeBand, KIND_FREQ, value); bandParameters[activeBand].freq = value; }
              else if (component == gainKnob) { setP(activeBand, KIND_GAIN, value); bandParameters[activeBand].gain = value; }
              else if (component == qKnob)    { setP(activeBand, KIND_Q,    value); bandParameters[activeBand].q    = value; }
          
              // Create = find first disabled band, enable it, set defaults, select it
              if (component == create && value > 0)
              {
                  create.setValue(0);
                  local freeSlot = findFirstInactiveBand();
                  if (freeSlot != -1)
                  {
                      setBandEnabled(freeSlot, true);
                      initBandParams(freeSlot, 1000, 0, 1);
                      switchToBand(freeSlot);
          
                      // Optional: one-time UI nudge during wiring
                      // eqPanel.setContentData(ft_data);
                  }
                  return;
              }
          
              // Delete = disable current band and select next active (if any)
              if (component == delBtn && value > 0)
              {
                  delBtn.setValue(0);
          
                  if (bandParameters[activeBand].Enabled)
                  {
                      setBandEnabled(activeBand, false);
          
                      local nextBand = findNextActiveBand(activeBand);
                      if (nextBand != -1) switchToBand(nextBand);
                      else { buttons[activeBand].setValue(0); bandParameters[activeBand].active = false; }
          
                      // Optional: one-time UI nudge during wiring
                      // eqPanel.setContentData(ft_data);
                  }
                  return;
              }
          }
          1 Reply Last reply Reply Quote 0
          • P
            pandaz21 @ustk
            last edited by

            @ustk For sure! Here's a minimal snippet of the onInit and onControl tabs.

            // Minimal reproducible snippet: DraggableFilterPanel bound to Parameter Hub
            // - Hub exposes attributes: Frequency1..9, Gain1..9, Q1..9, Enabled1..9
            // - Panel uses IdPatterns; no default bands created via custom data
            // - A simple "createBand(0)" shows a dot at 1 kHz / 0 dB / Q=1
            
            Content.makeFrontInterface(640, 360);
            
            // 1) Script FX that owns the Parameter Hub attributes
            const var fx = Synth.getEffect("Script FX1");
            
            // 2) Panel semantics (axes + ranges), WITHOUT creating bands
            fx.setDraggableFilterData({
              ParameterOrder: ["Gain","Freq","Q","Enabled"],
              DragActions:    { "DragX":"Freq", "DragY":"Gain", "ShiftDrag":"Q", "DoubleClick":"", "RightClick":"" },
              Range: {
                "Gain":      { "min": -24.0, "max":  24.0 },
                "Frequency": { "min":   20.0, "max": 20000.0 },
                "Q":         { "min":    0.1, "max": 10.0 }
              }
            });
            
            // 3) DraggableFilterPanel bound by IdPatterns to the hub
            const var panel = Content.addFloatingTile("EQ", 10, 10);
            panel.set("width", 620);
            panel.set("height", 260);
            
            panel.setContentData({
              "Type": "DraggableFilterPanel",
              "ProcessorId": "Script FX1",
              "ItemCount": 9,
              "FrequencyIdPattern": "Frequency$N",
              "GainIdPattern":       "Gain$N",
              "QIdPattern":          "Q$N",
              "EnabledIdPattern":    "Enabled$N",
              "AllowAdd": false,
              "AllowRemove": false
            });
            
            // 4) Safe resolver: <Kind><N> -> attribute index by scanning names
            const var KIND = ["Frequency","Gain","Q","Enabled"]; // 0,1,2,3
            var paramIdx = []; for (i=0;i<9;i++) paramIdx[i] = [-1,-1,-1,-1];
            
            inline function idxOf(band, kind)
            {
                // cached?
                if (paramIdx[band][kind] != -1) return paramIdx[band][kind];
            
                reg n  = band + 1;
                reg id = KIND[kind] + n;           // "Frequency1", "Gain3", ...
                reg ii, N = fx.getNumAttributes();
            
                for (ii = 0; ii < N; ii++)
                    if (fx.getAttributeId(ii) == id) { paramIdx[band][kind] = ii; return ii; }
            
                return -1; // don't cache miss
            }
            
            inline function setP(band, kind, value) { reg a = idxOf(band, kind); if (a != -1) fx.setAttribute(a, value); }
            inline function getP(band, kind)        { reg a = idxOf(band, kind); return (a != -1 ? fx.getAttribute(a) : undefined); }
            
            // 5) Minimal "Create" for one band so the panel shows a point
            inline function createBand(b)
            {
                setP(b, 3, 1);      // EnabledN = 1
                setP(b, 0, 1000);   // FrequencyN
                setP(b, 1, 0);      // GainN
                setP(b, 2, 1);      // QN
            }
            
            // Run once to demonstrate
            createBand(0);
            // ========================================================================
            
            // onControl Tab
            
            // ========================================================================
            const var F = Content.addKnob("Freq", 10, 280);
            const var G = Content.addKnob("Gain", 220, 280);
            const var Q = Content.addKnob("Q",    430, 280);
            F.set("mode","Frequency"); F.set("min",20); F.set("max",20000); F.set("defaultValue",1000);
            G.set("min",-24); G.set("max",24); G.set("defaultValue",0);
            Q.set("min",0.1); Q.set("max",10); Q.set("defaultValue",1);
            
            function onControl(c, v)
            {
                if (c == F) setP(0, 0, v);      // Frequency1
                if (c == G) setP(0, 1, v);      // Gain1
                if (c == Q) setP(0, 2, v);      // Q1
            }
            ustkU 1 Reply Last reply Reply Quote 0
            • ustkU
              ustk @pandaz21
              last edited by

              @pandaz21 in order to create a snippet, you need to go to menu->Export->Export Snippet and paste it here between code backticks

              Hise made me an F5 dude, browser just suffers...

              1 Reply Last reply Reply Quote 1
              • First post
                Last post

              35

              Online

              1.9k

              Users

              12.4k

              Topics

              108.0k

              Posts