News:

SMF for DIYStompboxes.com!

Main Menu

Multi Effects Pedal

Started by MstrKurt, November 20, 2013, 11:35:51 PM

Previous topic - Next topic

ElectricDruid

Quote from: MstrKurt on February 04, 2014, 04:40:21 PM
Thanks Slacker, I will give it a try according to the datasheets soon and see if I hit a wall.

I was told today that the sample time that is able to be achieved by 16k of RAM can be increased by lowering the resolution of my ADC or lowering the sample rate?. Is there any truth to this?.

Yes. At least in theory.

For a given amount of memory (like 16K) if you reduce the ADC resolution, then the input samples you're storing take up less bits, and you'll get more samples in, which will give you longer delays. In practice, reducing the samples to (say) 11-bits with a 16-bit RAM would cause enough difficulties that I seriously doubt it would be worth the extra effort. 12-bit is a borderline case, since at least you'd be handling whole nibbles, but it's still not easy and is going to eat up processor cycles that would be better used for something else.

Alternatively, if you reduce the sampling rate, then a delay of X samples gets longer by the same amount. If you're sampling at (say) 44KHz and you've got 1 second total delay, then reducing the sample rate to 22KHz will give you 2 seconds delay. You can trade off bandwidth against delay time. This has no other consequences like changing the ADC resolution. Usually it's far more straightforward to do and would involve changing the setup of whatever sets the sample rate - a timer or perhaps the master clock rate.

HTH,
Tom

MstrKurt

Thanks for that information, Tom.

MstrKurt

Hey guys, I've had slow progress over the last month from dealing with some personal trauma's.

I've just got back in to the swing of things and realised that the distortion has seem to stop working :S.

The circuit is unchanged and the only additions I've made is switchable effects. The code for distortion I have is:



  if (Input>threshold)
  {
Input = ADC1BUF0;     
Input = threshold;  //  Clip the input
     
  }

    Output = Input;     // Sets output = clipped input


  DAC1RDAT = Output;       // Send output to the DAC


Threshold is some nominal value that in this example is 2000, but will be set by a potentiometer eventually.
Where

slacker

Hope the personal trauma's were nothing serious.

I think your code is wrong or there's some bits missing. It looks to me like the "input = ADC1BUF0;" should be before the if statement not inside it, that would set input = ADC1BUF0 then if it's above threshold it sets input to threshold instead, that would clip the positive side of the input.

MstrKurt

#64
Thanks for the help Slacker, I think that's worked however, I've found a problem that's further going to hinder my progress.. 2 of the pickups screw holes on my guitar have snapped from being squashed in the travel bag. Is there any way I can buy new pickups for relatively cheap? or is it better to take it to a guitar shop.

Thanks!.


EDIT:
https://www.dropbox.com/s/va7n1xtz9bn6fzx/Guitar%20Pickups.jpg - Here is an image to show what I mean. Notice that 2 of the pickups plastic holes have snapped off.

EDIT 2:
Would anyone know how I can generate a clean guitar signal from my PC maybe route it through the Line Out port to the input of my pedal to see the effects without a guitar?.

slacker

Ouch. If it was mine I'd take one of the screws down to the local hardware store and see if I could find some nuts that would fit, fit the pickup then screw the nut on to hold it at the correct height, maybe use two nuts to lock it in place. If you can't find nuts to fit the existing screws you'll probably find a suitable replacement screw or bolt.

Ice-9

Re- pickups, I would get some screws and nuts, not the self tapper screws that are in but something like a 2mm thread (or what is the best size to match diameter). I would then put it together and glue the nut with some epoxy glue to the underside of the pickup, that way the pick up height would still be adjustable.
www.stanleyfx.co.uk

Sanity: doing the same thing over and over again and expecting the same result. Mick Taylor

Please at least have 1 forum post before sending me a PM demanding something.

MstrKurt

Thanks for the help, both. I will look to purchase some nuts tomorrow :)

MstrKurt

Hey again guys,

Would anyone know how I'd implement a Phaser effect?.

Am I right to assume that I get the ADC value, and store that in a fractional variable. Then take that variable through a chain of all pass filters and save the output of them to another variable and then multiply the original signal with the APF'd variable?.

slacker

#69
Yeah that's pretty much it, that will give you fixed phase shifted effect. To get the sweeping phaser effect you need to modulate the allpass filters so that the phase shift moves about.

I don't think you need to use fractional numbers you can just use signed integers.

There's a pretty good explanation of how to do phasing and other effect here http://www.spinsemi.com/knowledge_base/effects.html#Phase_shifting

Digital Larry

Though you don't multiply the direct signal with the phase shifted one - just add them together.  Invert one or the other and you'll get a different sound.  Add some feedback and yet more colors will emerge.
Digital Larry
Want to quickly design your own effects patches for the Spin FV-1 DSP chip?
https://github.com/HolyCityAudio/SpinCAD-Designer

slacker

Oh yeah I missed that, might be interesting if you did multiply them, some sort of weird ringmoddy octavy mess of a thing.

MstrKurt

Thanks a lot for the info guys. I may actually try multiplying them and see what comes of it!

MstrKurt

Since trying Distortion out on a new guitar, the distortion seems to decay very quickly.

I've used an Overdrive type of path where I multiply the input by some random value which pushes the wave to the clipping points of the dsPIC. Any idea why it gives a harsh decaying sound?.

Digital Larry

If all you are doing to get harmonics is to slam the signal to the clipping point, you can expect a harsh result.  The gain is constant up to that point and then it immediately becomes zero.  This puts a real sharp edge on the signal, making high order harmonics come out.

Soft clipping like you naturally get from vacuum tubes or well designed distortion pedals has a more gradual transition.  In order to implement this in your DSPic, you'd need to implement some math function (I've read that hyperbolic tangent (tanh) is good for this).  Or you could try piecewise linear, like:

Input = 0 to 0.5, gain = 1.5
Input = 0.5 to 0.75, gain = 1.2 (then you have to add 0.15, so that you don't get a jump at the point where the gain changes)

I.e. since 0.5 x 1.2 = 0.6, to get the level to 0.75 (0.5 x 1.5 in the first gain range) you need to add 0.15 to the level in the second gain range.

etc.

These are just ideas... many ways to do it.  If you try to do math functions in real time you may be limited by the speed of your microcontroller and the efficiency of the supplied math library.  Piecewise linear should be less load on the CPU but may not give you the sound you want.
Digital Larry
Want to quickly design your own effects patches for the Spin FV-1 DSP chip?
https://github.com/HolyCityAudio/SpinCAD-Designer

MstrKurt

#75
Ok that sounds a good plan DL.

One thing I don't understand however is the adding the 0.15 part.

Does this mean that if the input is 0 to 0.5 the gain is 1.5, if the gain is 0.5 to 0.75 the gain is 1.2 but with the added 0.15 it's 1.35?

EDIT:


    if ((Input > 0)&&(Input<0.5))
    {
        Input = Input*2;
    }
    else if((Input>=0.5)&&(Input<1))
    {
        Input = Input*1.5;
    }
    else if((Input>=1)&&(Input<1.5))
    {
        Input = Input*1.2;
    }
   else if((Input>=1.5)&&(Input<1.8))
   {
     Input = Input*1.1;
   }
   else if((Input>=1.8)&&(Input<1.98))
   {
       Input = Input * 1;
   }

Is this correct?

Digital Larry

What you want to do is make sure that at the transition point, the calculation is the same for that point using the equation on either side.  Clearly you are only going to calculate that point using one of the equations.  This is like slope - intercept y = mx + b stuff from algebra.  The idea is to make sure that as you go through the gain transition point there's not a jump in the waveform.  Even this is not truly soft clipping as it still creates sharp corners (but not 90 degree type) on the waveform at the gain transition points.  Soft clipping approach changes gain continuously as input amplitude increases.  Nevertheless, it should be less harsh than simply clipping abruptly at some signal level, and you can fairly quickly try out different approaches to see if any of them are acceptable.  I've never actually done what I am describing!

E.g.

0 to <= 0.5:

Output = input * 1.5

at 0.5, output = 0.75.

For the next vertical slice, reduce the gain to 1.2.  But to get the result to be 0.75 at 0.5, you need to ADD 0.15 in this section.

> 0.5 to <= 0.8

Output = input * 1.2 + 0.15 (if input is negative, subtract 0.15 instead)

If your next gain transition is at 0.9:

0.9 * 1.2 + 0.15 = 1.23

If the gain reduced to 1.1, then

0.9 * 1.1 + x = 1.23 ... so x = 0.24

Hope this makes sense!

Digital Larry
Want to quickly design your own effects patches for the Spin FV-1 DSP chip?
https://github.com/HolyCityAudio/SpinCAD-Designer

MstrKurt

Still not catching on I don't think.


    if ((Input > 0)&&(Input<0.5))
    {
        Input = Input*1.5;
    }
    else if((Input>=0.5)&&(Input<0.8))
    {
        Input = ((Input*1.2)+0.15);
    }
    else if((Input>=0.8)&&(Input<0.9))
    {
        Input = ((Input*1.2)+0.15);
    }
   else if((Input>=1)&&(Input<1.2))
   {
     Input = ((Input*1.2)+0.15);
   }
   else if((Input>=1.2)&&(Input<1.3))
   {
     Input = ((Input*1.2)+0.15);
   }
   else if((Input>=1.3)&&(Input<2))
   {
     Input = ((Input*1.2)+0.15);
   }

Is that what you mean?.

Using that code, I'm getting a constant whining tone out of the amplifier when nothing is being input, however the distortion transition is a lot smoother.

---------------------------------
DELAY: This is not my code, but is available on DSP Related's website.

Code in main:

    Input = dc_removal(Input);
    Delay_set_fb(0.8);                                                 
    Delay_set_delay(8000);

    Output=Delay_task(Input);
    DAC1RDAT = Output;


Code called Delay:


#include "p33fxxxx.h"
#include "dsp.h"
#include "libq.h"

#define smul(a,b) ( (long)(a)*(b) )
#define sround( x )  (short)( ( (x) + (1<<14) ) >>15 )

#define S_MUL(a,b) sround( smul(a,b) )


/****************DELAY.C*******************************/
#include "delay.h"
#include "math.h"

#define MAX_BUF_SIZE 8000  //8000

/*****************************************************************************
*       Fractional delay line implementation in C:
*
*                    ---------[d_mix]--------------------------
*                    |                                         |
*                    |                                         |
*                    |x1                                       v
*     input ------>[+]----->[z^-M]--[interp.]----[d_fw]-------->[+]-----> output
*                 ^                         |
*                 |                         |
*                 |----------[d_fb]<--------|
*******************************************************************************/

fractional d_buffer[MAX_BUF_SIZE] __attribute__((far));
fractional prev_x;
fractional prev_y;

#define DC_REMOVAL_POLE Q15(0.999)

/*
This interface defines the delay object
*/
static struct fract_delay {
    fractional d_mix;       /*delay blend parameter*/
    int d_samples;    /*delay duration in samples*/
    fractional d_fb;            /*feedback volume*/
    fractional d_fw;            /*delay tap mix volume*/
    int n_fract;     /*fractional part of the delay*/
    fractional *rdPtr;      /*delay read pointer*/
    fractional *wrtPtr;     /*delay write pointer*/
char reverse_buffer;
};

static struct fract_delay del;

/*
This function is used to initialize the delay object
*/
void Delay_Init(short delay_samples,fractional dfb,fractional dfw, fractional dmix) {
        Delay_set_delay(delay_samples);
        Delay_set_fb(dfb);
        Delay_set_fw(dfw);
        Delay_set_mix(dmix);
        del.wrtPtr = &d_buffer[MAX_BUF_SIZE-1];
        del.rdPtr = del.wrtPtr - (short)del.d_samples;
        /*Wraps read pointer*/
        if (del.rdPtr < d_buffer) {
                del.rdPtr += MAX_BUF_SIZE-1;
        }
del.reverse_buffer=0;
}

/*
These functions are used as interface to the delay object,
so there's not direct access to the delay object from
external modules
*/
void Delay_set_fb(fractional val) {
    del.d_fb = val;

}

void Delay_set_fw(fractional val) {
    del.d_fw = val;
}

void Delay_set_mix(fractional val) {
    del.d_mix = val;
}

void Delay_set_delay(short n_delay) {
    //short dif;
    if (n_delay==0) n_delay=1;
    /*Get the integer part of the delay*/
    del.d_samples = n_delay;

    /*gets the fractional part of the delay*/
    //dif = (n_delay - del.d_samples);
//del.n_fract= dif;
}

fractional Delay_get_fb(void) {
    return del.d_fb;
}

fractional Delay_get_fw(void) {
    return del.d_fw;
}

fractional Delay_get_mix(void) {
    return del.d_mix;
}

void Delay_put_fifo_value(fractional val){

// writes value in fifo
*(del.wrtPtr) = val;

    /*Increment delay write pointer*/
        del.wrtPtr++;

    /*Wraps delay write pointer*/
        if ((del.wrtPtr-&d_buffer[0]) > MAX_BUF_SIZE-1) {
                del.wrtPtr = &d_buffer[0];
        }
}

fractional Delay_get_fifo_value(void) {

        fractional x_est;

//get buffer next value
        x_est = *(del.rdPtr);

    /*Increment delay read pointer*/
        del.rdPtr++;

    /*Wraps delay read pointer*/
        if ((del.rdPtr-&d_buffer[0]) > MAX_BUF_SIZE-1) {
                del.rdPtr = &d_buffer[0];
        }
return x_est;
}

fractional Delay_get_fifo_ping_pong(void) {

        fractional x_est;
fractional x_est2;
int offset;

// if (del.rdPtr < del.wrtPtr) {
offset = (abs(del.wrtPtr - del.rdPtr));
if (offset > MAX_BUF_SIZE/2){
offset = MAX_BUF_SIZE - offset;
}
// }
// else {
// offset = del.rdPtr - del.wrtPtr;
// }

if (del.rdPtr==del.wrtPtr) {
del.reverse_buffer =1;
}
if (del.rdPtr==del.wrtPtr+1) {
del.reverse_buffer =0;
}
// pending to add check for when wrtPtr is just the last buffer byte
//get buffer next value
        x_est = *(del.rdPtr);


//find the point at a max distance
if (del.rdPtr < &d_buffer[0]+(MAX_BUF_SIZE/2)){
x_est2= *(del.rdPtr+MAX_BUF_SIZE/2);
}
else{
x_est2= *(del.rdPtr-MAX_BUF_SIZE/2);
}

    /*Increment delay read pointer*/
        if (del.reverse_buffer) del.rdPtr--;
else del.rdPtr++;

    /*Wraps delay read pointer*/
        if ((del.rdPtr-&d_buffer[0]) > MAX_BUF_SIZE-1) {
            del.rdPtr = &d_buffer[0];
        }
        /*Wraps read pointer*/
        if (del.rdPtr < d_buffer) {
                del.rdPtr += MAX_BUF_SIZE-1;
        }
offset = ((long)offset * 0x8000) / (MAX_BUF_SIZE/2);
// x_est = S_MUL(x_est , offset) ;
// x_est += S_MUL(x_est2, 0x8000-offset);

return x_est;
}

/*
This is the main delay task,
*/
fractional Delay_task(fractional xin) {
        fractional yout;
        fractional * y0;
        fractional * y1;
        fractional x1;
        fractional x_est;

    /*Calculates current read pointer position*/
        if (del.wrtPtr > d_buffer+ (short)del.d_samples)
del.rdPtr = del.wrtPtr - (short)del.d_samples;
        else
del.rdPtr = del.wrtPtr + MAX_BUF_SIZE - (short)del.d_samples;


        /*Wraps read pointer*/
//        if (del.rdPtr < d_buffer) {
//                del.rdPtr += MAX_BUF_SIZE-1;
//        }

        /*Linear interpolation to estimate the delay + the fractional part*/
        //y0 = del.rdPtr-1;
        y1 = del.rdPtr;

        //if (y0 < d_buffer) {
        //    y0 += MAX_BUF_SIZE-1;
        //}

        //x_est = (*(y0) - *(y1))*del.n_fract + *(y1);  // to be validated
x_est= *(y1);

    /*Calculate next value to store in buffer*/
    x1 = xin + S_MUL(x_est,del.d_fb);  //should it be S_MUL?
    //x1= xin;
        /*Store value in buffer*/
        *(del.wrtPtr) = x1;

    /*Output value calculation*/
    //    yout = S_MUL(x1,del.d_mix) + S_MUL(x_est,del.d_fw);  // should it be S_MUL?
yout = S_MUL(x1,del.d_mix) + S_MUL(x_est,del.d_fw);  // should it be S_MUL?

    /*Increment delay write pointer*/
        del.wrtPtr++;

    /*Wraps delay write pointer*/
        if ((del.wrtPtr-&d_buffer[0]) > MAX_BUF_SIZE-1) {
                del.wrtPtr = &d_buffer[0];
        }
        return yout;
}
/***********DELAY.h*************************************/
#ifndef __DELAY_H__
#define __DELAY_H__

void Delay_Init(short delay_samples,fractional dfb,fractional dfw, fractional dmix);
void Delay_set_fb(fractional val);
void Delay_set_fw(fractional val);
void Delay_set_mix(fractional val);
void Delay_set_delay(short n_delay);
fractional Delay_get_fb(void);
fractional Delay_get_fw(void);
fractional Delay_get_mix(void);
fractional Delay_get_fifo_value(void);
fractional Delay_get_fifo_ping_pong(void);
void Delay_put_fifo_value(fractional val);
fractional Delay_task(fractional xin);

#endif


fractional ga= Q15(0.95);  //0.95
fractional gr= Q15(0.9998);  //0.999

fractional calc_envelope(fractional input, fractional envelope){
input = _Q15abs(input);
if (envelope<input){
envelope= S_MUL(envelope, ga);
envelope+= S_MUL((Q15(1)-ga),input);
}
else {
envelope= S_MUL(envelope, gr);
envelope+= S_MUL((Q15(1)-gr),input);
}
return envelope;
}

fractional dc_removal(fractional input)
{
// y[n] =  x[n] - x[n-1] + p*y[n-1]       (where p = pole, 0 < 1-p << 1)
prev_y= S_MUL(prev_y , DC_REMOVAL_POLE);
prev_y= prev_y + input - prev_x;
prev_x= input;


return prev_y;
}



/*****USAGE EXAMPLE****************************************/

void test_func(void) {
    fractional xin;
    fractional yout;
Delay_Init(50,Q15(0.7),Q15(0.7),Q15(1));
fractional i=Q15(-0.8);
    while(1) {

            /*When there's new sample at your ADC or CODEC input*/
            /*Read the sample*/
xin =  i;
i = i >> 1;
            /*Apply the Delay_task function to the sample*/
            yout = Delay_task(xin);

            /*Send the output value to your ADC or codec output*/
//          tracevalue = yout; //write_output(yout);
// tracevalue2 = xin;
// traceout();

    }
}



I'm not 100% I'm getting a delayed result. If I increase the Delay_set_fb(0.8); to a higher value I can hear some form of very short reverb type effect, but nothing to prove delay is evident.  (Unfortunately I know very little about this code, and even less of how it works, I don't quite understand the Q15 stuff, S_MUL etc).





Digital Larry

What is the actual range of the input signal that the DAC sees?  Is it about 0 when there's no input or is it offset?  Does it go + and - or only +?

I'll take a closer look at the code some time!
Digital Larry
Want to quickly design your own effects patches for the Spin FV-1 DSP chip?
https://github.com/HolyCityAudio/SpinCAD-Designer

MstrKurt

The average swing of the input from the pickups of the guitar is around 100-200mV (that's going in to the ADC) with a bias voltage of 1.6V. So it has a maximum level of -1.6V on the negative half cycle?