Old 01-19-2020, 11:31 PM   #1
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
Default A Basic Delay Plugin Explained

I got a request from a fellow user about how to make a basic delay plugin. I figured I would make it a public post so it was open for feedback if anyone else wanted to chime in. This first post will have the full source and the second post will break it down by sections and explain what is happening where.

The purpose is a basic short delay that can create echoes through feedback. I'm not going to claim to be the best teacher or explain the best and I can easily talk too much, but hopefully the user who asked will benefit anyways.

++changelog

2020/01/20 ... incorporated Ashcat's moving the feedback into the function itself, added db2ratio()

Also, I should point out that I put the default values to those exact settings because I liked how it sounded on Otaku Gang's "Ten Crack Commandments (Star Wars Remix)" which is the track I tend to test my plugins out on. And now you know.

Code:
desc:simple delay plugin

slider1:350<1,500,1>Delay (ms)
slider2:-15<-90,0,0.1>Wet (dB)
slider3:-45<-90,0,0.1>Feedback (dB)

@init

buf0 = 100000;
buf1 = 200000;

function db2ratio(db) ( 10^(db * 0.05); );

function delay_set(buffer,ms,fbGain)
 instance(buf,size,frac,pos)
(
  buf = buffer;
  size = srate * ms * 0.001;
  frac = size - floor(size);
  size = floor(size);
  feedbackGain = db2ratio(fbGain);
);

function delay(in)
 instance(pos,buf,old,out,size,frac,feedbackGain)
(
  out = buf[pos];
  out = out*(1-frac) + old*frac;
  old = buf[pos];
  buf[pos] = in + (out * feedbackGain);
  pos += 1;
  pos >= size ? pos -= size;
  out;
);

@slider

delay0.delay_set(buf0,slider1,slider3);
delay1.delay_set(buf1,slider1,slider3);

wetGain = db2ratio(slider2);


@sample

spl0 += delay0.delay(spl0) * wetGain;
spl1 += delay1.delay(spl1) * wetGain;

Last edited by SaulT; 01-20-2020 at 09:04 PM.
SaulT is offline   Reply With Quote
Old 01-19-2020, 11:32 PM   #2
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
Default

Code:
desc:simple delay plugin

slider1:350<1,500,1>Delay (ms)
slider2:-15<-90,0,0.1>Wet (dB)
slider3:-90<-90,0,0.1>Feedback (dB)
What controls do we want? This is a delay so it seems pretty straightforward - we want to select how long the delay is, and milliseconds is a fairly intuitive pick for units. I'm picking a simple volume control for how much delay to blend back in, in decibels. Similarly, I'm picking decibels for feedback. Audio people should know decibels, it's one of the basic units of measure. 90 decibels of attenuation is basically silence for all intents and purposes, so that's a good minimum.

Code:
@init

buf0 = 100000;
buf1 = 200000;
Okay, so I could have said something like "buf0 = 0; buf1 = buf0 + srate;" but instead I'm picking a predetermined range. Why is buf0 100k? Because I'm thinking of the worst case scenario - someone is in a 96 kHz samplerate and they pick a 1 second delay. We want our plugins to work anywhere from 22.05 kHz to 96 kHz samplerates, and what if we are trying to create a reverb with dozens of delay lines? We want to think ahead and plan for these things.

Code:
function delay_set(buffer,ms)
 instance(buf,size,frac,pos)
(
  buf = buffer;
  size = srate * ms * 0.001;
  frac = size - floor(size);
  size = floor(size);
);
Every time we change a slider we are changing something about the delay. One of the values of using functions is that instead of putting all of this in the @slider section we can put all of the variable tweaking in a separate function. Secondary benefit? By putting it in a function we can reuse the code for multiple delay lines. Remember, we are coding for two channels, left and right, right? Now, what if we wanted to add support for more channels? It's relatively simple, we create delay2 and so forth and call delay2.delay_set(...). It isn't full blown object oriented code, but using functions to create objects makes our code simpler and quicker to write!

Code:
 instance(buf,size,frac,pos)
when a variable calls this function it becomes an object and JS assigns these variables to that object. So delay0 will now have delay0.buf, delay0.size, etc.

Code:
  size = srate * ms * 0.001;
  frac = size - floor(size);
  size = floor(size);
Remember that we get milliseconds by dividing the samplerate (samples per second) by 1000. This is the same as multiplying by 0.001. The tricky part of this code is that if we want a precise delay often we will end up with a fractional value. For example, at 44100 kHz samplerate if we want a 35 ms delay then the exact value of that delay is 0.035 * 44100 = 1543.5 samples. We can't have half a byte of buffer so we use a technique called linear interpolation to get that fractional delay amount. I love interpolation, but for a basic delay plugin we really don't need anything fancier than linear interpolation.

Code:
function delay(in)
 instance(pos,buf,old,out,size,frac)
(
  out = buf[pos];
  out = out*(1-frac) + old*frac;
  old = buf[pos];
  buf[pos] = in;
  pos += 1;
  pos >= size ? pos -= size;
  out;
);
The heart of our plugin here. We are using a circular buffer to create our delay. This means that we are cycling through a buffer with a position variable keeping track of our place. So we pull out the current value, we linear interpolate the precise value, and put it aside for a second. Our new sample is put into the buffer, we move the position variable up one, and if it is greater than the size it goes back to the beginning. The last value we list in a function is what is returned, in javascript or whatnot we would specifically say return out;, here the return is assumed.

Code:
@slider

delay0.delay_set(buf0,slider1);
delay1.delay_set(buf1,slider1);

wetGain = 10^(slider2/20);
feedbackGain = 10^(slider3/20);
See how little code we need here? We update the values of our delays and we calculate our decibel multipliers. Remember, decibels are just ratios and volume adjustment is just a multiply.

Code:
@sample

spl0 += delay0.delay(spl0 + old0 * feedbackGain) * wetGain;
spl1 += delay1.delay(spl1 + old1 * feedbackGain) * wetGain;

old0 = spl0;
old1 = spl1;
This part of the code is executed every sample, right? So each sample we are putting the current sample and some of the previous output into our buffer, then make sure we save the current output for use next sample. Hopefully this is clear enough.

...

And, umm, that's basically it. Now, there are a lot of places you could go with this. You can take this technique with the circular buffers and make reverb functions, or you could put a filter on the delayed output, all kinds of things.

Remember that Reaper lists all of the current variables in the "development environment" window. You can check to make sure they look right. A common mistake I make when I'm writing is I'll forget to put a function's variable inside the instance() command. If I was to see the pos variable just hanging out but not delay0.pos and delay1.pos, then I'll know I forgot to put it in the instance().

I could have written the plugin without the functions, but it's such a valuable and important part of JS that I really needed to make sure that I illustrated it. Using functions makes your code a lot simpler to write, easier to read, and much more scalable.

Last edited by SaulT; 01-20-2020 at 12:23 AM.
SaulT is offline   Reply With Quote
Old 01-20-2020, 05:19 AM   #3
lgz
Human being with feelings
 
Join Date: Jan 2020
Posts: 4
Default best delay code

It's great that I saw it!! This post is really helpful. I learned a lot in the interpretation of each line of code!
lgz is offline   Reply With Quote
Old 01-20-2020, 09:27 AM   #4
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,093
Default

I appreciate your '... explained' posts (also this one), thanks.
nofish is offline   Reply With Quote
Old 01-20-2020, 10:14 AM   #5
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

db2ratio is such a ubiquitous function, and with all your talk about how useful functions are, you left it out. I’m not sure it would make the @slider section a lot tidier, but if we’re illustrating how functions save us from writing redundant code...

But wait! One big thing and then a slightly smaller thing.

1) We don’t want to feed the mix signal back through the delay at the end there. The feedback split should come before the mixer. In fact, it should probably happen in the delay function itself.

Code:
function delay(in)
 instance(pos,buf,old,out,size,frac)
(
  out = buf[pos];
  out = out*(1-frac) + old*frac;
  old = buf[pos];
  buf[pos] = in + (out * feedbackGain);
  pos += 1;
  pos >= size ? pos -= size;
  out;
);
Then in @sample you just send it the input and remove all references to old0 and old1.


The other thing I wanted to mention is more about the fundamental nature of this very simple type of delay. Basically, it’s gonna glitch hard and strange when you adjust the delay time. Consider what happens when we have a long time and then shorten it. We basically leave a bunch of buffer slots full of stuff, it never actually plays out so we kind of jump back to even further back then we already were, and then if we switch back to a longer time we’ll end up hearing stuff that maybe happened quite a while ago. We expect weird things when simple digital delays are modulated, and I understand that this example wasn’t meant to address that, but just pointing it out for those who might have higher expectations.

Last edited by ashcat_lt; 01-20-2020 at 10:42 AM.
ashcat_lt is offline   Reply With Quote
Old 01-20-2020, 08:59 PM   #6
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
Default

Quote:
1) We don’t want to feed the mix signal back through the delay at the end there. The feedback split should come before the mixer. In fact, it should probably happen in the delay function itself.
And this, friends, is why I made this a public post. I went through an iteration or two and for some reason the code just seemed weird to me. Ashcat is of course correct and this is the more proper way to do it. I'll update the code above with a changelog.

...also, for completeness sake, how about I add that db2ratio code.

A very good point, the delay is not going to smoothly modulate necessarily and this function is definitely not programmed to accommodate changes in delay length!
SaulT is offline   Reply With Quote
Old 01-21-2020, 12:31 AM   #7
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by SaulT View Post
Okay, so I could have said something like "buf0 = 0; buf1 = buf0 + srate;" but instead I'm picking a predetermined range. Why is buf0 100k? Because I'm thinking of the worst case scenario - someone is in a 96 kHz samplerate and they pick a 1 second delay. We want our plugins to work anywhere from 22.05 kHz to 96 kHz samplerates, and what if we are trying to create a reverb with dozens of delay lines? We want to think ahead and plan for these things.
You could also have stored the samples interleaved (i.e. 0, 1, 0, 1, 0, 1, ...). That way you don't have to worry about buf0 and buf1 overlapping, even beyond 96 kHz.
Tale is offline   Reply With Quote
Old 01-21-2020, 07:58 AM   #8
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
Default

Which is a valid point, of course. My thinking was that if someone wanted to either extend the technique to 3+ channels or take the concept to multiple delay lines for reverb or some such it would be more modular. In a different iteration of this type of plugin I downsampled the delay before storing it, opening up delays of potentially 30+ seconds, assuming that JS memory is still limited to 8 megs of course. It’s good that people know there are more than one way to approach the problem.
SaulT is offline   Reply With Quote
Old 01-21-2020, 10:55 AM   #9
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

So the OP kind of assumes that we already know how the memory buffer and “arrays” work in JS. That’s one of the weirder concepts both for new coders and for those of us with some experience in other languages where there real arrays to work with.

I have this stupid idea about doing the whole thing more like an analog delay where there is just a set number of samples in the buffer and we change delay time by changing how fast we read/write to it. I’m sure somebody’s done it somewhere. It seems like it helps both with modulation and with the fractional sample issue if done correctly. It also seems like it could potentially use lots of CPU.
ashcat_lt is offline   Reply With Quote
Old 01-21-2020, 01:51 PM   #10
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
Default

Default assumption of 22.05 kHz? If 44.1 or 48 then 2x downsample, if 88 or 96 then 4x downsample, that type of thing? It sounds plausible. A lot of time I’m lowpassing my delays anyways, there is certainly a style where you might not need or want anything above 10 kHz. The positive is more room for delay, the downside is all that up and downsampling... Seems plausible tho.
SaulT is offline   Reply With Quote
Old 01-21-2020, 02:57 PM   #11
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

Not exactly what I was saying.

Say for the sake of simplicity that we have a buffer 10 with 10 slots. If we want the delay to be 10 samples long, we read and write to each successive buffer slot on each sample. If we want a delay that is 5 samples long, we read/write every other slot per sample. If we want a 1 sample long delay we do it for every 10th slot. Then we do...something...to fill the slots that skipped for when we change the delay time again.
ashcat_lt is offline   Reply With Quote
Old 02-09-2020, 10:17 AM   #12
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

Quote:
Originally Posted by SaulT View Post
Similarly, I'm picking decibels for feedback. Audio people should know decibels, it's one of the basic units of measure. 90 decibels of attenuation is basically silence for all intents and purposes, so that's a good minimum.
I meant to mention this originally too, but caught up in other parts of it. I suppose it’s a little more advanced point than the very basics that you were addressing here, but at this point I think it’s safe to get into it.

In the floating point engine, this kind of assumption is dangerous. You can usually get away with it in analog because -90db is going to bury it in the noise floor, but here we don’t have that safety net. We do, however, have a bunch of ways that we might amplify and smash this signal after it comes back out. One might ask why anyone would add enough gain to hear this and I would answer “guitar amp”.

What I like to do is pick a lower usable limit and then have the slider go 1 step further than that and call that OFF. Like one of those volume controls with the power switch built in.

So like:
Code:
slider3:-45<-91,0,0.1>Feedback (dB, -91 = off)
Then in the delayset function, change the feedback line
Quote:
fbGain == -91 ? feedbackGain = 0 : feedbackGain = db2ratio(fbGain);
It IS discontinuous and could cause clicks going back and forth, but we haven’t bothered smoothing anything else yet, so...
ashcat_lt is offline   Reply With Quote
Old 02-09-2020, 10:58 AM   #13
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

Now this is going even further down the road...


But then I look at the other end of that slider. You’ve got it topping out at 0db in order to stop it from self-oscillating with escalating velocity. That’s a very good idea. Positive feedback here could end up getting REALLY LOUD. Just as the real noise floor is down past -300db, the real clipping ceiling is over 300db louder than your DAC can pass or any fixed point file can reproduce. I do these things on purpose sometimes. It gets stupid loud.

At 0db feedback, if we play something that is shorter than the delay time, it will repeat exactly the same literally forever. I’ve done that too. Turn off the speakers and walk away and come back days later and it’s still playing whatever i last did. That’s cool.

But it’s also going to mix in whatever you play next, and whatever you play after that, and the whole thing is going to keep getting louder and louder. Worst case scenario, you play the exact same thing over and over in time with the delay. This will also escalate quickly toward infinity. We have to decide how much we care about that. We can just do like ReaDelay and tell folks “yeah it does that”, or we could try to handle it ourselves.

The easiest way to stop it getting quite so loud is to just choose a limit and do that min(max()) thing before we write to the buffer. Then it’ll just get more distorted as you play. If we want that distortion to sound better, we call our favorite waveshaping function at this point instead. This will still end up getting pretty loud - heading toward square waves with RMS (!!!) levels equal to whatever ceiling we’ve set.

If we want it to work much more gracefully, we could take a page from some pedal delays which compress the signal on the way into the buffer and then expands it on the way back out. Now each of those processes are like a whole other thread like this, but it’s actually just a couple functions that we call from within our delay function. What ends up happening is that the whole mix gets pushed down as you add new material in a very natural and fairly transparent way. Real pedals also have real limits, and a combination of curvy saturation with compansion is really the best way to go.

Then you could even allow positive feedback for those whackjobs that actually enjoy self-oscillation. It’ll get loud, but it won’t open a portal to the abyss and take the entire known universe with it anymore.

Course then we’re going to want to automate that delay time...
ashcat_lt is offline   Reply With Quote
Old 04-07-2020, 04:44 AM   #14
Hypex
Human being with feelings
 
Join Date: Mar 2015
Location: Australia
Posts: 451
Default

Thanks SaulT for your guide. The code looks different to the JS: Delay. Either way it helps to know the relation between where Reaper is on the FX timeline and what happens in the sample routine. Been a while since I read up on JSFX and some effects do things differently.

Because it's my obsession, I wonder how you go about a stereo delay that bounces each delay across stereo channels? I've examined the JS Delay with the bounce version and I can't see it doing much apart from mixing the sample together, I presume to mono the sound, so that a mono sound gets bounced around. Not exactly a stereo delay. But it's not obvious where it bounces to left or right.

I also find it can be bit harsh, since it pans hard left or right and doesn't bring the bounces together. Nor have any stereo field control. It's not the best on a guitar lick but can be effective on a stereo vocal delay.
Hypex is offline   Reply With Quote
Old 04-08-2020, 11:13 AM   #15
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
Default

Thanks, there are many ways to skin a cat, as they say.

The simple way to create a stereo delay is to sum to mono then pan that mono signal to different places in the spectrum. Anything more complicated would have to start making assumptions about the incoming signal, I think. I haven’t done a lot of thinking about delay lately, it’s mostly been about saturation and lo-fi for me, is there something specific that you’re interested in?
SaulT is offline   Reply With Quote
Old 04-08-2020, 11:41 AM   #16
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

Ping pong delay really is just sending the feedback from each buffer to the other instead of back to itself. I took a quick look at how to hack this code into doing that, but didn’t have a lot of time.
ashcat_lt is offline   Reply With Quote
Old 04-09-2020, 08:21 AM   #17
Hypex
Human being with feelings
 
Join Date: Mar 2015
Location: Australia
Posts: 451
Default

I suppose what I'm most interested in is a ping pong delay as it's known. I shouldn't confuse terms; as I tend to think of a basic delay as being a mono delay, since it just copies the sound as is; and I think of a stereo delay as bouncing across the speakers in ping pong fashion, even though the delay bounced around is a mono copy. It's just when I listened to a song with an obvious stereo echo effect I would call it a stereo delay, though perhaps dual delay is a better term.


So I've tried a few things. I found a bouncing delay can be good for vocals. I suppose that would be an LCR delay, since I've applied it to a vocal track, and feed back both wet and dry. For vocals this it can be effective with only drums behind it.


I've also done this to a electric guitar chord sustained, and when it ends the echo trail left bouncing sounds good. For comparison, one example would be Billy Idol's Flesh for Fantasy. I recommend the extended mix as it starts with an immediate delay on the guitar that bounces away on the speakers, after the guitar stops.

I also tried it on a small guitar lick. It was fine as a standard delay. But bouncing it just sounded too harsh. For example look up Genesis, Land of Confusion, extended mix. After the guitar is brought in they delay the last note at 0:55 with what sounds like a filtered delay. It's simple but effective. I've tried duplicating the delay and found on a similar lick a 300 ms delay at -6dB feedback is close, just maybe a little faster than that song.


But, it doesn't have the cool bouncing effect either. So it sounds plain by itself or with only drums. However, putting a bounce on it can make it sound too harsh on the panning. This is where I wonder if some kind of LCR delay would be better to fatten up the delay so it has a cool bounce?


Now, back to the code. The problem with EEL is that it can be confusing to read. Having variables whose value points to an internal array location doesn't help for readability. In my own code I've considered to write an Array support function that acted as an allocator, both for memory management and readability. After seeing the code for this delay example it makes more sense than the uncommented code with senseless variable names. A few things stand out:


1. Just an observation. Here ratio is computed with 10^(db * 0.05). Or 10^(dB/20). Reaper JSFX uses 2^ (dB/6). Haven't looked if one is more accurate or the same result as the other.


2. The code here has helped me to see a mix buffer is used. Allowance of 1 second. I see this means all work must be performed in this window. I imagine this could be problematic if the delay goes past the window. It would be easy if the mix buffer was exactly as long as the effect would take, but with a window only then the delay must be calculated to work in that window. In the case the effect tails off longer than the window, quite likely, then a tail from the previous window must be calculated to continue. The code looks to handle this fine.


3. Bouncing ping pong echo effect in JS: Delay. After looking and re-looking I still cannot see how it bounces from one speaker to the other. I understand the idea behind it and myself would implement it by flipping a buffer or buffer index after counting the delay time. I don't see this. I see what looks like taking a mono mixdown multiplied by some right channel sample put in a left channel, and the left channel put in the right, at the same time. This doesn't make any sense! Not to me anyway. Where on earth is it flipping the channels!?


4. I see an immediate problem with bouncing, if the sample is longer than the delay. Then it will cut it off unless it's designed to compensate. I've heard clicks in the tempo synced delay JS Delay Tempo Ping-Pong and I'm sure this was happening the way it sounded. Going just by my ear.


5. I find I don't like the way Reaper uses a set Take FX buffer length. Some delays can keep trailing off and even though they tend to disappear in the mix by themselves they are still there in the mix. A sudden cut off will cause an audible click. I listened to some delays I set up and they cut out. I would prefer they fade out evenly on the tail to avoid clickage. I obviously need to add a soft gate to fade below a threshold. I just prefer not to layer one effect on another to compensate. I like to set up a single delay so it does disappear softly before the next note appears. Just me I guess, prefer to simplify it when I can. :-)

Last edited by Hypex; 04-09-2020 at 06:02 PM.
Hypex is offline   Reply With Quote
Old 04-09-2020, 09:56 AM   #18
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

Quote:
Originally Posted by Hypex View Post
1. Just an observation. Here ratio is computed with 10^(db * 0.05). Or 10^(dB/20). Reaper JSFX uses 2^ (dB/6). Haven't looked if one is more accurate or the same result as the other.
db = 20 log10 (Vout/Vin), so Vout/Vin = 10^(db/20) exactly. 2^(db/6) is an approximation which sets 6 "db" as exactly 2 times gain, which it really shouldn't be. Double gain is actually 6.02...something.


Quote:
2. The code here has helped me to see a mix buffer is used. Allowance of 1 second.
Well there's a space of 100000 slots between the start of the left buffer and the start of the right buffer, so the delay can be 100000/sample rate long before the left buffer starts to "leak into" the right buffer, which will not work the way you want. A lot of the JS delays do a thing where the left and right samples are sort of interleaved into the same buffer such that buffer[0+2x] is the left side and buffer[1+2x] is the right. This way you don't really have to define the end of the buffer, and can get it to be half as long as the 8 million or so slots will allow.


Quote:
3. Bouncing ping pong echo effect in JS: Delay.
You're talking about "Delay w/Tempo Ping-Pong"? Yeah, that's weird. Unless I'm missing something, I think it's more like a mono-summed delay with autopan on the output, which frankly you could just do with a pair of plugins. Not how ping pong delay normally works.





Quote:
4. I see an immediate problem with bouncing, if the sample is longer than the delay.
Again, not the normal way to do it. If you just cross-over the feedback between the buffers, it just naturally makes each consecutive repeat come up on the other side of the stereo field. They go back and forth on each repeat like a real ping pong should.



Quote:
5. I find I don't like the way Reaper uses a set Take FX buffer length.
I never use Take FX. Not necessarily for this reason, but it's another example of how automating Track FX is usually more useful.
ashcat_lt is offline   Reply With Quote
Old 04-09-2020, 10:38 AM   #19
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

Trying to make this particular delay ping pong is a bit complicated by a couple of things, and I'm not there yet, but I did notice that it's only actually putting out one repeat no matter what the feedback is set to because the delay_set is not storing feedbackGain as an instance variable, but the delay function is looking for it as an instance variable. Need to add that variable to the instance list in the _set function like so:
Code:
function delay_set(buffer,ms,fbGain)
  instance(buf,size,frac,pos,feedbackGain)
 (   buf = buffer;
     size = srate * ms * 0.001;
     frac = size - floor(size);
     size = floor(size);
     feedbackGain = db2ratio(fbGain); );
ashcat_lt is offline   Reply With Quote
Old 04-11-2020, 08:37 AM   #20
Hypex
Human being with feelings
 
Join Date: Mar 2015
Location: Australia
Posts: 451
Default

Quote:
Originally Posted by ashcat_lt View Post
db = 20 log10 (Vout/Vin), so Vout/Vin = 10^(db/20) exactly. 2^(db/6) is an approximation which sets 6 "db" as exactly 2 times gain, which it really shouldn't be. Double gain is actually 6.02...something.
Damn, 2^(db/6) equation looked so neat in the code. I suppose it was intended to provide a rigid set of values.

Quote:
Well there's a space of 100000 slots between the start of the left buffer and the start of the right buffer, so the delay can be 100000/sample rate long before the left buffer starts to "leak into" the right buffer, which will not work the way you want. A lot of the JS delays do a thing where the left and right samples are sort of interleaved into the same buffer such that buffer[0+2x] is the left side and buffer[1+2x] is the right. This way you don't really have to define the end of the buffer, and can get it to be half as long as the 8 million or so slots will allow.

Yes I've seen that. Stereo tends to be stored in interleaved fashion in the sample formats I've noticed. Given it needs to reference each sample with a stereo pair as a point in time this makes sense.


Quote:
You're talking about "Delay w/Tempo Ping-Pong"? Yeah, that's weird. Unless I'm missing something, I think it's more like a mono-summed delay with autopan on the output, which frankly you could just do with a pair of plugins. Not how ping pong delay normally works.

Yeah that one. Looking at it now, it does actually feature a stereo width it uses to pan each side. Which looks useful. And has an obvious bounce. But, it goes through 400 samples then does something, which doesn't look right.


Quote:
Again, not the normal way to do it. If you just cross-over the feedback between the buffers, it just naturally makes each consecutive repeat come up on the other side of the stereo field. They go back and forth on each repeat like a real ping pong should.

Well that must be what the "Delay w/Stereo Bounce" is doing. If it is doing a cross-over feedback effect that a neat way of doing it. Because the bouncing wasn't obvious. Right now I'm trying to understand where the delay starts. Logically, it would start at time offset set for the delay, but even in the example here the sample position seems to start at 0.


Quote:
I never use Take FX. Not necessarily for this reason, but it's another example of how automating Track FX is usually more useful.

Since I discovered it I find it useful if there is just a short section of a track I want to put some FX on. I tend to have an FX track below so I can isolate some section and apply some FX. But it's useful to avoid adding another track as takes up space. But folding the tracks would help here. Ideally I'd set up FX tracks so I can fold them out of the way so I just see the main tracks so it looks better organised.

Last edited by Hypex; 04-11-2020 at 08:43 AM.
Hypex is offline   Reply With Quote
Old 08-09-2022, 03:17 PM   #21
reapercurious
Human being with feelings
 
reapercurious's Avatar
 
Join Date: Jul 2007
Posts: 1,888
Default

I'm re-learning how to make a delay line and happened upon this thread.

Wondering something about this delay effect; if feedbackGain is at 0db (*1) then why doesn't the feedback fly into screaming self oscillation the way reaDelay would?
reapercurious is offline   Reply With Quote
Old 08-09-2022, 03:48 PM   #22
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,264
Default

At 0db feedback, you should just get an infinite repeat. It will tend to escalate in volume as you play into it more, but it shouldn’t take off screaming until there is positive gain in the feedback loop. ReaDelay has some resonance in its filters which I think is why it’s a little harder to control.
ashcat_lt is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 03:12 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.