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.
    • 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

                        17

                        Online

                        1.7k

                        Users

                        11.8k

                        Topics

                        102.7k

                        Posts