Another Delay-based Looper

Delay Looper

This looper records to an Audulus4 delay line, the delay time becoming the loop period. Up to 100% feedback can be set, creating an endless sound loop. The module allows either continuous layering or single capture. The capture window is either fixed or input-based with fade in and fade out to avoid clicks. Fade options are to fade out at by the end of the initial capture period or to allow signal decay to layer over subsequent passes.

The delay node is set up internally with its mix set 100% to the delayed output, but the module if armed to record can pass the live audio through to the output as it’s being recorded to the delay node (see mix input).

To avoid transient bursts at the breakpoints of audio, the input, output, and feedback for the delay node are faded in or out or crossfaded over a specific amount of time. To avoid adding discontinuities into the audio as the fade is occurring, the fader control runs at the sample rate, producing a control signal that follows a cosine function over the range of zero to pi radians (aka 0 to 180 degrees, the two points where the cosine function has zero slope). Applied as the control value for a level node or crossfade node, this control signal reduces signal amplitude quickly without adding higher frequency artifact.

If the mix input is set to a value greater than zero, the looper will add the current audio stream directly to any loop output, with full volume when mix equals one. With mix set to zero the latest stream will only begin to be heard after the loop period has ended.

The looper includes peak limit functionality, turned on by the AGC button (it’s not exactly an automatic gain control, but AGC fits better on the button). The peak limit section computes a rolling* average of output envelope and if the average exceeds the set point will goose the feedback level back from 100% until the output level has hit the set point. In Peak Limit mode, the feedback knob setting serves as the set point. This allows you to create huge chorus effects without blowing out the audio level. With the ACG (peak limit) off, the knob sets the % feedback.

  • (Actually, it’s more a pseudo rolling average, as this uses only one storage element, making it a form of IRR filter. A true N-sample rolling average requires N storage elements so you can remove the N+1 th item)

Outputs for loop period and a stream start pulse are brought out for use downstream, to trigger an LFO, for example.

I/O

Outputs

Loop output – The audio output, which is a mix of any existing audio loop and any live audio stream being added, depending on the setting of the mix input.

Pout–The current loop period, in seconds (note that this is the same as the looper’s period, which is truncated to give an integer number of samples).

sync–a 20ms pulse, its leading edge marking the start of the most recently recorded stream.

Inputs

In – The audio input. Its role depends on the state of the module. The input, as routed to the delay node contained in this module, is referred to as the audio stream or the stream.

Mix – sets how much of the current audio stream, presented at the input of the internal delay node, will be passed to the output. When the module isn’t currently recording, there is no audio stream, so only the present loop is output.

Pin – The time base for the looper. This is a floating point value at the “Pin” input, which is multiplied or divided by an integer set by adjusting the “period mult/div” knob. The resulting value is quantized to the sample rate to avoid blurring as the feedback is resampled.

Layer – TRUE sets layered recording mode in which a new sample stream adds to the existing audio. FALSE sets single mode, starting a fresh loop with each stream. (Note that in single mode, streams longer than the loop period can wrap around. See “tails,” below.)

samp – A trigger initiates capture of an audio stream for at least one loop period. Holding samp TRUE extends capture time. Holding this input high extends the capture if the tails input is TRUE.

erase – A trigger initiates an erasure of the loop.

tails – FALSE sets the looper to force a fade-out before the end of one period. Best for sampled live audio which may never fade entirely. TRUE allows the capture to continue until the input has dropped to nearly zero.

Arm – TRUE allows new samples to be recorded. If FALSE, looper plays and can be erased but it can’t be recorded to.

AGC — input or button controls peak-limit mode. Tthe button and input are connected to an XOR expression node, so one but not both true will set peak limit mode. Green light indicates peak limit is on.

Modes of operation

this module will either layer audio streams continuously into the loop)o or will add each new audio stream as a new loop (“layer”==FALSE).

In single mode capture, a trigger applied at the “samp” input initiates recording an audio stream into the delay node, crossfading what emerges from the looper from the previous loop output to the live input and crossfading back when the initial period has passed, effectively erasing the old loop while recording the new one. Audio is either rapidly faded out by the end of one period (tails==FALSE) or is allowed to fade to zero naturally (tails==TRUE). Any audio in the stream initiated by the initial samp^ hit will be recorded until one of those fade-out scenarios plays out.

With “layer“ set to TRUE, audio streams layer over older streams. Any hit to “samp” will initiate a new audio stream having a duration at minimum the period of the loop, and with the same fade-out characteristics as for single mode.

The delay node when used in this configuration can produce audio volume values too high for the OS to handle. To avoid eventual sound system crashes, I added an arbitrary limit to the envelope, which once hit, will force the looper to perform an erase operation.

A trigger at the “erase” input stops recording and empties the delay by zeroing the input and feedback on the delay node for a time equal to the current delay period, plus the fade/crossfade time*. In either recording mode, any hits to “samp” after erase is underway will initiate a new capture into the looper, erasing the old loop audio.

*Because of the delay to 100% fade that’s inherent to the operation of the smooth fade control, a blip of audio can remain at the very beginning of the loop if erasing for exactly the recording time. Adding the fade time to the period when erasing mops up that blip as it wraps back to the beginning.

A pulse is issued from the sync output with each new stream recorded (one continuous recording into the looper I’m calling a “stream”) and for each subsequent playback of the loop. As subsequent streams are overlayed in layer mode, the sync pulse will mark the latest stream added in the layering.

Demo movie


Version History

[2023-12-10]
Delay Looper Module v2023.12.10.01.audulus4 (47 KB)

  1. Fixed a slipping sync pulse problem. It was slipping by one sample per pass.
  2. Narrowed the sync pulse that’s output from the module. It was originally set to make the LED light visible, but the wide pulse was not appropriate for high loop rates. Now the sync pulse is set to one sample width.

[2023.06.12]
Delay Looper Module v2023.06.12.01.audulus4 (46.5 KB)
Canvas node displays the mode and input time mult/divide time display.
Uses Audulus 4’s delay line, replacing the legacy delay node.

[2020.03.09.01]
Delay Looper Module.audulus (105.6 KB)
Changes in this version:
•Was still hearing loss of high frequencies due to smearing at different sample rates. I realized that I had neglected to replace the sample rate quantizer between the time selector section at the bottom of the layout and looper logic and cross faders. Accurate timing there makes sure the smooth faders have a whole number of samples to operate over.
•Retained the time scaling and 44.1 khz quantizarion at the delay node from the previous update, as these were needed to ensure the correct whole number of samples in the delay for the current sample rate.
[2020.03.03.01]
Delay Looper Module.audulus (103.6 KB)
Changes in this version:
•Fixed the timing problem when playing loops at sample rates different from 44100 Hz. Off 44.1 kHz sample rates the period played by the delay node did not match the value applied, becoming proportionally shorter as the sample rate was increased. It appears that the value at the knob only matches the loop time when the rate is 44100 Hz, so it must be scaled by the ratio of the current sample rate to 44100. The scaled value is then quantized to a whole number of 44100 Hz samples to eliminate smearing with resampling.
•Increased the range of input period scaling to */÷ 32.
[2020.08.24.01]
Delay Looper Module.audulus (102.5 KB)
Changes in this version:
This version adds the mix input and changes the internals so that the live input stream can be summed with the loop output if desired, from zero to 100%.
[2020.08.21.01]
Delay Looper Module.audulus (100.4 KB)
Changes in this version:
Don’t allow change of start marker timing from sync output if the module is no armed for recording.
The upscaled number of crossfade number fo samples was not an integer for some sample rates, is now rounded up to the closest integer number of samples.
[2020.08.13.01]
Delay Looper Module.audulus (98.4 KB)
Changes in this version: Added the Pout and sync outputs.
[2020.07.20.01]
Delay Looper Module.audulus (92.4 KB)
[2020.07.02.01]
Delay Looper Module.audulus (92.4 KB)
[2020.07.20.01]
Delay Looper Module.audulus (137.0 KB)
[2020.06.25.01]
Delay Looper Module.audulus (133.1 KB)
[2020.06.19.01]
Delay Looper Module.audulus (128.8 KB)
[2020.06.10.03]
Delay Looper Module.audulus (208.9 KB)

Demos


Noise Gate and Looper Demo


Looper Daisy Chain Demo

Demo Revisions

[2020.08.27]
Noise Gate and Looper Demo.audulus (446.5 KB)

[2020.08.21]
Noise Gate and Looper Demo 2020.8.21.audulus (441.1 KB)
Looper Daisy Chain Demo.audulus (1.3 MB)
This demo has two loopers. The new sync and period outputs from the first looper is used to set up the second looper (with a shorter loop time) and drives a modulation signal which is synchronized with the last stream laid down in the first looper.
[2020.08.13.01] Pout and sync out used to control a modulation source
Noise Gate and Looper Demo 2020.8.13.audulus (436.2 KB)
[2020.08.02.02] put my res drum mod into the drum input.
Noise Gate and Looper Demo.audulus (375.2 KB)
[2020.08.02.01]
Noise Gate and Looper Demo.audulus (332.7 KB)
[2020.07.03.01] in which I hooked up the sound from the noise gate so you’ll hear the immediate as well as the looper output that comes later!
Noise Gate and Looper Demo.audulus (420.3 KB)
[2020.07.02.01]
Noise Gate and Looper Demo.audulus (420.1 KB)
[2020.06.30 Noise Gate Work (looper demo)]
Noise Gate Work.audulus (267.8 KB)
[2020.06.25.01]
Delay Looper Play Set.audulus (1.4 MB)
[2020.06.19.01]
Delay Looper 14.audulus (1.3 MB)
[2020.06.11]
Delay Looper Demo.audulus (521.1 KB)

7 Likes

This looper patch works really well for making seamless loops.

1 Like

That’s definitely what I was aiming for. I’m glad it’s working for you!

2 Likes

There is a problem with the calculations for the loop period on mobile devices with a 48k default sample rate using the demo patch. For example, setting the bpm to 120 and the period multiplier to 16, you get an 8 sec period for the loop length as you’d expect on 44.1k sample rate devices whereas you get a 7 sec 350 ms loop length with the same settings on a 48k sample rate device.

I’m tried to figure out where the adjustment needed to be made. The problem seems to be that the Audulus 3 internal clock seems to be running at a 44.1k sample rate so if you multiply the bpm frequency value 1/f by 48/44.1 that is fed into the Delay Looper module in the patch, the loop length will be correct.

The 44.1K and 48K sample difference on mobile Apple devices has been a headache for app developers for years now.

2 Likes

Dang! I tried to avoid that problem by using the Samplerate node everywhere to provide that value for calculations, but guess what? I only tested on MacOS. My bad. I’ll take a look.

2 Likes

One thing I noticed when looking at timing was that I didn’t correct the crossfade number of samples for the various sample rates. The 300 sample number was based on testing at the 44100 hz sample rate. The number needs to be adjusted by the ratio of the current sample rate to 44100. The patch makes the result an integer again. That way the transitions should be similar for other sample rates. Also, this module may not work for all higher rates. More later.

2 Likes

I’ve started looking back at the earlier, simpler timing circuit, applying what I’ve learned doing this one. I suspect I can get all the same functionality with hardwired logic, now that I have my little fader thingie And have learned more about how these delays work. So far so good!

3 Likes

I uploaded a new version and demo patch. The Looper is lighter weight, my having switched to straight logic snarl. The demo features a noise gate module I created to allow audio above some set point initiate a new stream to the looper, mostly for hands off mic keying.

2 Likes

I just uploaded looper version 2020.06.25.01, as well as a new version of the demo patch.
Dynamics of the peak limit function were changed to make a bit more responsive.
Change to the noise gate to aid in voice capture (details in the post) by adding a holdoff time at the end of each capture.

2 Likes

The July 2nd update and new demo (just a single looper with noise gate and source selector. I assume many of you are either retired or are in technical professions that translate to work-at-home. Either way, have a great July 4th weekend (and yes, you, too, you non—USA folks, for whom the 4th of July means little or nothing!) Stay safe and wear that mask when you’re out and about (this mainly for my US friends, as we’ve done a terrible job so far in containing this.)

4 Likes

New Looper and demo patch in topmost post. Cheers!

Changes in this version:
Replaced the running averager module used in the auto level module with the point to point logic implementation to reduce load on the CPU.

Renamed references to clock to read “period,” which describes the function a little better.

Converted the overload indicating LED driver with a dark-to-white + Red overload theme.

Added an external option to control whether feedback is to be managed by the level limiter module or is to be left as simple feedback. The external input is XOR-ed with the button state, so both remain functional–the button could be thought of as “reverse the action of the external input.”

Renamed delay readout to time rather than frequency. Red still marks longer and blue shorter times.

New dynamics for the peak limiter, tightening it up by dropping the sample length to 1/4 second, compensating by reducing the correction gain from 1 to 1/2 the current over-limit amount.

Rewired the samp indicator light so that its color during capture matches the layering mode–blue for layered and red for single stream.

3 Likes

Fixed misleading description of the layer input.

1 Like

Note that the demo MIDI keyboard patch is set to channel 16. To try on your iPad with screen keyboard pop it open and change it to Omni.

1 Like

New version in post one, with a period out and sync out added to allow modulation or other processes downstream. The sync pulse marks the beginning of the latest recorded stream.

2 Likes

New version, with some bug fixes and a horrible looking demo movie. Note the MIDI keyboard is set in this demo to ch16, single note.

1 Like

I added a mix input to add the live stream to the loop output during recording. With mix left at zero, any new input will only begin after the delay. This now works the same for layered and single mode.

1 Like

Always stay 1 upping yourself! That’s awesome, cuz your creations are already so incredible to begin with! You’re like the Apple hardware of audio DSP design lol :blush:

1 Like

Thanks! I’ve been so distracted by work, the election, and life that I’ve neglected my Audulus!

1 Like

I just uploaded version 21.03.03.01 of the looper, hopefully fixing the timing problem with sample rates differing from 44100 Hz. No new demos as there’s no real functional difference.

1 Like

Well, another tweak the other day, adding the original sample rate quantization back at the output of the time selection section. I’d forgotten that the faders need that quantization, too,

1 Like