How to prevent "zipping" effect on a modulated fractional audio delay line? (Flanger) I am implementing a Flanger using a fractional delay line.  I am modulating the length of the delay line using a sin function.  The delay line already uses linear interpolation to compute the delay output for the fractional sample.

When the delay length is decreasing (and pitch is increasing), the modulated length sounds just about perfect, with no artifacts.  However, when the delay length is increasing (and pitch is decreasing), I am getting a kind of "zipper" effect, with intermittent clicks, that one would expect from a non-interpolated delay line, as if sharp edges are being produced.

My guess is that I need to do additional interpolation beyond the simple fractional interpolation, but I have some questions.

First, why would I only hear artifacts during the period where my modulated delay length is decreasing?

Second, what is the best method to reduce these artifacts?  My first instinct is an all-pass filter, but is it typical to use an all-pass filter as well as an interpolation over a fractional delay length?  I just want to make sure I'm getting it right, and not wasting unnecessary cpu cycles :).

Some code will help.  In this code, config holds all delay line info, and delaySamples is the current working length of the delay line.  There are two read pointers, two write pointers, and two feedback values - one for each channel.  Thanks for taking a look!

void delayconfig_setDelayTime(DelayConfig *config, float delay) {
    config-&gt;delayTime = delay &gt; 0 ? (delay &lt;= 1 ? delay : 1) : 0;
    if (config-&gt;delayTime &lt; 0.0001) config-&gt;delayTime = 0.0001;
    config-&gt;delaySamples = config-&gt;delayTime*SAMPLE_RATE;
    int channel;
    float *rp, *wp;
    pthread_mutex_lock( &amp;delayMutex);
    for (channel = 0; channel &lt; 2; channel++) {
        rp = &amp;(config-&gt;rp[channel]);
        wp = &amp;(config-&gt;wp[channel]);
        *rp = *wp - config-&gt;delaySamples;
        while (*rp &lt; config-&gt;delaySamples) {
            *rp += config-&gt;delaySamples;
        }
     }
     pthread_mutex_unlock( &amp;delayMutex);
}

float interp(float *delayBuffer, float delaySamples, float position) {
    position = fmod(position, delaySamples);
    int prev = floorf(position);
    int next = fmod(ceilf(position), delaySamples);
    float frac = position - prev;
    return delayBuffer[prev] + frac*(delayBuffer[next] - delayBuffer[prev]);
}

void delay_process(void *p, float **buffers, int size) {
    DelayConfig *config = (DelayConfig *)p;
    float out;  
    int channel, samp;
    float *wp, *rp;
    for (channel = 0; channel &lt; 2; channel++) {
        rp = &amp;(config-&gt;rp[channel]);
        wp = &amp;(config-&gt;wp[channel]);
        for (samp = 0; samp &lt; size; samp++) {
            // here is where I'm changing my delay length.  The values are tentative while I'm figuring this out, and aren't really improtant.
            // The important thing is thing is that it's a sin function
            delayconfig_setDelayTime(config, 0.0061f + 0.006f*sin(M_PI*config-&gt;count++*INV_SAMPLE_RATE));
            pthread_mutex_lock(&amp;delayMutex);
            if (*rp &gt;= config-&gt;delaySamples) (*rp) -= config-&gt;delaySamples;
            if (*wp &gt;= config-&gt;delaySamples) (*wp) -= config-&gt;delaySamples;
            float interpolated = interp(config-&gt;delayBuffer[channel], config-&gt;delaySamples, (*rp)++);
            int wpi = floorf((*wp)++);
            pthread_mutex_unlock(&amp;delayMutex);
            out = interpolated*config-&gt;wet + buffers[channel][samp]*(1 - config-&gt;wet);
            if (out &gt; 1) out = 1;
            config-&gt;delayBuffer[channel][wpi] = buffers[channel][samp] + out*config-&gt;feedback[channel];         
            buffers[channel][samp] = out;
        }
    }
}
It's a bug.

M_PI*config->count++

You are processing 2 channels, but incrementing your modulation in the inner loop. So at the end of processing 1 channel, you continue to increment the modulation as you process the second channel.

c1 [ 1,2,3,4]         [9,10,11,12]...
c2           [5,6,7,8]


The modulation has a discontinuity, i.e in my simplifies example - c1 modulation jumps from '4' to '9'. resulting in zipper noise. Hope that makes sense.