[sdiy] help understanding circular buffers for delay line

Andrew Simper andy at cytomic.com
Fri Mar 2 05:07:36 CET 2012


If you are using a power of 2 buffer length then just use an "&" of
the length - 1 for starters as this will be much easier to code, worry
about optimisation later:

buffer [index & (length - 1)]

so for a fixed integer sample delay you would have:

inpos = (inpos + 1)  & (length - 1);
buffer [inpos] = input;
output = buffer [(inpos - delay) & ( length - 1)];

and make sure delay is less than length - 1

Andy



On 2 March 2012 11:53, Matthew Smith <matt at smiffytech.com> wrote:
>
> Quoth Dan Snazelle at 02/03/12 12:27...
> ...
>
>> So, these two indexes....are they both driven by "for" loops?(one with an offset?)
>
>
> This is one of those concepts I'd generally try to explain using diagrams. However, let's see what we can do in plain text.
>
> For starters, if you haven't already done so, I'd suggest you have a look at this Wikipedia article:
>
> http://en.wikipedia.org/wiki/Circular_buffer
>
> ...but possibly stopping when you get to the code, as they are using the dreaded malloc which I have, on more than one occasion, been told to avoid when dealing with small micros.
>
> Here's a very rough example, without using pointers (which I probably would in Real Life) where I am sampling at 48ksps, 8 bits.
>
> # define SIZE_OF_BUFFER 4096
>
> unsigned char mybuffer[SIZE_OF_BUFFER];
> int i=0; // write index
> int j=0; // read index
> for (;;)
> {
>  i++;
>  if (i==SIZE_OF_BUFFER)
>  {
>  i=0; // We are indexing from zero, so 4095->0.
>  }
>
>  j=i+1;
>  if (j==SIZE_OF_BUFFER)
>  {
>  j=0;
>  }
>
>  mybuffer[i]=read_dac();
>  write_dac(mybuffer[j]);
>
>  delay(); // wait until it's time for the next sample.
> }
>
> In real life, to do make this run in time, I'd make this an ISR, rather than an infinite for loop, or use an RTOS.
>
> What's happening here is a simple delay. As I haven't initialised mybuffer[], for the first pass through the loop, write_dac() will be
> spitting out indeterminate values - something that should be avoided in a real implementation.
>
> After that first pass through though, what will be read from the buffer into the DAC will be the sample that you wrote in there SIZE_OF_BUFFER iterations ago. (Note that the oldest entry in the buffer is the NEXT position, or zero if we've reached the end.)
>
> If we are sampling at 48ksps, this means that the value going to the DAC will be (1/48000) * 4096 = 0.0853 seconds behind what's being read in.
>
> To decrease the delay, we change the relationship between i and j.
>
> To give the effect of an echo, we can scale the value coming out by, say, shifting it 2 places to the right (divide by 4.) Then we can add it to the signal being written, shift the result 1 place to the right (divide by 2, because we could now have a 9 bit number, having added to the 8 bit number) and then send that to the DAC. So the value coming out is half of what's gone in, plus a quarter of what was there 85ms ago.
>
> These are very rough examples (probably with errors!) but I hope they make it a bit more clear.
>
> One thing you may have gathered - any significant delay will need a fair amount of RAM to achieve which, in a microcontroller context, will probably involve the use of external RAM. (It's dirt cheap, but does mean an extra chip.)
>
> Cheers
>
> M
>
> --
> Matthew Smith
>
> Business: http://www.smiffytech.com
> Blog:     http://www.smiffysplace.com
> Linkedin: http://www.linkedin.com/in/smiffy
> Flickr:   http://www.flickr.com/photos/msmiffy
> Twitter:  http://twitter.com/smiffy
>
> ABN 16 391 203 815
>
> _______________________________________________
> Synth-diy mailing list
> Synth-diy at dropmix.xs4all.nl
> http://dropmix.xs4all.nl/mailman/listinfo/synth-diy



More information about the Synth-diy mailing list