Plug-ins, Hosts, Apps,
Hardware, Soundware
Developers
(Brands)
Videos Groups
Whats's in?
Banks & Patches
Download & Upload
Music Search
KVR
   
KVR Forum » DSP and Plug-in Development
Thread Read
Some trouble with vibrato
LemonLime
KVRist
- profile
- pm
- e-mail
PostPosted: Thu Apr 26, 2012 7:20 pm reply with quote
Hello.

I'm currently exploring writing a vibrato program/plug-in and I have it mostly working pretty well except for what sounds like sample noise in the output. I've parsed the code and stepped through breakpoints several times, but the problem is eluding me. Here is a sample of the output with the undesired noise:

http://www.christianfloisand.com/vibtest.wav

Here is the code (un-optimized for easier reading):


SR = 44100
VDEL_T = 0.0005         // variable delay time
PHASE = 0            // phase for sine wave modifier
MDEL_S = (int) VDEL_T * SR   // maximum delay in samples
buffer = new float[1024]   // soundfile buffer
delayBuf = new float[MDEL_S]
memset (delayBuf, 0, sizeof(float)*MDEL_S)
writeIndex = 0    // write position for delayBuf

// the vibrato function, using variable delay and a sine wave LFO
//    to modulate the delay time

void vibrato (float *input, float *delayBuf, int *writeIndex, int frames)
{
   int readIndex;
   float out, delSamples, rawPos, fract, next;

   for (int i = 0; i < frames; i++) {      // frames = 1024

// modulate delay time by sine wave LFO
      delSamples = (VDEL_T * sin(PHASE) * SR;
      if (delSamples > MDEL_S)
         delSamples = (float) MDEL_S;

// calculate read pos in buffer relative to write pos
      rawPos = *writeIndex - delSamples;
// and check bounds within circular buffer, delayBuf
      rawPos = (rawPos >= 0 ? (rawPos < MDEL_S ? rawPos : rawPos - MDEL_S) : rawPos + MDEL_S;
      readIndex = (int) rawPos; // get read pos in buffer
      frac = rawPos - readIndex; // for interpolation

      if (readIndex != MDEL_S-1)
         next = delayBuf[readIndex+1];
      else
         next = delayBuf[0];

      out = delayBuf[readIndex] + frac * (next - delayBuf[readIndex]);
      delayBuf[*writeIndex] = input[i];
      input[i] = out;

// wrap writeIndex if needed
      if (*writeIndex != MDEL_S)
         *writeIndex++;
      else
         *writeIndex = 0;

      PHASE += TWOPI * freq / SR; // increment phase, freq = 6.4Hz
      if (PHASE >= TWOPI) PHASE -= TWOPI;
      if (PHASE < 0) PHASE += TWOPI;
   }
}

// in the main loop, read from soundfile (in) into buffer
//   (vector size of 1024), process vibrato on buffer,
//    then write to output file (out)

do {
         
   readCount = sf_readf_float(in, buffer, VECTORSIZE);

   vibrato(buffer, delayBuf, &writeIndex, readCount);
         
   writeCount = sf_writef_float(out, buffer, readCount);
         
} while (readCount);



Any ideas what is causing the sample noise? I'm using a slightly modified variable delay algorithm from a text ("Audio Programming", by Boulanger & Lazzarini). And so far I'm only handling mono files until I get it perfected (rid of the noise). Smile

Many thanks for any feedback.
Chris
^ Joined: 15 Apr 2012  Member: #278696  Location: Toronto, ON
aciddose
KVRAF
- profile
- pm
- e-mail
- www
PostPosted: Thu Apr 26, 2012 8:09 pm reply with quote
write a delay class and apply unit tests.

once you have a working delay object applying a modulation to the time is trivial.

here is a very quick example:


   class delay
   {
   public:

      delay()
      {
         time = 0;
         size = 0;
         buffer = 0;
         end = 0;
         pos = 0;
      }

      ~delay()
      {
         if (buffer) delete [] buffer;
      }

      INL int get_pos()
      {
         int p = pos - buffer - time;
         return (p >= 0 ? p : p + size);
      }

      INL void set_size(int n)
      {
         if (size != n)
         {
            size = n;
            time = 0;
            fraction = 0.0f;

            float *n = new float[size];
            float *t = buffer;
            buffer = n
            pos = buffer;
            end = buffer + size;
            clear();

            if (t)
            {
               delete [] t;
            }
         }
      }

      INL int get_size()
      {
         return size;
      }

      INL void set_time(float v)
      {
         ASSERT(v >= 0);
         ASSERT(v < size);
         time = ftol(v);
         fraction = v - time;
      }

      INL int get_time() { return time; }

      INL void clear()
      {
         if (buffer)
         {
            float *p = buffer;
            while (p < end)
            {
               *p++ = 0.0f;
            }
         }
      }

      INL void advance()
      {
         ASSERT(pos >= buffer);
         ASSERT(pos < end);
         ASSERT(end > buffer);
         pos++;
         if (pos >= end) pos = buffer;
      }

      INL float read_lerp(int i, float f)
      {
         ASSERT(pos);

         float *A = pos - i;
         float *B = pos - i - 1;

         if (B < buffer)
         {
            B += size;
            if (A < buffer)
            {
               A += size;
            }
         }

         return lerp(*A, *B, f) + 1e-10f;
      }

      INL float read()
      {
         return read_lerp(time, fraction);
      }

      INL void feed(float input)
      {
         ASSERT(pos);
         *pos = 1E-10f + input;
         advance();
      }

      INL float read_feed(float input)
      {
         float tmp = read();
         feed(input);
         return tmp;
      }

   private:

      int size;
      float *buffer;
      float *end;
      float *pos;

      int time;
      float fraction;
   };


it could use a LOT more assertions to make sure everything is always valid. these can be defined only when you have a specific define like ENABLE_ASSERTIONS. you can then set your project configuration up so that you have release, debug, test/assert.

the set_size function could actually allocate a new size and schedule the buffers to be exchanged on the next process call. it's quite complex enough for a simple example though.

then it just becomes a matter of calling

constructor() {
delay.set_size(max_time);
}

process() {
delay.set_time(base_time + modulation);
for (i = 0; i < samples; i++) {
output = delay.read_feed(input);
}
}

if you do anything wrong the assertions will trigger to show you what you messed up.
^ Joined: 07 Dec 2004  Member: #50793  
LemonLime
KVRist
- profile
- pm
- e-mail
PostPosted: Fri Apr 27, 2012 8:29 pm reply with quote
I did start out by coding the algorithm I gave above to work with a simple constant delay, and it does -- it works perfectly fine with a constant delay. Looking over your code that you provided helped me find a solution though! At the top of the loop where I calculate the variable delay in samples I had:

delaySamples = baseDelayTime * sine() * SR

Then I changed it to this, which solved my problem:

delaySamples = ((baseDelayTime / 2) + (sine() * baseDelayTime / 2)) * SR

Works like a charm! Smile
Thanks for the feedback.
^ Joined: 15 Apr 2012  Member: #278696  Location: Toronto, ON
aciddose
KVRAF
- profile
- pm
- e-mail
- www
PostPosted: Fri Apr 27, 2012 9:16 pm reply with quote
don't forget that you can't have a delay of zero if you read before feed. you have to add special code to handle that. you'll figure that out if you implement a comb filter / flanger.
^ Joined: 07 Dec 2004  Member: #50793  
LemonLime
KVRist
- profile
- pm
- e-mail
PostPosted: Sun Apr 29, 2012 12:56 pm reply with quote
Yeah, that makes sense. I'll keep that in mind, thanks!
^ Joined: 15 Apr 2012  Member: #278696  Location: Toronto, ON
All times are GMT - 8 Hours

Printable version
Page 1 of 1
Display posts from previous:   
ReplyNew TopicPrevious TopicNext Topic
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Username: Password:  
KVR Developer Challenge 2012