Saturating arithmetic

- All,

I understand the Cortex M3 core has some saturated arithmetic instructions.

Is it possible to get GCC to use them, to avoid having to use constructions like this:Code:

c=a-b;

if (c<0) c=0;

I'm going to be playing two audio streams simultaneously, and when I combine the source data I don't want any overflow. And it needs to be fast.

Any comments?

Thanks,

Chris. - Ta for comments so far.

It seems there isn't an easy way...

I could do c=a+b/2, but then as you say, a single channel would be 1/2 amplitude.

I think I'll just use 32bits for storing the 16bit results, then I haven't lost any accuracy, and can then use compares to limit the range. Probably slow, but it ought to work OK......

Chris. - Think this way: 0V =0x80, min (i.e. negative) volts = 0x00, max volts = 0xFF. Then c=(a-b)/2. Do the same trick with a+b. DC offset out of your DAC goes away with one capacitor. Thinking this way is better than saturating arithmetic. (Which I believe Cortex-M3 has but I am not looking at my ref material at the moment.)

Does anybody have any idea how to combine (conference call style) 3 to perhaps 10 digital channels? I’m thinking (a+b+c+d+e)/5 would work but with only one speaker the levels would be lower than desired. Quote:

On 26-03-2009 at 13:55, Anonymous wrote:

All,

I understand the Cortex M3 core has some saturated arithmetic instructions.

Is it possible to get GCC to use them, to avoid having to use constructions like this:Code:

c=a-b;

if (c<0) c=0;

I'm going to be playing two audio streams simultaneously, and when I combine the source data I don't want any overflow. And it needs to be fast.

Any comments?

Thanks,

Chris.

No cortex doesn't really support saturation logic, a real DSP with saturation logic usually has a extended range intermediate register (40 bits or so) so that intermediate calculations don't saturate, just the final result, you can turn it on or off with a bit so it's done automatically.

The cortex M3 has SSAT and USAT instructions that are capable of converting 32 bits values to smaller sizes with saturation, a sort of poor man's saturation logic. Cortex's bigger brothers, like ARM926, has the real thing, as does most TI DSP's and even microchip's dsPIC, I think.- My 0x00 to0x80 to 0xFF might have been better stated with 3 hex digits to match the STM32 12-bit ADC and DAC.

But Chris, range limiting like saturating math clips waveforms. Think of two nice sine waves 400Hz and 410Hz. (It will sound bad to the ear.) Each goes +/- 1 volt. Out of phase you get a dozen milliseconds with very low amplitude. In phase you get +/- 2 volts. Clip the in phase signal and the dissonance will sound much worse.

Here is something I have been thinking about but have not implemented because the guy with the money had much of in AIG. Form c=a+b then map 0x0000 to 0x1FFF into 0x0800 to 0x17FF. Gradual range compression. Zero to small changes near 0x1000 with larger changes closer to the endpoints. (Table lookup & interpolation would work but I would recommend math. Something like sqrt might work. Cortex @72MHz is good at simple integer math.) Then subtract 0x0800 to get 0x000 to FFF. The distortion this makes may be small enough to sound much like the original 400Hz + 410Hz.

A major cord such as CEG and next C has frequency ratios 4:5:6:8. Try a 4:5 ratio if you test this with your ear. A above middle C is 440Hz The C# above that is 550Hz.

Unrelated but possibly useful is my FIFO page: http://www.hmtown.com/fifo.htm Dig around a bit and you may be able to let me know if my thoughts are useful. - My simple saturation code
Code:

if (samples[0]<DAC_MIN) samples[0]=DAC_MIN;

if (samples[0]>DAC_MAX) samples[0]=DAC_MAX;

dac_buffer[0]=(s16)samples[0];

where samples[] is 32bit signed and dac_buffer[] is 16bit signed.

I realise that two full-scale signals will sound awful, and it would be better to have a non-linear compression as the signal approaches full scale.

But this is an experimental board, which will never see production in its final form (or anywhere near), so if it sounds awful, then the volume level will have to be turned down on one channel!

Chris. - To my way of thinking you want gradual compression such that you will never get to the point of clipping. I would focus my thinking around 0V +/- signal. Because you are only using two channels the following may work well:

Arithmetically scale your a+b and a-b signal to +/- pi/2. Then take the sine. You now have a number in –1..+1. Scale appropriately and output to your DAC.

I have a fast sin(x) where x is an unsigned 32-bit integer representing 0 to 2pi. Value returned is a signed 32-bit integer = sin(x)*2^24 good to 12 bits. My function is all integer following the infinite series through -x^7/7! It never returns a result >1.0. I only use 0x6C bytes of ROM, a few stack words and no dedicated RAM.

With this scheme low amplitude values are hardly attenuated. High amplitude values are reduced by as much as 50%. And the reduction is gradual.

I would begin by using a floating point library sine function. That will take many more clocks that my fast integer sine. If it sounds okay than write a fast integer sine.

Or... you could simply use (a+b)/2 and (a-b)/2 using an analog amplifier if needed to boost the signal.

That just seems to be about detecting overflow?

In this case, surely, just making c

unsignedwill suffice - won't it? :-?