PWM compressor

GroupDIY Audio Forum

Help Support GroupDIY Audio Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.
abbey road d enfer said:
B) I often use 40+dB of compression; why? Because I can. Check the piano at the end of "A day in the life".

As I understand  a lot of live fader riding was done during the take.

Cheers

Ian
 
squarewave said:
I like it. But you could improve the parts a LOT. A regular ol' 74H cmos chip is going to give you distortion. Something high voltage with low on resistance (Ron) and good on resistance flatness would be better. Like MAX4600 (and with that particular chip you wouldn't need an inverter because it's dual SPST with one NO and and one NC). With low Ron and +-15V circuit you could get the noise and distortion very low. You could also use an LCL filter to get a 3 pole filter, keep the characteristic impedance low and use small passive parts. The PWM at 60kHz could be fine but any jitter in the PWM could be an issue. It might be worth while using a high speed PWM IC and run a 600kHz. If you do all of those things, it could perform very well. But of course when pushing the envelope with parts like I suggest you will run into unexpected problems. For example, the gate capacitance of the MAX4600 is very high at over 500pF. In theory it should not matter in this particular case (might even be good) but it is high.
To go back on topic: thanks for the insight!
I'll update the 74HC eventually and I'm also ready to kick out the 40106. Each timer on the ATmega268p is connected to two outputs, which can have different polarities. That way I can get two complementary PWM signals straight from the MCU, and they should be perfectly in sync:
Code:
int pwm = 64;

void setup() {
        TCCR2A = 0b10110011;
        TCCR2B = 0b0000001;
}

void loop() {
                analogWrite(3, pwm);
                analogWrite(11, pwm);  
}

Unfortunately the Arduino's ADC only has a 10KHz sampling rate. This means that I'll now have to turn to the STM32, so I can start working on a simple detector input  :-\ (12bit ADC and I can sample at 96KHz with ease).
For all it's awesome capabilities, it's a whole lot more complex to work with. At worst you should expect my progress to slow down quite a bit, but never say never :D
 
krabbencutter said:
True. And if you need more than 20dB compression you're either doing something wrong, or you need an 1176  ;D

But I guess the -60dB came up, because you could also use PWM for a mixer circuit. But besides the switching speedm clock speed would be another constraint. As PWM resolution decreases drastically the lower you go -40dB already equals 1% Pulse Width. So to get 100KHz switching speed with 16bit PWM resolution would a require a 6,5GHZ clock, if I'm not mistaken.

Indeed for simple compression /limiting you should be able to get away with say 20-30 dB of attenuation.  Fader automation OTOH requires deep kill.

JR
 
krabbencutter said:
Unfortunately the Arduino's ADC only has a 10KHz sampling rate.
You don't need it to be that fast. Because of aliasing effects, you can get a very accurate level without getting even to 20kHz.

But you can get sample rates quite a bit higher than that by changing the prescaler value. For a 16MHz chip the default prescaler is 128 which equates to an actual sample rate of around 9.6kHz. But you can set the prescaler to as low as 16 which equates to a sample rate of 77kHz. However, because there is other stuff going on, you will never reach that speed. The speed depends on how quickly you can dereference the ADC register in your loop. In practice, with a decent amount of other stuff going on, I have found I can get a sample rate of ~14kHz which is just fine for getting an audio level.

The Atmel chips on an Arduino are legit mirocontollers. But to get the most out of them you'll have to identify the specific Atmel chip, find the datasheet for it and get closer to the metal. A lot of the Arduino libraries are a little too simple. My code for reading an ADC on Arduino is much more direct. You may or may not want to setup registers or do things in this particular way depending on your specific Atmel chip. This also ruins portability of course. This is for an Arduino Micro.

Code:
#define ADCMUX_PS_32 0b101
#define ADCMUX_PRESCALER ADCMUX_PS_32

const uint8_t C_ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIF) | ADCMUX_PRESCALER;
const uint8_t C_ADMUX = (1 << REFS0);

init()
{
    ADCSRB = ADHSM | 0x00; 
    ADMUX = C_ADMUX;
    ADCSRA = C_ADCSRA | (1 << ADSC);

    // setup ADC pins ... or not
    // these are chip pin numbers not Arduino labels
    DIDR0 = (1 << lopin);
    DIDR2 = (1 << (hipin - 8));
}

loop()
{
    // do stuff

    // check if sample ready
    while (!(ADCSRA & (1 << ADIF)));

    uint32_t aval;

    ADCSRA = C_ADCSRA;
    aval = ADCL & 0xff; 
    aval |= (ADCH & 0x3) << 8;

    // factor aval into your running value 
}

WARNING:  This code is just condensed fragments from file so it's definitely not going to run correctly unless you study the datasheet and compensate accordingly.

Finally, note that you could add a little circuitry to your ADC input to help a lot. You need to bias the ADC input anyway. But again, 14kHz is pretty good. You don't need it. I just use two 22K resistors to make the bias voltage and then a 68K series into that to also attenuate (adjust depending on how much you want to attenuate) and then a cap to block the bias from what it's connected to. But you could add a simple transistor rectifier and smoothing capacitor. It doesn't need to be a precision rectifier since you can compensate for in code.
 
JohnRoberts said:
Indeed for simple compression /limiting you should be able to get away with say 20-30 dB of attenuation.  Fader automation OTOH requires deep kill.
Note that the 60dB limit is theorized based on the switching speed. But one could switch the PWM to completely 0 in which case you go from that limit to something much deeper which could certainly qualify as "deep kill". That's sort of how a fader works anyway.

Note that analog gates are just P and N channel mosfets in parallel with one high and one low so that they're either both on (switch open) or both off (switch closed) and there are dual complementary P+N mosfet packages that are high voltage have low Ron and some can get into ~30ns rise + fall time range. I have to wonder if making a descrete switch is possible / useful here.
 
Some progress on the STM32 front:
- I'm sampling the ADC at 192k (4x Oversampling), therefore I might get by with a simple RC anti-aliasing filter
- passing the signal through the MCU I get a round trip latency of 30uS  8) (screenshot attached, please ignore the 50Hz ground loop)
- after jumping through some* hoops, I can now access to the DSP instructions for my board

So here's what's next:
- I need to use some averaging method, to get a stable peak value. The easiest way would be to add 4 samples and divide the value by 4. As the ADC is storing the 12bit right-aligned samples in a 16bit format, I should not run into any overflowing issues during the addition. Using DSP instructions I might be able to keep the number of required processing cycles low
- after averaging the samples, I'll convert the averaged peak value into a 32bit floating point number, so I can pass it on to whatever DSP function I like (like enabling side-chain filtering in the future)
- if that processed 32bit float exceeds the compressoion threshold, the PWM will be adjusted according to the compression ratio on the next cycle
- to keep things simple I'll start with some fixed compression & time constants. The envelope will be a simple hold value without retriggering

I'll keep you posted :)

* because vendor tools mostly suck I spent the whole weekend getting my IDE to work and when I had abandoned all hope of any success I codenamed my project "tristessor". The only thing that kept me sane was this video https://youtu.be/c9Xt6Me3mJ4?t=808
 

Attachments

  • 2019-06-22.png
    2019-06-22.png
    891.7 KB · Views: 11
krabbencutter said:
I need to use some averaging method, to get a stable peak value. The easiest way would be to add 4 samples and divide the value by 4.
I wonder why you have to do that; the time-constant is supposed to take care of that.

- to keep things simple I'll start with some fixed compression & time constants. The envelope will be a simple hold value without retriggering
Again, why do you need a hold function? This should be integral to the TC algorithm.
 
Don't know if we got some kind of misunderstanding, but what do you mean with time constant? I was referring to the values of my not-yet-existing compression algorithm. And to keep things simple I thought about starting with a simple "if this than that" algorithm:

if (input peak) > (threshold value) multiply (pwm duty cycle) by (1/x) over the duration of (n * timebase)

This will of course sound like crap. But it will give me enough room for debugging the ADC side of things.
 
I do not want to appear like I am encouraging this pursuit, but I have done some work in digital coding to execute equivalent to one pole RC attack and release time constants (it is even relatively simple to apply different attack and release time constants).

Some microprocessors have advanced instruction sets (like multiply-accumulate) to make this operation more processing efficient. This running accumulation has the effect of smoothing (just like a LPF).

JR
 
I'm sorry but I'm not getting your point.
What are you not encouraging?
What time constants are you & squarewave talking about? And why?

(don't mean to offend, I'm honestly confused right now ^.^)
 
Time constants are referring to the attack and release times of your compressor. In a conventional compressor circuit, the sidechain is amplified, rectified and then filtered with simple RCR filter. The speed of your RC defines the attack. The the speed of the CR defines the release time. If all of that is done in software then yes, time constants don't matter right now.

But note again, that you don't need high performance DSP to do this at all. It's way overkill. The signal is not being digitally processed. You're just processing the sidechain which is a relatively slow affair. It might not be quite as fast as a FET compressor that's so fast its wave-shaping. But that's only because you need to process a few samples and adjust the PWM and that delay isn't really going to change much regardless of what your digital tool chain is. In fact, adding DSP processing and extraneous stuff will probably just add to the delay. A simple PIC or Arduino should be able to react within 500us and probably less if the code is right.

The way I would do this is just bias the input and read samples like I described above and then do something like this:

Code:
#define DMATE 16

uint32_t dmate = 0;
uint32_t bq0 = 0;
uint32_t bq1 = 0;
uint32_t bq2 = 0;

uint32_t rval = 0; // running value

add_sample(struct context *ctx,  uint32_t aval)
{
    // rectify 
    if (aval >= 511) {
        aval -= 511; 
    } else {
        aval = 511 - aval; 
    }

    // scale up to make integer math more accurate
    aval *= AMULT;

    // decimation
    dmate = dmate * (DMATE - 1) + aval / DMATE;

    // biquad filter
    bq0 = bq1;
    bq1 = bq2;
    bq2 = (DMATE - 93 * bq0 + 218 * bq1) / 128; // beware can go neg

    rval = (bq0 + bq2) + 2 * bq1;
}

NOTES: I just typed this in "freestyle" so adjust as necessary. Bi-quad filter values are non-sense. You need to compute that somehow (using an online calculator for example).

That's it. You don't need DSP or lot's of speed for this. This will give you an accurate and relatively fast level well above 20kHz even if the samples are only being read at 10kHz. And this is relatively fancy with a 2nd order bi-quad LP filter. Technically you could get by without that. But I would use it because you could adjust the LP corner and add a HP to filter the sidechain which is very useful. Now you can implement time constants you want in code.
 
Thanks for the code & explanations!

Talking about DSP, I meant DSP in general and regardless of complexity.
But on top of that, the Cortex M4 has a dedicated floating point unit, as well as support for a set of SIMD, MAC and DSP instructions. This should  speed up even a simple biquad. I might not beat the theoretical 20uS of an 1176, but I'm curious to see, where I'll end up.
 
Back
Top