Oscilloscope Script
-
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
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); }
-
Really nice!
-
@HISEnberg siiick