[sdiy] help understanding circular buffers for delay line

Matthew Smith matt at smiffytech.com
Fri Mar 2 04:53:12 CET 2012


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



More information about the Synth-diy mailing list