HISE Logo Forum
    • Categories
    • Register
    • Login

    Oscilloscope Script

    Scheduled Pinned Locked Moved Scripting
    3 Posts 3 Posters 62 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

      I am trying to optimize my oscilloscope script while still keeping it visually interesting. I recall having a discussion a few months ago about this but I can't find the original post.

      Anyways here is an oscilloscope script for drawing the analyzer to a path in a panel. There are a lot of adjustable variables for optimizing it (buffer size, frame rate, frame count, etc.) There is also optional opacity settings (glow effect) and ghost trails (though these consume a lot more CPU). Hope you guys like it and let me know if you can think of any optimizations or better visual performance for it!

      Here is an A/B comparison of it with the floating tile display

      osc.gif

      HiseSnippet 3084.3oc2as0baabEFTRH0Vw4Vmz59HFMsy.YISSRcupNQzTjRbh3kRRY0LdznYIvJx0BDfE.zxJdzL8WRm9SnO1eF8w9X+Ij+AsmcWbYW.PKZEeIN7AEuWNm8rmy24xtKRaWGCrmmiqRtE6c0XrRt6o18Ja+gUFhH1J02WI2mp53Y7PSb+ICTdxUiQddXSkb4l+.5Dxc2ETX+9wu8IHKjsANtKEkm5PLvGQFQ7i6s8deGwxpFxD2iLRX1quWcCG6JNVNS.gYd0BJiQFWfFfahnSaNUkCQdCUx8.09EMJfWquAB0GsCd6s2n+5au0NHbQyhaswl6rQgc5u4FasogRtOopIw2wsqOxG6ojagm3XdU2gNWZyWfmR7H8svzFEU5BqLu6ZNVlzsHsWkJCIVlsC0RdJ.SaGqylmqy9Z0FDSRT+w5tujMfVLEhJvbyIKdyKIdEEEuBBhWFhTNAQZAtH8UpcMbIi8iGgaKqa6icOGA1IQQgOWk4962WshCLCa+7iPWfq4BMhnPe8BEVUC9yx6tnMXT7.CDVqkmAXOAHhyX7huZQM3GXG870dAxUqrMx5JOraQsGqwfU4Gf8qd94XCe8khFbIfgxz8jiqUqZGQh1m3M1Bc0Sl.D61EvHf7bCb3rVcq.rfyqT7PuPJZFaacFrc.ZBUB.QUbFM1wFZnuTv3YrZ9.T1Enqp8.hMNugKFLoT7saq9OmtYSKeS78crKN00JX7vkRfZ5N6npMOn2g.wqWXmM2US6QORqC1bhA1TqOa2o4Q9Ar14NtZ8w9f8SqR6iiYjKd.iMs6zpc0N8pWsKvpWsDWwbD1df+vk9iBKzpZK0bxH.Vaais7fgJdMWrBzx48v9cH1C3L.fbiwt9Drmt7hrbJhJa3SdAV22cBN8FsW0FsOKBIv4cfpUOV1VdWIh1uS4SlIhRtZ05TtQ0ypz53l8.BWa2HE04t.VmyHOXjmcZ7PFSbcAqUM5LpaaheILdAg8AXVpfLFBVkwH+gZNLrfWD4ifvDsoCDCB3RJsyPHCch9tHh0MNS1enlbcBSNzHZ+IwsEzwJqrLaVb+T17E1c4GOwan9T0YAKy0x3nQnA1D+IlX5ZleCQbdixGzrdui2u5YcazpECuVH+NRyndyyhlEa3BaHq+1GaPFg7IN1hF4pUp2nbupmUqbkdsnF50JsaJTP0mVsy2eVyyXp.JBuXBiZEmIzXaILZAdYkq7cGzATa6CJuiZcLcQJ7xBA+D2CmT9oUq0pSCw4UtPsZkqNk408vx625D171Dl21Sie85Tt9QroslD6BUMG4bIH7u.YMA6I5pCNePqQzjwBr8fiZApDvunBEeS83jLDs5UuUS9R1MiwqzpSypchsiqkWRlaWt2gm06v5U9tlU6RouT9DBaiHXxjwl.vh0Ow1BBWpc9DaCpENXnnopmDrZ4XfrzrwWBSAVDAecZzSA51MhDQ3Y7+9AowlqDx2GnoWLeAsGlZJSgqM.WPHa4K0k.yqFOmz9Mf9fEyPyCR+Ck3.Klis0Ufxb7UZlbDOMngCwNHbQRMEkPbEgvOozUICDD6HSauxiS5DsbDkICK7rTg4N8YjSk0+PGwZmn+QVwG0S24JZEWV6OHEpJCM19tnKgbLQ5fL0KlvjDqHQevpPrWHqEydjTGMH+4vLKaYomxce4L1Nb32kDSVfXFao3tSncHh43yaHlLXnu3DOj0S5YNhX98v7BH3QZkl5Z6A+AadXHmCn3AAXPhME4x2oIWCphgtHr05gRL5QkRN49PfQSVxNfaLJWkusWUhvrL4AFJdJKNLxSibtF1FA0xZFMOnKcofNw3uWIiDo3XFepaxyupE0JQ5sv9CyxkMCCW8Xd93z.0kSQRPvNehcXjQweo5fqHo91YB4eX7lXEwMQl9Ao4qCT5Mw+JVFys.loSWnGIynG.is4LHoQEWj2vBibEgmI.enQisjh38PFbSF7IE4HbSd5yJb5xYv3301yG452DeY2I8Y0zPgwAPU1xt7LrShB4kHz1sI.X13lXkAKTFAz4BEHApblWxtYR2OI0GIS0mrJjFIrmi9KuYM20uInBWZvfJNt1fDoKUGvrXSFPKymepdc9+wK+kD+gksFODoKWqypQ3ZJ1cir1wCxSCGwPHQR3pAwqVENBSugDiKrgC7BGRIQQILddcBdFqItd5wxn0oypfWH+Oux8ztLbzgnCOXwugroSOb+LAYDYXB+rH4bFbuV7ssajr6yapaysXum1IIZ+ey9FYX+indlbADgMGX4boF+dNjx3ITD9zzSynCC+PDqlQl+j5.AWlvMzL6w.kwe8qWGIJtINIj.g2ZY3QktNc4yA2CCckaCbyuiyDHsLVOrJP8AKq8pXQLqZB8GR7jJQmO8qWV9TKrqvQqVX0kAmMlUmNTJ5xYVBpiMipJHKq9HiKRUXd30ePYRGLxL35nDtsir.U+zJmWtRcfKBqV1ktm5nXwyIiydjsWP2KHiCKAz2QiLZrqyKvoNeZz4MBNO9Jqr6hoJTK7r5eyiy5v8SyYJDo3hGSAJIqsI6aAHFscs.ZfcEeTHmr0Mg0VD+jFXDbodzqvw0wR2H759VkeJ9jHE5lmM.sD0hu1Bjuwa7Y50zvn+4b5edBj0ymo5jRcfQxoO64mxuNmoktMUwBA5WZdJlFUesBYF4Aa4gyjPmwb5BiWHXIBz67PULcuf8S1n.TesfEqoiOtkM3C+pEu6hfPjbnyOOywBXlEUbxX3.Ac5DpaOYTeraDtHXhJ4VP9ICTm9SFH9hFF76LTXhN10AG6Viw1S6cNTBtnQ5yKDHUvT8YOtvmG73BbUmBwTI2cUCziJLQV7ggTNt99HeTHi.dFeCwPe6ieAw.ye0h6ptO16BvTBKUzkgqj6S3K6mE9lFzCTyV0EUCuSdkWJ9vSWI1fUbQbGWsG+Xyw8P1SQXwtmeva5vWrZVNHH6xfdDPuRWyuPUrqouvj8lgENPgv28egZ4IlDmvW0Po+fvmC6dpqWZ6har9FaTZMEhOdjz.aUbss1oz1BCTJXjRat45EJrthO9k9hOsFydj6HUZzfkhr50Mg7uBOpxpzQYknRuxeVqZ.n14xSbbuf85OP+minNjWqHpB+Uy.LYVsW+s+bJ012rmI9bzDK+mxgZL.b5m.6KUa3XNwB4K+hbz2sLX.vmS5YvnO0ksGb.DQ36asmoaVEwuRsMw2XX1x3bYHifm06BYL3wM+LUdgrwB3Bp09KuidIy4DV+6D8fzDZs5GDt72SsAxCxXy5X5uHshx9XKjjZ5DYGxueuLdy551u.BM01wB4lTIO+LuKK952kKLyHA5VR6mBZcgYyT75ey6YE49qUYp7OlDXFj3iGA9KUgje+LPb4G4Z.b5SWBDIWs4jQ7GkO7Qhg9xMGsJBd6Bz1TApK11j03+A+BFrHsctfAKFNnnJQkqRVTMJ2H+qYHJWkjdH3iDf7CBN1+y8Z6BEZfuLnPiftuydu07peeqS9b9N4Sgvi1X12IASo7aYs0NAAmz5.rM1kBfJNs3j68u9r+yr9k6Ldl+xcZY3CKeOWjs2XGOIF2EOhzCJSvSryi8v0bw+0NTHsX+UbPtYNzO9s0fMYlzzE4Owk4aTdD8jcutH3KLa15a3CBZg2UePPumir7AqvHoueo6FJi7r9UgzwVvoEhxuvJ7KrWYbcCGamwCcrIFhF8NXvebv.rqnrm4FpruObjvnd9ww60AagQB.3l+i8NB.dHWPOguk5hhuweKWYZu9cpbwkcGUe7j.60Vb67yTwsu0k2O.E599NYw8CSVPyKDmr32vZSuMtojqXN4Zp+2uyyUTLyjER8lfhRYRQI4CB3OwVl07tjlEUUTCTEEEKOHryRhcB0fIwLnsDmZPDNc6+8aqxd66tXXmaxuHZpdVzkr8D3PzrRQKJRXb2kD69PjqIXCMdKjZSNluv7+n3Ce8Wz41J8tL2lxWmJ2lxu+Wf419vdYMy+K5KqY189f3g+71JdeUHtbIMloT6Cis78QAAuOViQHCWmyL3uX.0o+Nrdf8sM6+QNtqZCZasTOTfhxHHkwYFFxrJEgktsDt1skv0usDtwskvMusDt0skvsuYBoYzKOw2g+4XSKyocU9CIjiWgCyMQ4+iYkPuv
      
      Content.makeFrontInterface(400, 400);
      namespace Oscilloscope
      {
          const var Analyser1 = Synth.getEffect("Analyser1");
          const var BUFFER = Synth.getDisplayBufferSource("Analyser1");
          const var BUF_OSC = BUFFER.getDisplayBuffer(0);
          const var pnl_Osc = Content.getComponent("pnl_Osc1");
          const var timer = Engine.createTimerObject();
          const var Button1 = Content.getComponent("Button1");
          
          const BUF_LENGTH = 4096;  // Reduced buffer size for better CPU
          
          reg BUF_PROPERTIES = {"BufferLength": BUF_LENGTH, "NumChannels": 1};
          BUF_OSC.setRingBufferProperties(BUF_PROPERTIES);
          BUF_OSC.setActive(true);
          
          const TEMP_BUFFER = Buffer.create(BUF_LENGTH);  
          const DRAW_BUFFER = Buffer.create(BUF_LENGTH); 
          
          const FRAME_COUNT = 3;
          reg frameBuffers = [];
          reg currentFrameIndex = 0;
          
          // Cached path objects
          reg mainPath = Content.createPath();
          reg trailPath = Content.createPath();
          
          for (i = 0; i < FRAME_COUNT; i++)
          {
              frameBuffers.push(Buffer.create(BUF_LENGTH));
          }
          
          reg magnitude = 0.5;
          const MAGNITUDE_SMOOTH = 0.9;
          const MIN_MAGNITUDE = 0.05;
          
          // Decimation
          const DECIMATE_FACTOR = 32; 
          const DRAW_EVERY_N_FRAMES = 1;
          reg frameCounter = 0;
          
          const BACKGROUND_COLOUR = 0x00000000;
          const WAVEFORM_COLOUR = 0xA0FFAE00;
          const WAVEFORM_SHADOW = 0x60FF8000;
          const WAVEFORM_TRAIL = 0x30FFAE00;
          
          // Lower values for better performance
          const GLOW_EFFECT = true;
          const MOTION_TRAILS = true;
          const CORNER_SMOOTH = 3.0;
          const PATH_THICKNESS = 2.0;
          
          // Magnitude update
          inline function updateMagnitude()
          {
              local newMag = DRAW_BUFFER.getMagnitude();
              magnitude = magnitude * MAGNITUDE_SMOOTH + newMag * (1.0 - MAGNITUDE_SMOOTH);
              magnitude = Math.max(MIN_MAGNITUDE, magnitude);
          }
          
          // Frame storage - only copy decimated points
          inline function storeCurrentFrame()
          {
              for (i = 0; i < BUF_LENGTH; i += DECIMATE_FACTOR)
                  frameBuffers[currentFrameIndex][i] = DRAW_BUFFER[i];
              
              currentFrameIndex = (currentFrameIndex + 1) % FRAME_COUNT;
          }
          
          // Drawing function
          inline function drawOscilloscope(g, panel, mag)
          {
              g.fillAll(BACKGROUND_COLOUR);
              
              local width = panel.getWidth();
              local height = panel.getHeight();
              local midY = height / 2;
              
              local scaledHeight = height * Math.min(1.0, mag);
              local drawY = midY - scaledHeight/2;
              local bounds = [0, drawY, width, scaledHeight];
              
              // Draw trail frames if enabled
              if (MOTION_TRAILS)
              {
                  for (frameIdx = 0; frameIdx < FRAME_COUNT; frameIdx++)
                  {
                      if (frameIdx == currentFrameIndex)
                          continue;
                      
                      local age = (currentFrameIndex - frameIdx + FRAME_COUNT) % FRAME_COUNT;
                      local opacity = 0.7 - (age / FRAME_COUNT) * 0.6;
                      
                      trailPath.clear();
                      local sample = Math.max(-1.0, Math.min(1.0, frameBuffers[frameIdx][0]));
                      trailPath.startNewSubPath(0, midY - sample);
                      
                      for (i = DECIMATE_FACTOR; i < BUF_LENGTH; i += DECIMATE_FACTOR)
                      {
                          local x = (i / BUF_LENGTH) * width;
                          sample = Math.max(-1.0, Math.min(1.0, frameBuffers[frameIdx][i]));
                          trailPath.lineTo(x, midY - sample);
                      }
                      
                      trailPath.roundCorners(CORNER_SMOOTH);
                      
                      g.setColour(Colours.withAlpha(WAVEFORM_TRAIL, opacity * 0.5));
                      g.drawPath(trailPath, bounds, {"Thickness": PATH_THICKNESS * 0.5});
                  }
              }
              
              // Draw main path
              mainPath.clear();
              local currentFrame = frameBuffers[currentFrameIndex];
              
              local sample = Math.max(-1.0, Math.min(1.0, currentFrame[0]));
              mainPath.startNewSubPath(0, midY - sample);
      
              for (i = DECIMATE_FACTOR; i < BUF_LENGTH; i += DECIMATE_FACTOR)
              {
                  local x = (i / BUF_LENGTH) * width;
                  sample = Math.max(-1.0, Math.min(1.0, currentFrame[i]));
                  mainPath.lineTo(x, midY - sample);
              }
              
              mainPath.roundCorners(CORNER_SMOOTH);
              
              // Glow Effect
              if (GLOW_EFFECT)
              {
                  g.setColour(Colours.withAlpha(WAVEFORM_SHADOW, Math.min(1.0, mag)));
                  g.drawPath(mainPath, bounds, {"Thickness": PATH_THICKNESS * 2.0});
              }
              
              g.setColour(WAVEFORM_COLOUR);
              g.drawPath(mainPath, bounds, {"Thickness": PATH_THICKNESS/2});
          }
          
          pnl_Osc.setPaintRoutine(function(g) { 
              drawOscilloscope(g, this, magnitude); 
          });
          
          // Timer Function (Buffer copying)
          inline function onTimerCallback()
          {
              BUF_OSC.copyReadBuffer(TEMP_BUFFER);
              
              for (i = 0; i < BUF_LENGTH; i += DECIMATE_FACTOR)
                  DRAW_BUFFER[i] = TEMP_BUFFER[i];
              
              updateMagnitude();
              storeCurrentFrame();
              
              // Skip frames to improve performance
              frameCounter++;
              if (frameCounter >= DRAW_EVERY_N_FRAMES)
              {
                  pnl_Osc.repaint();
                  frameCounter = 0;
              }
          };
          
          timer.setTimerCallback(onTimerCallback);
          
          inline function onButton1Control(component, value)
          {
              if (value == 1)
              {
                  for (i = 0; i < FRAME_COUNT; i++)
                  {
                      for (j = 0; j < BUF_LENGTH; j += DECIMATE_FACTOR)
                          frameBuffers[i][j] = 0.0;
                  }
                  
                  timer.startTimer(30);
              }
              else
                  timer.stopTimer();
          };
          
          Button1.setControlCallback(onButton1Control);
      }
      
      ChazroxC 1 Reply Last reply Reply Quote 10
      • rglidesR
        rglides
        last edited by

        Really nice!

        1 Reply Last reply Reply Quote 0
        • ChazroxC
          Chazrox @HISEnberg
          last edited by

          @HISEnberg siiick 👍

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

          66

          Online

          1.7k

          Users

          11.7k

          Topics

          102.2k

          Posts