What does signed_saturate_rshift(val, 16, 0); do?

Status
Not open for further replies.

Mike5000

Well-known member
What does the signed_saturate_rshift(val, 16, 0); ] in the mixer class do?
I assume the val variable cannot overflow so it is an overflow preventer... but what does the other 2 parameters (..,16,0) specify ?


More complete code:
Code:
static void applyGain(int16_t *data, int32_t mult)
{
	const int16_t *end = data + AUDIO_BLOCK_SAMPLES;

	do {
		int32_t val = *data * mult;
		*data++ = signed_saturate_rshift(val, 16, 0);
	} while (data < end);
}

The above is from @paul s mixer class https://github.com/PaulStoffregen/Audio/blob/master/mixer.cpp
 
What does the signed_saturate_rshift(val, 16, 0); ] in the mixer class do?
I assume the val variable cannot overflow so it is an overflow preventer... but what does the other 2 parameters (..,16,0) specify ?

More or less from audio/utility/dspinst.h:
Code:
int32_t signed_saturate_rshift(int32_t val, int bits, int rshift)
{
    int32_t out;
    asm volatile("ssat %0, %1, %2, asr %3" : "=r" (out) : "I" (bits), "r" (val), "I" (rshift));
    return out; 
}
That means, it translates directly to the assembler instruction SSAT (<- read there) , where "bits" specifies the bit position to saturate to, in the range 1 to 32 and rshift specifies the optional shift before saturation (to the right, in this case).
The saturation is - simplified - like "if (x >y) x= y" where y is a power of 2 (bits) -1 ( and taking into account the sign )

(Not sure if "volatile" is really needed here)
 
Last edited:
@Frank B So we basically divide by 2^rshift first to scale down.
Then if the scaled down result is above 2^bits then the result returned is no larger than 2^bits ?
 
Then if the scaled down result is above 2^bits then the result returned is no larger than 2^bits ?

Yes, but done as a signed integer. So "above 2^bits" means both greater than positive 2^(n-1)-1 and less than negative -(2^(n-1)). Both "larger than 2^bits" cases are proper limited to those largest most positive and lowest most negative values.

It all happens with a just 1 instruction that executes in a single cycle. It's done without changing program flow for the condition tests, which would normally cause a 2-cycle pipeline refill in the case of a branch taken. It's done without using any extra registers for the intermediate computation, allowing the compiler's register allocater to more effectively optimize all the surrounding code.
 
Status
Not open for further replies.
Back
Top