HISE Logo Forum
    • Categories
    • Register
    • Login

    How to draw super smooth Waveform?

    Scheduled Pinned Locked Moved General Questions
    13 Posts 6 Posters 812 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.
    • oskarshO
      oskarsh
      last edited by

      I am experimenting with waveform drawing. I would like to draw a super smooth waveform. I can customize the drawing of a Waveform by drawing the path with LAF. I then use

      Path.roundCorners(10)
      

      however it seems like its not getting any rounder after using a value of 15 which is not very round.

      I was thinking that I could edit the path on the fly and remove some points to get rid of these sharp inclines but I found no way on how to actually edit the path.

      Does anyone have any tips on how to achieve such a look?

      28e76340-4b3e-47bc-b9e7-d2aa9cbb074f-image.png

      d.healeyD 1 Reply Last reply Reply Quote 0
      • d.healeyD
        d.healey @oskarsh
        last edited by

        @oskarsh Posting a snippet might help.

        Libre Wave - Freedom respecting instruments and effects
        My Patreon - HISE tutorials
        YouTube Channel - Public HISE tutorials

        Christoph HartC 1 Reply Last reply Reply Quote 0
        • Christoph HartC
          Christoph Hart @d.healey
          last edited by

          There's a dubious function available as LAF callback that lets you customize the audio rendering options:

          
          const var AudioWaveform1 = Content.getComponent("AudioWaveform1");
          
          const var laf = Content.createLocalLookAndFeel();
          
          laf.registerFunction("getThumbnailRenderOptions", function(obj)
          {
          	 Console.print(trace(obj));
          	 
          	 obj.manualDownSampleFactor = 4.0;
          	 return obj;
          	 
          });
          
          AudioWaveform1.setLocalLookAndFeel(laf);
          

          By changing the manualDownSampleFactor property you can get a low-res signal with less edges which you can then make more rounded using the function you mentioned above.

          mmprodM 1 Reply Last reply Reply Quote 5
          • mmprodM
            mmprod @Christoph Hart
            last edited by

            @Christoph-Hart Can I do this to the oscilloscope per chance? Or get a similar effect

            For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life.
            John 3:16

            oskarshO M HISEnbergH 3 Replies Last reply Reply Quote 0
            • oskarshO
              oskarsh @mmprod
              last edited by

              @mmprod Yes you will need to use a scriptnode oscilloscope buffer. Instead of downsampling inside the LAF you will need to downsample the audio signal in the scriptnode network. You can then smooth the path the same.

              mmprodM 1 Reply Last reply Reply Quote 1
              • M
                Mighty23 @mmprod
                last edited by

                @mmprod
                https://forum.hise.audio/topic/11419/help-needed-detecting-audio-signal-in-display-buffer-for-oscilloscope-animation

                If this is something you've never done before, this post could be a great starting point. It’s not exactly what you’re looking for, but it’s a good foundation for customizing your own oscilloscope using the one in Scriptnode.

                Free Party, Free Tekno & Free Software too

                1 Reply Last reply Reply Quote 1
                • HISEnbergH
                  HISEnberg @mmprod
                  last edited by

                  @mmprod I am still not done this but I am working on an oscilloscope I plan to share soon with interpolation to smooth the waveform:

                  HiseSnippet 3945.3oc6Zz1Tabb9D3KoVIoMoMMoebKSSFgMHj.AFhKMHCRFl.HUIY6zZ6g43tURq4zcWu6DfZGOS+X+V+KzeJ4mP+MjeA4a8isOO6d6c6JIvDhMYbmnwCV6tOO6979K6plg91znH+Pib46LJfZj68MaOxKt+18sXdF6siQtOxrQjMy00Ox1OftZwJFOXTfUTD0wHWtYeHBVtaeKC9mu6KefkqkmMMaJCiG6yro6yFvhyls4VeErk0sbncXCTftxV6Y66ssuq+Pfjl0rjQfk8IV8nGZgfMiowtVQ8MxcGy0r2X0RGSKs5pqWYk0qTYsJqsZotkVo6wquVkiW4dUVwoxpksWwH26TygE6G1N1JlFYj6VOv2YT699m4INfGyhXG6RwAkMZCmrX559tNHKhyZrcelqSSorJxvH261LSxMqPx8wlGvbXoymIA+P9BjLLTEf4lQm7lUi7JqRdkTHuoPR4THoaIHoOxrscHKHNaEjddOy87hogcs.8jJoHf0Xl+ymXtsO.gWbwAVmPqGBCRwnPkRkVf.+Y96mO+R2gr4qkO4m1jjFs2du82G9ail0H6rW6l6W8OQdvipWuVKx2qc5574NKA72RjGaExr.EPTdvvLJlbpUHIvy8HvonLYShTP0iFus+f.eOXPg4j.LGHjVZoLDqeNhC2ACwnV2tTa.bXZDRU3lNXywE5KQdvPXpPzhJfFFyzHNo7IC+cXQAtViDH0F7r.sXxlog1QfjFoOwNLAlEJMAB6W6vG1YW.kxkVtx8I.k0h5Lzl5PNVPgtTudw8Ic8CIQwVGybYwiFaOZ1Bzts5rWs1v972xSfOyINv84HO2WnbTKHV+vgC.2KOOpaDrb47uDDKRFnXDMtEyqmXOxDQEzOLfWTwnpcL6TZg3vgz44LBeBvkjD2mlvM4Un7N0Nn4QohZwYUzNjBXTHibEaUGJXXDZENRJUPwQfvkDHz74Co837X68d3gU2G1utVtQTNt64whYVtr+JkXEIlOOd9Q1vjd8paYCwNPEPwRb32Yjm0.lsbc.CD.EBu81U2u1Q6TaavUZSRoharNGu1C78i6mgAmFcz2LtoGOPPfuqULy2iTe7seuC6TqUyF6WsydMN7n5U2tSCT.spf5.81w.+62kD3y7hiHw9DV5FBxYZ7YTpGIxZP.5xwc.YQCsbmts9AU+5id3tMZ2AscVcrinWee.r3PKlqJNa2X+FOp0QO3gbuWLQSTwicgrL2eRfZU8IJPERcD13VmQNy5TJHhFPrgECmDy1GznA2yPhbuPfwTD0fOxqbOp14Qsp1o1NJayHJjI9L99fSIzSQVwCCA4mCIfZchJy1tSqFeUsidxd6zYWz0d4DyjIoAP4aehGXQdAXurhQ1CyjrSEuF0q2tVmi3lZ7fCI38XTAB1Rf1oK3ywsqnbN3BzUBoHP.GtSim.tG+Y91s5XVrmw7b7OiDgNI3lY4EyVDrXs3dWJ6lPdhFlc1sUs161X+c39.BWfN8CoQ8gz9pxTvHmaEtM20lzzJtuJABEEgSojHPDC.mrfV3xPqytLHQBfuNd1gJFXJaAWHwo.XWd5ygXdHvEXHObeBi76U7Gfw28tySxKBnlgYwfgQ8KLMR.n1WlmGaoaHTr019C8h46Lm+qhxzpIxTRclK3zRpOzylKg3QjjlTBZi4AgL.8QBHIKmjIQDGbgDMWaPwkRpiyRBPKJRjH4JDPA33GWezlJZ3.A4pOscFeHWfeDu.lawC.NuXWWe+vBYjBYIxxfB4Eje+ljKEfTRQmbvOrtjB.WbWxKlm7GfCm74eNQNdLdRYOlbevOHic2MSP5o7M442eBn374cuq9BuL+jeSpmdJ64fH.27kDHKPU.XHEr98RgkaZ.VA6CpTLHudN.oUv3pbkH6sEAzSU7hjLoJcgpRAdoUzqzhfrHo7zsJT2MgUuTB974mhsPYtFOgvtbkqfZQipW.xtDLzU+W7YStCoPYfpimGTjJ5zx3Jwh.AIhYlpXdLMo3uW34LgLRxyIJVUDkJ2ES+.Qh..Hs7GFiJzrExKKrEKYhCTBLEjJ8B8xB5TrKziWUW2BoobwxWktG+1rJdlP2InRQAPcId9Pr8ddVtKPrN0m4PFBU9gEOgkT4.AKwv7BgB++vnQmwb3AagLDQXkrOAGWHQHf.zmx50OVAhc4SnBx.lCViTBjfSeB0iIe5y5Fmj0JfmSPyRUotDg8I4OvsdWbwIX0rPyBGR0wbE28UYsj7MEscACDIsljZIaRIUtsePZMmNVwVPlQmDeZBKlCUZMvPy8iZQsbRhPqTeqhDQFOHo+iM0ipqfyBSI0sx9nZ9ktWSIbg94svTqtTkesbsGxKjzY5kAKOerLo8omRcgiUgpQyflvRsr75QK.s4pTI+SkZhwq7lmhXf04EJUr7BIiXdEzA6Np0cu.oLXMkRCyqvA6fo9wNNzJAHU7yEK5z7SK8bcq.nOqv3Com0d3w7D6.avMjWTh9cDiuiNmHIhTa3xhnsYhfoGlEoKraU.mkTkWv9y8AyBLNUpmojJC2pQv5WExMCKIei4d53W37EHilWymQUrpkhQS9lRcSZalJhkdeulEwSdhWZ4NZR7KD2KSALEV7GtdPJbtP8fRjsRXjtDDFy3WsS.fAfHUmp20vqLR6lWTnVdA6AV1r3Q7x9WUlKFEjYaz75RB4w2Pzyxl5c2bGjLVTkL3EGlEdGyTJZdqfrGNjqp5Fz2pfVyhKHIt40v2Iw.ufVlhEHOEr5zosEDJ7ERxXAv72lqir8r49B814d4E6jLQrGMlHsy3jMPgBS9eAsU5UQNfB3kSKzWZiDWry5THHgLbRZJwJ6JRSZ8KiT2KStwMkhivarNDy03drk8IpUGo0Wn.rMI075AdDIsZwmrwwu.uOOXmkfgrC+KxcMqdpzxovZlx5L6t2k7YPZD7VRKMggt1cIU+b9kSNLLD53imvQbKWXIIEKUpLuJqGE3foMgyPToEwheeXo6XZcegz.rrOri0DyGoDpMFTjq.iQNQg2vE3esvJ3UHl1efu2g9wzF.OBr3sgpPIiuT2tScMr80PeWWXGm1xhy5RPrfG+Rh.+GKWPRHAzH2szuKdyK9t3Uep.aQ2zJ.56g2aWi.p2E8.BFIsfCe6Q6sCTaFdA9IykcUWvb6POkYSEWm+sM2gFcRrefQtYRusYibuSLe0OPdY+VdTWCliQt7lR8lw4puHyH0AbGhrIFskv0HcluIbKCkC68iSdrCwgU20GZOwqWGFHWvy7WXpN0Edv+q3sdUG7e+OtUh.Qv84M+J5ni8sBcLNtm7IhdeyJKudk6s1FqrdICVLcfbg2yrxpKuw5UVekxJyur.iMprwZ2a4MV0Hlddr5qMw0Dy77blnyzbv4w6a.ui4JEKgW97bIWIdC6XHfj3xFgk4WLKe888OCPClZC9vcYhQkW9d7waOLJ1evCCsBfnNQZXtCsq0P23pAPkgg3inAqhdp7Ee.dCkvV0BaFjG7bsM3eVe8R2aiR2qLGpN9854ROv2gpsy3yOkbe4Hovm6QQzGSwL3JzR5oAKVGB61Ndj6X6TyZRkv3yy8+yNkkkyWyyQ4rWK+KMLl7gp9PSfnwR20e2L70ESV.bf0drJ7NH7hfrkpu93qsGS6pRhejYSVrc+oSiyLEZDbyeSPiIOA4GXJdqnLB7Vl0+52Pu23rJm+6JN+2yrs3ZSSdow2OabY8mZL81UUEMOgFWc.lcSUm1Ljp+Hy.iCEBM9b4l8JyWk+d+NpSU2+olYbA4GGqTwsuzCZ5LjAAZMg3Qh22S95Xvb4lAy1HFWBGiDQapmCev+E9jrXYbbtjEKKWT0JyTFHNI1+WKMwNWioApP6gCiR2XwCLm6eXlkTN.qnHD5XnI.eAQuFPPN5Bjic8sOge2vSjIO4wyd.BQA6Dd85jv+cdMYzbinIxJY3PZ7Y9gmvsVS9N3CJ731IJPNUU7shvb2rDSSASAZD9f20DJ.rXIAXM8cGEz22iYipPARRhW5TJ3fcsh5.cmgA3ZOLBpwwogWa.X9O6CH.StCg7OfnUzf3Hr5aLPIVkD35fOSZpKjF0lYBgxXwO3CM.v8UlrVH7.xb.upjYLArv0EhDAAHNAenIIgYkgFE8IJTjBLSmJT.35PE+bSekeCMiSGVPA2ihnE0fYZzwXayqhRPknK8bY0ki4SN9LFX9JG54hCrF3u33Pc3HiamQSKr4iXpL0irDU8UfnmxQ.Qc.y6wnGG7UzVy3.qySGCETYzNlFfd333xvXgrpgTXCNTFoviqqct7.03nQHoy006EIehQIdfE6ER3W0M35Tf9TJf3WatKT1jMnzbHGXEwe.MQDTSHBpV9wYRcp0b.dski6lNwwmJKO3IPQy7eWJb99WyGye94GRA+vIqSPOK++9p9yKK3J+yKSTGeGnd6n.+HneEkctMc.qCz1Sj1rigwxSECsY2gFOzSeqESIgZqUVY1uAEE0AQgJbaImTY+93sfF7z1LXr14c.S1zE4Y+yu8Kq4g+9kZSAN2Q7S4iWelZIVCg534c5Td5Sur5znULnCsurput0Uyx7U7ir6cdS8ir6VWsJttbx6sf1Xzpk81RZrMCyKTy6TpKlFAoweoYR2mxY0cCOv2yWVbPlRuEEhdzqGUyVZpLT03Xn8UUa3VTWpUjhq4uaKwqdd.Oq40RV7Zpt9eiofb4Wz2OnB6uQMytzVQm8J0J5qc5852V5MrGJDx7sGM8mh+hrWlvU2je7022v0R7qj1TUG5v722GKEiKV9XwLDbJB1MIUuXhYdiWLAlUb73WbsDTsf8IX0rJGERlhTyNpv2x2OFu6a0eq7hmCmeAav1qGBFqUlpMWcnWLQ+BiL29Kd1ih.Hd1.1Iz+5yRpN8YbAkXWe1ZKW5dqs9QGY0e.CKT9EGczfgQL6EiwRbXnkUOX5Ei.6jEEq.UrsH9D4gKdVeZHcwyv+4Eu3Yg.rEGDrhw.TRKzxCrPihbdeVICWfk4bgbMWtLvQt9alpHt0OUEwOVUQr7OUEwOUEwa8UQbCmf6mmdW5fobVyxeBeLA6H7B5Vdla5tkmZqupShuiTH8uvezJ04212JbpK8ceYcfImJNYW1t1aD7uB17a+x2xRb7F+YodqLYwJ+Txhe3IK9w8EOm8+qewyajDA2Dmw.K6P+irEuMFZm9y3y.7sGuugaad.NlT13TwkfmoKG.Q4Nx1Veql.wkutHtx0EwJWWDW85h3ZWWDu20Ew0e0HhIgpNL1efv0zv3fl0DOgStzdMyMqw+CFi.0X
                  

                  This was vey much inspired by @Mighty23 's approach but with some distinct differences. To be honest I was struggling with it and used chatGPT to figure out the anti-aliasing filter and linear interpolation functions, i.e. it isn't completed yet. Use a noise signal and compare the green with the red output to see the differences.

                  Perhaps you could employ @oskarsh suggestion with downsampling the signal for further smoothing. Also if anyone can figure out how to compress the signal on the vertical axis based on the amplitude would be a true hero.

                  Content.makeFrontInterface(400, 400);
                  
                  /* ======================================================================
                  ==================== OSCILLOSCOPE DISPLAY BUFFER ========================
                  ========================================================================*/
                  
                  // Variables
                  const var pnl_Osc1 = Content.getComponent("pnl_Osc1");
                  //const var Fx1 = Synth.getEffect("Fx1");
                  const var Fx = Synth.getEffect("Fx"); // Your ScriptFx with an fft analyzer to reference.
                  
                  // Buffer Properties
                  const var BUFFER = Synth.getDisplayBufferSource("Fx");
                  const var BUF_OSC1 = BUFFER.getDisplayBuffer(0);
                  const var BUF_LENGTH = 1024; // Reduced buffer length for stability
                  const var BUF_PROPERTIES = {
                      "BufferLength": BUF_LENGTH,
                      "NumChannels": 1
                  };
                  
                  BUF_OSC1.setRingBufferProperties(BUF_PROPERTIES);
                  BUF_OSC1.setActive(true); // Activate the buffer
                  
                  const var TEMP_BUFFER = Buffer.create(BUF_LENGTH); // Temporary buffer for processing
                  
                  reg BUF_SIGNAL = false; // Initialize as false
                  var scalingFactor = 1.0; // Dynamic scaling factor
                  const var SCALE_DECAY = 0.98; // Smoothing factor for dynamic scaling
                  
                  // Interpolation Factor
                  const var INTERPOLATION_FACTOR = 50; // Number of points to interpolate between samples
                  
                  // Visual Properties
                  const var MAX_GHOSTS = 5; // Number of ghost trails
                  const var COLOUR_BG = Colours.black;
                  const var COLOUR_RAW = Colours.red; // Raw waveform color
                  const var COLOUR_SMOOTH = Colours.green; // Smoothed waveform color
                  const var COLOUR_SATURATED = Colours.yellow; // Color for saturated peaks
                  const var STROKE_WIDTH1 = 2.0; // Smoothed waveform thickness
                  const var STROKE_WIDTH2 = 1.0; // Ghost trail thickness
                  const var OFFSET_SCALE = 10.0; // Vertical offset scale for ghost trails
                  const var SMOOTH_WINDOW_SIZE = 15; // Smoothing window size for anti-aliasing
                  const var SATURATION_THRESHOLD = 0.8; // Threshold for saturation
                  
                  // Create Paths
                  const var oscPath = Content.createPath();
                  const var rawPath = Content.createPath(); // Path for raw waveform
                  const var ghostPaths = [];
                  
                  for (i = 0; i < MAX_GHOSTS; i++) 
                  {
                      ghostPaths.push(Content.createPath());
                  }
                  
                  var frameCount = 0;
                  
                  // Anti-Aliasing Filter Function
                  var smoothed = [];
                  inline function smoothBuffer(buffer, windowSize) 
                  {
                      for (i = 0; i < buffer.length; i++) 
                      {
                          local sum = 0;
                          local count = 0;
                          for (j = -Math.floor(windowSize / 2); j <= Math.floor(windowSize / 2); j++) 
                          {
                              if ((i + j) >= 0 && (i + j) < buffer.length) 
                              {
                                  sum += buffer[i + j];
                                  count++;
                              }
                          }
                          smoothed[i] = sum / count;
                      }
                      return smoothed;
                  }
                  
                  // Linear Interpolation Function
                  inline function interpolateSamples(buffer, factor) 
                  {
                      local interpolated = [];
                      for (i = 0; i < buffer.length - 1; i++) 
                      {
                          interpolated.push(buffer[i]);
                          for (j = 1; j < factor; j++) 
                          {
                              local t = j / factor;
                              interpolated.push(buffer[i] * (1 - t) + buffer[i + 1] * t); // Linear interpolation
                          }
                      }
                      interpolated.push(buffer[buffer.length - 1]);
                      return interpolated;
                  }
                  
                  // ---------- Paint Routine ----------
                  pnl_Osc1.setPaintRoutine(function(g) 
                  {
                      g.fillAll(COLOUR_BG);
                  
                      if (!BUF_SIGNAL) 
                      {
                          return; // If no signal, avoid unnecessary drawing
                      }
                  
                      var width = this.getWidth();
                      var height = this.getHeight();
                      var midY = height / 2;
                  
                      // Shift ghost paths
                      for (i = MAX_GHOSTS - 1; i > 0; i--) 
                      {
                          ghostPaths[i] = ghostPaths[i - 1];
                      }
                  
                      oscPath.clear();
                      rawPath.clear();
                  
                      // Copy buffer data and smooth it
                      BUF_OSC1.copyReadBuffer(TEMP_BUFFER);
                      var smoothedBuffer = smoothBuffer(TEMP_BUFFER, SMOOTH_WINDOW_SIZE);
                      var interpolatedBuffer = interpolateSamples(smoothedBuffer, INTERPOLATION_FACTOR);
                  
                      // Calculate dynamic scaling factor
                      var peakLevel = TEMP_BUFFER.getPeakRange(0, BUF_LENGTH)[1];
                      scalingFactor = Math.max(0.1, Math.min(scalingFactor * SCALE_DECAY, 1 / peakLevel));
                  
                      // Draw the raw waveform
                      var sample = TEMP_BUFFER[0];
                      rawPath.startNewSubPath(0, midY - sample * midY * scalingFactor);
                  
                      for (i = 1; i < BUF_LENGTH; i++) 
                      {
                          var x = (i / BUF_LENGTH) * width;
                          sample = TEMP_BUFFER[i];
                          var y = midY - sample * midY * scalingFactor;
                          rawPath.lineTo(x, y);
                      }
                  
                      // Draw the interpolated waveform
                      sample = interpolatedBuffer[0];
                      oscPath.startNewSubPath(0, midY - sample * midY * scalingFactor);
                  
                      for (i = 1; i < interpolatedBuffer.length; i++) 
                      {
                          var x = (i / interpolatedBuffer.length) * width;
                          sample = interpolatedBuffer[i];
                          var y = midY - sample * midY * scalingFactor;
                          oscPath.lineTo(x, y);
                      }
                  
                      ghostPaths[0] = oscPath;
                  
                      // Draw ghost trails with vertical offset
                      for (i = MAX_GHOSTS - 1; i >= 0; i--) 
                      {
                          var opacity = 0.5 * (1 - i / MAX_GHOSTS);
                          var verticalOffset = OFFSET_SCALE * (i - MAX_GHOSTS / 2);
                          g.setColour(Colours.withAlpha(COLOUR_SMOOTH, opacity));
                          g.drawPath(ghostPaths[i], [0, verticalOffset, width, height], {"Thickness": STROKE_WIDTH2});
                      }
                  
                      // Draw the raw waveform
                      g.setColour(COLOUR_RAW);
                      g.drawPath(rawPath, [0, 0, width, height], {"Thickness": 0.5});
                  
                      // Draw the smoothed interpolated waveform
                      g.setColour(COLOUR_SMOOTH);
                      g.drawPath(oscPath, [0, 0, width, height], {"Thickness": STROKE_WIDTH1});
                  });
                  
                  // ---------- Timer Callback ----------
                  const var oscTimer = Engine.createTimerObject();
                  oscTimer.setTimerCallback(function() 
                  {
                      if (frameCount++ % 1 === 0) 
                      {
                          BUF_SIGNAL = Fx.getCurrentLevel(true) > 0.001; // Update if signal active
                          pnl_Osc1.repaint(); 
                      }
                  });
                  
                  // Start the timer
                  oscTimer.startTimer(30);
                  
                  
                  mmprodM 1 Reply Last reply Reply Quote 3
                  • mmprodM
                    mmprod @HISEnberg
                    last edited by

                    @HISEnberg @oskarsh @Mighty23 thanks guys- I’ll look into these and get back to you

                    For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life.
                    John 3:16

                    1 Reply Last reply Reply Quote 0
                    • mmprodM
                      mmprod @oskarsh
                      last edited by

                      Ok - I put a "sample and hold" node before the oscilloscope (is that what you meant by downsampling? @oskarsh ) and drew the oscilloscope path like @Mighty23 and @HISEnberg recommended. I got some decent results but unfortunately now HISE crashes after 5-10 seconds of opening! My hunch is that the timer interval is too fast or that the buffer size is too high. When I delete the code and the panel that is being painted, HISE doesn't crash.

                      //code thanks to mighty23
                      const var dp_analyser = Synth.getDisplayBufferSource("Script FX2");
                      const var rb_analyser = dp_analyser.getDisplayBuffer(0);
                      const var BUFFER_LENGTH = 512;
                      const var properties = {
                          "BufferLength": BUFFER_LENGTH,
                          "NumChannels": 1
                      };
                      rb_analyser.setRingBufferProperties(properties);
                      const var P1 = Content.getComponent("P1");
                      const var NUM_WAVEFORMS = 6;
                      const var waveformBuffers = [];
                      var time = 0;
                      
                      for (var i = 0; i < NUM_WAVEFORMS; i++) {
                          waveformBuffers[i] = rb_analyser.createPath(P1.getLocalBounds(0), [0, BUFFER_LENGTH, -8.0, 8.0], 0.0);
                      }
                      
                      var bufferIndex = 0;
                      var offset = 0;
                      
                      P1.setTimerCallback(function() {
                          var newPath = rb_analyser.createPath(
                              this.getLocalBounds(0), 
                              [offset, BUFFER_LENGTH + offset, -12.0, 12.0],
                              time * (bufferIndex + 1) * 0.2
                          );
                          
                          waveformBuffers[bufferIndex] = newPath;
                          bufferIndex = (bufferIndex + 1) % NUM_WAVEFORMS;
                          offset = (offset + 256) % BUFFER_LENGTH;
                          time += 0.1;
                          
                          this.repaint();
                      });
                      
                      P1.setPaintRoutine(function(g) {
                          var a = this.getLocalBounds(0);
                      
                              g.setColour(0xFFB8C043);
                              g.drawPath(waveformBuffers[i], a, 1);
                      });
                      
                      P1.startTimer(1);
                      

                      Any ideas?

                      Otherwise I may stick with a non-smoothed oscilloscope for this project - this seems like a very finicky process.

                      For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life.
                      John 3:16

                      HISEnbergH oskarshO M 3 Replies Last reply Reply Quote 0
                      • HISEnbergH
                        HISEnberg @mmprod
                        last edited by

                        @mmprod i was getting instant crashes too, I suspect the buffer doesn't clear so it just fills up and crashes. Did you try my version? I basically bypass the buffer altogether and just refer to the fft and draw it to the panel.

                        1 Reply Last reply Reply Quote 0
                        • oskarshO
                          oskarsh @mmprod
                          last edited by

                          @mmprod yes the sample and hold node is correct. Go a little easy on the timer. 10 or 20 should be enough.

                          The problem with the oscilloscope is that you draw many points that are not necessary basically overlapping points. To get better performance you need to simplify the path and drop points that are the same. There are many discussions on the juce forum about this

                          1 Reply Last reply Reply Quote 0
                          • M
                            Mighty23 @mmprod
                            last edited by

                            @mmprod said in How to draw super smooth Waveform?:

                            Any ideas?

                            I introduced a simplifyBuffer function that removes redundant points from the buffer based on a SIMPLIFICATION_TOLERANCE value.

                            const var dp_analyser = Synth.getDisplayBufferSource("HardcodedMasterFX1"); // or what you neeed
                            const var rb_analyser = dp_analyser.getDisplayBuffer(0);
                            const var BUFFER_LENGTH = 512;
                            const var properties = {
                                "BufferLength": BUFFER_LENGTH,
                                "NumChannels": 1
                            };
                            rb_analyser.setRingBufferProperties(properties);
                            const var P1 = Content.getComponent("P1");
                            const var NUM_WAVEFORMS = 6;
                            const var waveformBuffers = [];
                            const var SAMPLE_REDUCTION = 4; // Only process every 4th sample
                            var time = 0;
                            
                            for (var i = 0; i < NUM_WAVEFORMS; i++) {
                                waveformBuffers[i] = rb_analyser.createPath(
                                    P1.getLocalBounds(0), 
                                    [0, Math.floor(BUFFER_LENGTH/SAMPLE_REDUCTION), -8.0, 8.0], 
                                    0.0
                                );
                            }
                            
                            var bufferIndex = 0;
                            var offset = 0;
                            
                            P1.setTimerCallback(function() {
                                var newPath = rb_analyser.createPath(
                                    this.getLocalBounds(0), 
                                    [Math.floor(offset/SAMPLE_REDUCTION), 
                                     Math.floor((BUFFER_LENGTH + offset)/SAMPLE_REDUCTION), 
                                     -12.0, 12.0],
                                    time * (bufferIndex + 1) * 0.2
                                );
                                
                                waveformBuffers[bufferIndex] = newPath;
                                bufferIndex = (bufferIndex + 1) % NUM_WAVEFORMS;
                                offset = (offset + 256) % BUFFER_LENGTH;
                                time += 0.1;
                                
                                this.repaint();
                            });
                            
                            P1.setPaintRoutine(function(g) {
                                var a = this.getLocalBounds(0);
                                g.setColour(0xFFB8C043);
                                for (var i = 0; i < waveformBuffers.length; i++) {
                                    g.drawPath(waveformBuffers[i], a, 1);
                                }
                            });
                            
                            P1.startTimer(20);
                            

                            Fix this value according your "downsampling":

                            const var SAMPLE_REDUCTION = 4; // !!!!!!!! Only process every 4th sample
                            

                            We need to optimize the scope design; this version could be a starting point. You can adjust the SIMPLIFICATION_TOLERANCE value to control how much the buffer data is simplified, "skipping" redundant points.

                            That said, I realize this discussion might be going off-topic and potentially not fair to other users. Let me know if it would be better to start a dedicated thread for this topic.

                            Free Party, Free Tekno & Free Software too

                            1 Reply Last reply Reply Quote 0
                            • mmprodM
                              mmprod
                              last edited by

                              Thanks guys for the help- the closest I got was simply using the original code with the higher timer value like how @oskarsh suggested. But of course the animation is more choppy.

                              @HISEnberg and @Mighty23 I tried both your methods- these work well but I can’t wrap my head around how to ‘restyle’ it to a ‘smooth’ waveform. I’m also getting the occasional HISE crash which may be a problem for future plug-in users.

                              Im going to use the vanilla oscilloscope for my project. Once again, thanks for your generous help!

                              For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life.
                              John 3:16

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

                              16

                              Online

                              1.7k

                              Users

                              11.8k

                              Topics

                              102.7k

                              Posts