Main Archive Specials Wiki | FAQ Links Submit Forum


Branchless Clipping

Type : Clipping at 0dB, with none of the usual 'if..then..'
References : Posted by musicdsp[AT]dsparsons[DOT]co[DOT]uk

Notes :
I was working on something that I wanted to ensure that the signal never went above 0dB, and a branchless solution occurred to me.

It works by playing with the structure of a single type, shifting the sign bit down to make a new mulitplicand.

calling MaxZerodB(mydBSample) will ensure that it will never stray over 0dB.
By playing with signs or adding/removing offsets, this offers a complete branchless limiting solution, no matter whether dB or not (after all, they're all just numbers...).

eg:
Limit to <=0 : sample:=MaxZerodB(sample);
Limit to <=3 : sample:=MaxZerodB(sample-3)+3;
Limit to <=-4 : sample:=MaxZerodB(sample+4)-4;

Limit to >=0 : sample:=-MaxZerodB(-sample);
Limit to >=2 : sample:=-MaxZerodB(-sample+2)+2;
Limit to >=-1.5: sample:=-MaxZerodB(-sample-1.5)-1.5;

Whether it actually saves any CPU cycles remains to be seen, but it was an interesting diversion for half an hour :)

[Translating from pascal to other languages shouldn't be too hard, and for doubles, you'll need to fiddle it abit :)]


Code :
function MaxZerodB(dBin:single):single;
var tmp:longint;
begin
//given that leftmost bit of a longint indicates the negative,
// if we shift that down to bit0, and multiply dBin by that
// it will return dBin, or zero :)
tmp:=(longint((@dBin)^) and $80000000) shr 31;
result:=dBin*tmp;
end;



Comments


Added on : 29/11/05 by hotpop[ DOT ]com[ AT ]blargg
Comment :
Since most processors include a sign-preserving right shift, you can right shift by 31 to end up with either -1 (all bits set) or 0, then mask the original value with it:

out = (in >> 31) & in;




Added on : 01/12/05 by mumart[ AT ]gmail[ DOT ]com
Comment :
I prefer this method, using a sign-preserving shift, as it can clip a signal to arbitrary bounds:

over = upper_limit - samp
mask = over >> 31
over = over & mask
samp = samp + over
over = samp - lower_limit
mask = over >> 31
over = over & mask
samp = samp - over

Is it faster? Maybe on modern machines with 20-plus-stage pipelines and if the signal is clipped often, as the branches are not predictable.





Added on : 22/03/06 by musicdsp[AT]dsparsons[DOT]co[DOT]uk
Comment :
hmm.. Did some looking into the sign preserving thing. My laptop has an P3 which didn't preserve as mentioned, and my work PC (P4HT) didn't either. Maybe its an AMD or motorola thing :)

unless it's how delphi interprets the shr.. what does a C++ compiler generate for '>>' ?




Added on : 28/03/06 by mumart[ AT ]gmail[ DOT ]com
Comment :
C and C++ have sign-preserving shifts. If the value is negative, a right shift will add ones onto the left hand side (thus -2 becomes -1 etc).

Java also has a non-sign-preserving right shift operator (>>>).

I tried googling for information on how Delphi handles shifts, but nothing turned up. Looks like you might need to use in-line assembly :/




Added on : 29/03/06 by bero[ AT ]0ok[ DOT ]de
Comment :
Here my SAR function for Delphi+FreePascal

FUNCTION SAR(Value,Shift:INTEGER):INTEGER; {$IFDEF CPU386}ASSEMBLER; REGISTER;{$ELSE}{$IFDEF FPC}INLINE;{$ELSE}REGISTER;{$ENDIF}{$ENDIF}
{$IFDEF CPU386}
ASM
MOV ECX,EDX
SAR EAX,CL
END;
{$ELSE}
BEGIN
RESULT:=(Value SHR Shift) OR (($FFFFFFFF+(1-((Value AND (1 SHL 31)) SHR 31) AND ORD(Shift<>0))) SHL (32-Shift));
END;
{$ENDIF}




Added on : 29/03/06 by bero[ AT ]0ok[ DOT ]de
Comment :
Ny branchless clipping functions (the first is faster than the second)

FUNCTION Clip(Value,Min,Max:SINGLE):SINGLE; ASSEMBLER; STDCALL;
CONST Constant0Dot5:SINGLE=0.5;
ASM
FLD DWORD PTR Value
FLD DWORD PTR Min
FLD DWORD PTR Max

FLD ST(2)
FSUB ST(0),ST(2)
FABS
FADD ST(0),ST(2)
FADD ST(0),ST(1)

FLD ST(3)
FSUB ST(0),ST(2)
FABS
FSUBP ST(1),ST(0)
FMUL DWORD PTR Constant0Dot5

FFREE ST(4)
FFREE ST(3)
FFREE ST(2)
FFREE ST(1)
END;

FUNCTION ClipDSP(Value:SINGLE):SINGLE; {$IFDEF CPU386} ASSEMBLER; REGISTER;
ASM
MOV EAX,DWORD PTR Value
AND EAX,$80000000

AND DWORD PTR Value,$7FFFFFFF

FLD DWORD PTR Value
FLD1
FSUBP ST(1),ST(0)
FSTP DWORD PTR Value

MOV EDX,DWORD PTR Value
AND EDX,$80000000
SHR EDX,31
NEG EDX
AND DWORD PTR Value,EDX

FLD DWORD PTR Value
FLD1
FADDP ST(1),ST(0)
FSTP DWORD PTR Value

OR DWORD PTR Value,EAX

FLD DWORD PTR Value
END;
{$ELSE}
VAR ValueCasted:LONGWORD ABSOLUTE Value;
    Sign:LONGWORD;
BEGIN
Sign:=ValueCasted AND $80000000;
ValueCasted:=ValueCasted AND $7FFFFFFF;
Value:=Value-1;
ValueCasted:=ValueCasted AND (-LONGWORD((ValueCasted AND $80000000) SHR 31));
Value:=Value+1;
ValueCasted:=ValueCasted OR Sign;
RESULT:=Value;
END;
{$ENDIF}

              




Add your own comment
Comments are displayed in fixed width, no HTML code allowed!
Email:

Comment:

Are you human?



Site created and maintained by Bram
Graphic design by line.out | Server sponsered by fxpansion