HISE Logo Forum
    • Categories
    • Register
    • Login

    FFT Analyser Path - Need help drawing the magnitude to height

    Scheduled Pinned Locked Moved Unsolved Scripting
    2 Posts 1 Posters 53 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.
    • HISEnbergH
      HISEnberg
      last edited by HISEnberg

      I am struggling to draw the magnitude/peak/rms of an analyzer to my path. My example snippet is just grabbing the analyser buffer from my scriptnode network and copying it to fill a path in a panel.

      After some back and forth with Claude my code has become a bit messy but still I haven't solved scaling the magnitude response of the FFT analyser to the vertical axis. You can see in the example that the signal level can take an effect on the alpha. Does anyone have an example snippet on how to do this?

      HiseSnippet 2775.3oc0YstaabbEdojVkHk3hj.Wj9yIF8GqbroHks7En3FIQQYITcgPTUoAtFBC2cH4TsbmE6NTRzFBn+r+q8gouH8MnnOA8Mn8LWVtyPtTRlw1IkvvZ2Yly47cNy41LaiDlOIMkk3TZwiGDSbJ84tMGDw6VqKlF4r6VNkVvscadCLuqylChwooj.mRkl8khoKsvbNxe+mueSbHNxmjOjiyILpOYOZOJOezFq+6oggaiCHGS6Yr5Gu9t9rnZrPVe.Jy5VwIF6eFtC4.rXYy35rCNsqSo661ppeExiZ4iwsvOm7rmsZqG+rm9bLoZP0mt5Sd9pUddqmr5SehuSo4qGP4rjlbLmj5TZtMYACZ1kcQjR.mPSosBIhWp5zDjrZ3sYgABUTLpSstzvfFY1nTGmRtMxsXyprX20ceZ.c334VtuPNAJmBSCXoYrg2rVvq5jfWAPpjAjlSAouzsoeBMlmOi.Oel6tQbRRaLrOYBE0Zcl4e9Ut0XvJh3k6gOircB7xPJ7dbkJO.sRkJKs1hnEifskTXKhf1mFQ6gC2tMeiHb3fTRxhucQD7a4kQ0XIDjOqWLKBXZpbXXaNkiNGmfRg8ZfAu.Ic3J2gv2hlFGhGrY+1sIIMkS6cu1WdOgHsHE7HE9OI.w0i5PiHk8SHfcTN3gs9yDet2XDEGEdJ.SflLsDDYsLz4cO87UxDWlVnQEpkDVxQ0Ksb.liKqFGXqRgFSQ7pnYXATUNkv2vmSOm3wS5SJZgwPnmAlUJpHdzaDbp2roQczP0zfKFA3hBQZl3UDf.zeDAGng9RkCIQc3c0hJgzAEhS4MXTX+D32qd8ZKluaG0l1oeBlSYQJwhs20Odmip2bmC2aKfxpkqrVF3Uy1b2WdvF6c5V0qswOBKnR4pqYO8d0OnV8SMYRkxUprp0p1Z2lM1aie7zM28flvBVs5JVSuS8ce4NGeZyZarW8gfvxLw4.3mjKxlxoqNpGhLFV3iQwPHa5PSUJsCDSrG4bRnBrZrjQmhcHebXXKHcGpMKAQh.d.6gKGPSUOg3cIns29XIUzHXHBpc+HeoYlEowj.vIrPuggaO..TH3RIISERJYPajmbBzK.8eogiK9kEVUF14RTO58nLuWwORXJYRTvhUDnW9U4ZpxnJb00frlVg8FE8iXW2Mhxo3P5aHY1.T5fTNoWglB5vU6MpRm4nCPnAjcleDqOGn0SVYK+cCMseLDTP.I5YLXt1RTOLTQFt7BWcAVyqrTz8APfhEHAknfhzWHSoiwQjvB04QT.uNip4gLv6B0h0OJPDux6RSE9z6IFdS4ndlavpkeArREIuZkWO5jcym7QiMoNYkTLCydkungOzQ4MHJ26o9SZ4Kn7taDF2E6U4xs0+dfYLzRF.sS41PiDxzfBQ7.zqfpSv+t3AntutPa7VDNTVHUyPTnLprcBqmNaIRf2BMxARJalCDuhsx8vc.Wv9AhZZBdJLz6mMlG.N4fVITyINE9Ox1Xenk.QdKHWw3VM67I6C5MTn9Rub4deS1XY6foLyuZH9hXJMxCRMZa7K.OIDd+jHyUUjgeGbT.jUDVFn5PAePAOmjL.z6KzjVnUuqjrlJhzaxhc2Qs8h.EOpH+5ZHJ56LpOoM0vve62ZmnKeMuh9ZfztKuRApmPlkEf5XlmPxKuhgQ3VOYkBmzOjkRZ1ukYg7hrs4wO1F0CXI8j45RybekI0SQbFpMMJPl6.bNn85Wb1xnLFnqzCtL8gTBznzI4be4IXcYLwOn3rnxGviPfWQO7MjDlBCiqKitEkKqB1aDEnjdg3VodBcC1hVB86z.vdsheCA1XDMYqphlhrq0jcFkZj5ERnIxLgXwb3rLugDfhkdNEZUUMVsMMD5ZlDn1bu9LxYkl9fjTt3ryStUxgtmxRVGPtHyCcLm3Q3b9NpYNDylwlT1ugOTPV1BVkv6XrHbz27Bzn9u1Fc6fdciqVMx7t3hdioPtpXfalp86FugVagncUmXZvItWLLf3ZBwW6mPP5HVUkmEKkJC.z69grNdUQeKhtDZY6QJDD4L5RQHQFytO5B60T.ACUxfSTc0Ndd..BlA71zOPs2gdHxaTVceHr59VGYXoqANCLKICwKCiBfMrAKccTZkwa.fDKeKH22KPSvG4si4VZV44RgfW6F8bGXujqrdarN9uQQZC9Iw6qtwxsF7I2m4gUe8ubpAK6ZW04uQE.nfHTin0.YIDtnq+BqTXb.iIbXEySk6yhGXbrbwnlZSA2WPQUhFmhDh7TGdE1wrwwuHhN1ft5i5nUl7iZs1hWYbZzCXbxgQfN81EWXQfciNU61ENm97eghyOVvz5SVNYB8h52qEIY3Yd0Kzozb12x172taYyWUgzXgrHg03vXh98q+ZBczkXE2NmFgvR4x6l6WouaN0IecnAhqXUeLXGI7MuDUm+vtaA6pYLB3IHmXRBmJTmRaQNm5STW52BtaQROCNGNHpg2YAnxJwdmrqDTbVRoTWzM6FubtzTjCLe4BZ.uqSI2R+8RNNcIzNc4h29GNRkbTgbs51DDxeo85CERCGSgb2QDxmXqI6gao0jO0U9rkHNd8hEwSJMpH3jKgmumaSQOO4Qspt9Dupt+huwwY7KZ8Kb2mEzODysu2Wwsiqm.bSstrUwEpFA02FXd64uCWFbkq8xfusP7KcaP49cKFiyT.FAGvODXTeE52wsNjWymmCv4b29ONs2Wdka+8k6pj+hZWJPlJg29RKgeP+dVWoapv2AbjbzWcdo+padRoXQR0DnrSCX8do3dwgji.PCc.CsbbVSHq43YxhUXZSwJ77gl9fnzzoIg27um9rBNpqTpCzXRBEhqbAaf5J4qoQmvFLiHBU8dEw6BGkljn.4K+W3mdxpCMXvjUylLOMoyAD9Erjyj6O5mg.dwlgq36O4rQXH6BQt.p1eE1Uji0fENHtKKh5KFRshLHtQO3HQ7LbtCN8XLMT3f2reJjIO3vHce0BROA1VkL9Sbgi3VFzfYNfE.SMu55TFH+FXPjin3.3lJpJOzcUARKWF4GtRMtfOYeaKkMBvYOYNsYbgM.iT5J2SwaPv2LBV+4t6ldhXVnyBmST0G.8QvSk0RARwRmWHMn1jIduiKV8cYJWLB0z7t.QYN4PxkYElFIzXzQD5QT.4xLFWGbbCfhmRxy7DZfS.3vIpDDlurIicVOrz0XpJFZlt4qUS9Yt+.9bh7yNI2890x2gC.0C8RBrwJxUV88wm6L9V+4NOzmCP33DbTJb7GRUSN2jzidLTCL0ZzQnXkBovZzsf1YirYsZHqUILEaClhpl5a1fFq7tqCsRXwL3cKNsOMqdL5O8292eecwW1fzj.Zdvgo9fgQXmMkRi9voM9AQkZC99uLFdEyg2AmD.6g9VEol859nuSL8mc8Zi0WP4i4+P8Efm61UQ6F577W98nXU.dgLL1jJxoTO5bRHDRKw3WAwvsw8C4YiZGStOKhkk4O2C3HBTupSGhkiUgJzFbN1+LSG5iHgDbpQb5uc88fD83j8k4XmJaw69Guuv8qeiqBtHQhDzOk8sOptYWaelydq5y78Ndm9dN+HGgB4O++mc5u1ExouBRtci94e+9iQ2qeLjQOreB6TeUS+hDFepbDPuijsusf69h2QEb0A8fROm56aypwHbkokvGMsD93okvUmVBexzR3SmVBe1MSnnyfM5yY8TwIP6RMpqZItjpSIYHiy+iaEPMY
      

      fft.gif

      Content.makeFrontInterface(400, 200);
       
      namespace MinimalFftAnalyser
      {
          // Core components
          const var source = Synth.getDisplayBufferSource("fx");
          const var fftTimer = Engine.createTimerObject();
          const var pnl_Fft = Content.getComponent("pnl_Fft0");
          
          // Display buffer
          pnl_Fft.data.buffer = source.getDisplayBuffer(0);
          pnl_Fft.data.buffer.setActive(true);
          pnl_Fft.data.path = Content.createPath();
          
          // Processing buffers
          const buff = Buffer.create(pnl_Fft.data.buffer.getReadBuffer().length);
          reg lastPoints = [];
      
          // Configuration constants
          const THRESHOLD = 1.0;   
          const SIGNAL_DECAY = 0.1; 
          const SILENCE_THRESHOLD = 0.005; 
          const DISPLAY_BINS = 512; 
          const HEIGHT_SCALE = 1.0;  
          const button = Content.getComponent("Button1");
          
          // State variables
          reg signalLevel = 0.0; 
          
          // Button callback for enabling/disabling the FFT
          inline function onButton1Control(component, value)
          {
              if (value == 1)
                  fftTimer.startTimer(30);
              else
                  fftTimer.stopTimer();
          }
          
          button.setControlCallback(onButton1Control);
          
          // Initialize the FFT system
          inline function initialize()
          {
              pnl_Fft.setPaintRoutine(fftPaintRoutine);
              updateFFT();
              fftTimer.setTimerCallback(updateFFT);
              fftTimer.startTimer(30);
          }
          
          // Main paint routine for the FFT panel
          inline function fftPaintRoutine(g)
          {
              local bounds = this.getLocalBounds(0);
              local w = bounds[2];
              local h = bounds[3];
              local path = this.data.path;
              
              g.setColour(Colours.withAlpha(0xFFFFFFFF, signalLevel));
              g.fillPath(path, [0, 0, w, h]);
          }
          
          // Detects signal level from buffer data
          inline function detectSignalLevel()
          {
              local magnitude = buff.getMagnitude(0, buff.length);
              local scaleFactor = 5.0;
              
              signalLevel = Math.max(magnitude * scaleFactor, signalLevel * SIGNAL_DECAY);
              signalLevel = Math.min(1.0, signalLevel);
              
              return signalLevel;
          }
          
          // Handles silence or very low signal
          inline function handleSilence(path, w, h)
          {
              for (i = 0; i < lastPoints.length; i++)
                  lastPoints[i] = h/2;
              
              path.lineTo(w, h/2);
              path.lineTo(w, h/2);
              path.lineTo(0, h/2);
              path.closeSubPath();
              
              return path;
          }
          
          // Normalizes buffer values to find the maximum
          inline function normalizeBuffer(actualBins)
          {
              local maxVal = 0.000001; // Small non-zero value
              
              for (i = 0; i < actualBins; i++)
                  if (Math.abs(buff[i]) > maxVal)
                      maxVal = Math.abs(buff[i]);
              
              return maxVal;
          }
          
          // Creates the FFT path with optimized points
          inline function createFilteredPath()
          {
              local bounds = pnl_Fft.getLocalBounds(0);
              local w = bounds[2];
              local h = bounds[3];
              
              local path = Content.createPath();
              path.startNewSubPath(0, h/2);
              
              local actualBins = Math.min(DISPLAY_BINS, buff.length);
              
              detectSignalLevel();
              
              if (lastPoints.length != actualBins)
              {
                  lastPoints = [];
                  for (i = 0; i < actualBins; i++)
                      lastPoints[i] = h/2;
              }
              
              if (signalLevel < SILENCE_THRESHOLD)
                  return handleSilence(path, w, h);
              
              local maxVal = normalizeBuffer(actualBins);
              
              for (i = 0; i < actualBins; i++)
              {
                  local position = Math.log(1 + i) / Math.log(1 + actualBins);
                  local x = position * w;
                  
                  local normalizedValue = Math.abs(buff[i]) / maxVal;
                  local y = h/2 - (normalizedValue * h * HEIGHT_SCALE);
                  
                  y = Math.max(0, Math.min(h, y));
                  
                  if (Math.abs(y - lastPoints[i]) >= THRESHOLD)
                  {
                      path.lineTo(x, y);
                      lastPoints[i] = y;
                  }
                  else
                  {
                      path.lineTo(x, lastPoints[i]);
                  }
              }
              
              path.lineTo(w, lastPoints[actualBins-1]);
              path.lineTo(w, h/2);
              path.lineTo(0, h/2);
              path.closeSubPath();
              
              return path;
          }
          
          // Main update function called by the timer
          inline function updateFFT()
          {
              pnl_Fft.data.buffer.copyReadBuffer(buff);
              pnl_Fft.data.path = createFilteredPath();
              pnl_Fft.repaint();
          }
          
          // Initialize everything
          initialize();
      }
      
      HISEnbergH 1 Reply Last reply Reply Quote 0
      • HISEnbergH
        HISEnberg @HISEnberg
        last edited by

        Just a bump on this if anyone has any suggestions! 🤞

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

        44

        Online

        1.7k

        Users

        11.7k

        Topics

        101.9k

        Posts