HISE Logo Forum
    • Categories
    • Register
    • Login

    Free Reverse Delay built in RNBO

    Scheduled Pinned Locked Moved Blog Entries
    14 Posts 7 Posters 1.7k 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.
    • M
      Mighty23 @HISEnberg
      last edited by Mighty23

      @HISEnberg
      I just started a reverse delay idea myself. Since the post was looking for both RNBO and FAUST solutions, here is my implementation:

      import("stdfaust.lib");
      
      MAX_DELAY = 192000;  // Support up to 192kHz
      
      // Create phase-shifted phasor for reverse reading only the delayed signal
      phasor_phase(dtime, phase) = ((os.lf_rawsaw(dtime) + phase) % dtime) : int;
      phasor(dtime, phase) = phasor_phase(dtime*2, phase) <: <=(dtime), (*(-1) + dtime*2), _ : select2;
      
      // Main processing
      process = _ <: direct, delayed_and_reversed :> _
      with {
          // Get current sample rate for scaling
          current_sr = ma.SR;
          
          // Controls - scale the delay time based on current sample rate
          delay_base = hslider("Delay Time[style:knob]", 0.5, 0.02, 1, 0.01);  // 0-1 range
          delay_time = min(MAX_DELAY-1, delay_base * current_sr); // Scale to current sample rate
          dry_wet = hslider("Dry/Wet[style:knob]", 0.5, 0, 1, 0.01);
          
          // Direct path
          direct = *(1-dry_wet);
          
          // First delay the signal
          delayed = de.delay(MAX_DELAY, delay_time);
          
          // Then reverse the delayed signal
          reversed = rwtable(MAX_DELAY, 0.0, 
                            phasor(delay_time, 0) : int,    // Write the delayed signal
                            _,                              // Input from delay
                            phasor(delay_time, delay_time/2) : int   // Read reversed
                           );
                           
          delayed_and_reversed = delayed : reversed : *(dry_wet);
      };
      

      Free Party, Free Tekno & Free Software too

      LindonL T 2 Replies Last reply Reply Quote 1
      • LindonL
        Lindon @Mighty23
        last edited by

        @Mighty23 you dont appear o be using cuurrent_sr anywhere..

        HISE Development for hire.
        www.channelrobot.com

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

          @Lindon I edited the code in the previous comment.
          The key is to make the delay time scale with the sample rate.

          Free Party, Free Tekno & Free Software too

          1 Reply Last reply Reply Quote 0
          • T
            treynterrio @Mighty23
            last edited by

            @Mighty23 thank you! what's the process to get 2 channels tried to copy paste the process but it still says only 1 Channel

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

              @treynterrio you can use this code if you have 2 inputs and 2 outputs or use the previous one if you have one input and one output.

              The first code you can use in scriptnode in case you need to process, for example, mid and side separately.

              import("stdfaust.lib");
              
              MAX_DELAY = 192000;  
              
              // Phase-shifted phasor for reverse reading
              // dtime: delay time in samples
              // phase: phase offset for reading position
              phasor_phase(dtime, phase) = ((os.lf_rawsaw(dtime) + phase) % dtime) : int;
              phasor(dtime, phase) = phasor_phase(dtime*2, phase) <: <=(dtime), (*(-1) + dtime*2), _ : select2;
              
              // Single channel reverse delay processing
              reverse_delay = _ <: direct, delayed_and_reversed :> _
              with {
                  // Get current sample rate for scaling controls
                  current_sr = ma.SR;
                  
                  // User controls with sample rate scaling
                  delay_base = hslider("Delay Time[style:knob]", 0.5, 0.02, 1, 0.01);  
                  delay_time = min(MAX_DELAY-1, delay_base * current_sr);
                  dry_wet = hslider("Dry/Wet[style:knob]", 0.5, 0, 1, 0.01);
                  
                  // Direct path with gain
                  direct = *(1-dry_wet);
                  
                  // Delay into reverse buffer implementation
                  delayed = de.delay(MAX_DELAY, delay_time);
                  
                  // Reverse buffer using rwtable
                  reversed = rwtable(MAX_DELAY, 0.0, 
                                    phasor(delay_time, 0) : int,    // Write index
                                    _,                              // Input signal
                                    phasor(delay_time, delay_time/2) : int   // Read index
                                   );
                                   
                  // Process delayed signal through reverse buffer with gain
                  delayed_and_reversed = delayed : reversed : *(dry_wet);
              };
              
              // Main stereo processing - apply reverse_delay to each channel independently
              process = reverse_delay, reverse_delay;
              

              Free Party, Free Tekno & Free Software too

              JulesVJ 1 Reply Last reply Reply Quote 1
              • JulesVJ
                JulesV @Mighty23
                last edited by JulesV

                @Mighty23 It works great, thanks!

                The delay value is in 0 - 1 range. What is it referring to?
                So what about time-based reverse? Like milliseconds, or 1/4, 1/8, 1/16?

                M 2 Replies Last reply Reply Quote 0
                • M
                  Mighty23 @JulesV
                  last edited by

                  @JulesV
                  the delay value (0-1 range) refers to delay time as a fraction of one second.

                  • delay_base ranges from 0.02 to 1.0

                  • This gets multiplied by current_sr (sample rate), so:
                    at 44.1kHz: 0.5 = 22.050 samples = 500ms, 1.0 = 44.100 samples = 1 second

                  To do this in milliseconds, I can modify the implementation (I just need to remember). If I don't remember or am unmotivated to do it, feel free to message me and send a reminder :).

                  To switch from ms to tempo sync, there's a node for this:
                  https://docs.hise.dev/scriptnode/list/control/tempo_sync.html

                  Free Party, Free Tekno & Free Software too

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

                    @JulesV

                    import("stdfaust.lib");
                    
                    MAX_DELAY = 192000;  
                    
                    // Phase-shifted phasor for reverse reading
                    // dtime: delay time in samples
                    // phase: phase offset for reading position
                    phasor_phase(dtime, phase) = ((os.lf_rawsaw(dtime) + phase) % dtime) : int;
                    phasor(dtime, phase) = phasor_phase(dtime*2, phase) <: <=(dtime), (*(-1) + dtime*2), _ : select2;
                    
                    // Single channel reverse delay processing
                    reverse_delay = _ <: direct, delayed_and_reversed :> _
                    with {
                        // Get current sample rate for scaling controls
                        current_sr = ma.SR;
                        
                        // User controls in milliseconds 
                        delay_ms = hslider("Delay Time (ms)[style:knob]", 500, 20, 1000, 1);  
                        delay_time_raw = min(MAX_DELAY-1, delay_ms * current_sr / 1000);
                        delay_time = si.smooth(ba.tau2pole(0.05), delay_time_raw);
                        dry_wet = hslider("Dry/Wet[style:knob]", 0.5, 0, 1, 0.01);
                        
                        // Direct path with gain
                        direct = *(1-dry_wet);
                        
                        // Delay into reverse buffer implementation
                        delayed = de.delay(MAX_DELAY, delay_time);
                        
                        // Reverse buffer with windowing to reduce clicks
                        // Use a single buffer with smoothed reading
                        write_idx = phasor(delay_time, 0) : int;
                        read_idx = phasor(delay_time, delay_time/2) : int;
                        
                        // Apply Hann window to reduce discontinuities
                        window_pos = (phasor(delay_time, delay_time/2) / delay_time);
                        hann_window = 0.5 * (1.0 - cos(2.0 * ma.PI * window_pos));
                        
                        reversed = _ <: rwtable(MAX_DELAY, 0.0, write_idx, _, read_idx) * hann_window;
                                         
                        // Process delayed signal through reverse buffer with filtering and gain
                        // Add gentle low-pass filter to reduce aliasing artifacts
                        delayed_and_reversed = delayed : reversed : fi.lowpass(2, current_sr * 0.45) : *(dry_wet);
                    };
                    
                    // Main stereo processing - apply reverse_delay to each channel independently
                    process = reverse_delay, reverse_delay;
                    
                    

                    I tried a few additions because the previous version worked decently with mostly percussive sounds, but with instruments like vocals or pads, it tended to create too many clicks/pops and glitchy audio.

                    Test this new version (milliseconds > Delay time) included.

                    Free Party, Free Tekno & Free Software too

                    JulesVJ 1 Reply Last reply Reply Quote 2
                    • JulesVJ
                      JulesV @Mighty23
                      last edited by

                      @Mighty23 Thanks for the update, it sounds better. I think you made the transitions smoother, right?

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

                        @JulesV said in Free Reverse Delay built in RNBO:

                        Thanks for the update, it sounds better. I think you made the transitions smoother, right?

                        Yes, transitions are smoother now because of three changes:

                        1- Smoothed delay time

                        delay_time = si.smooth(ba.tau2pole(0.05), delay_time_raw);
                        

                        2 - Hann window on reverse buffer

                        hann_window = 0.5*(1 - cos(2*ma.PI*window_pos));
                        reversed = rwtable(...) * hann_window;
                        

                        3 - Gentle (?) low-pass filter:

                        ... : fi.lowpass(2, current_sr*0.45);
                        

                        this is to try to: avoid clicks, reduce aliasing

                        Free Party, Free Tekno & Free Software too

                        JulesVJ 1 Reply Last reply Reply Quote 0
                        • JulesVJ
                          JulesV @Mighty23
                          last edited by

                          @Mighty23 I tried it on vocals and pads, and rarely there're some clicks, but it's definitely better than the previous one.

                          Will there be a problem if we set the delay time higher than 1000 ms? Like 3000 ms?

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

                          19

                          Online

                          1.9k

                          Users

                          12.4k

                          Topics

                          107.7k

                          Posts