HISE Logo Forum
    • Categories
    • Register
    • Login

    How to draw super smooth Waveform?

    Scheduled Pinned Locked Moved General Questions
    13 Posts 6 Posters 813 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.
    • 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

                          32

                          Online

                          1.7k

                          Users

                          11.8k

                          Topics

                          102.7k

                          Posts