#include "stdafx.h"
#include "Dither.h"
#include "Mixer.h"
#include "../common/misc_util.h"
OPENMPT_NAMESPACE_BEGIN
mpt::ustring Dither::GetModeName(DitherMode mode)
{
switch(mode)
{
case DitherNone : return U_("no" ); break;
case DitherDefault: return U_("default"); break;
case DitherModPlug: return U_("0.5 bit"); break;
case DitherSimple : return U_("1 bit" ); break;
default : return U_("" ); break;
}
}
#if MPT_COMPILER_MSVC
#pragma warning(disable:4731)
#endif
#ifdef ENABLE_X86
void X86_Dither(int32 *pBuffer, uint32 nSamples, uint32 nBits, DitherModPlugState *state)
{
if(nBits + MIXING_ATTENUATION + 1 >= 32) {
return;
}
static int gDitherA_global, gDitherB_global;
int gDitherA = state ? state->rng_a : gDitherA_global;
int gDitherB = state ? state->rng_b : gDitherB_global;
_asm {
mov esi, pBuffer mov eax, nSamples mov ecx, nBits mov edi, gDitherA mov ebx, gDitherB
add ecx, MIXING_ATTENUATION+1
push ebp
mov ebp, eax
noiseloop:
rol edi, 1
mov eax, dword ptr [esi]
xor edi, 0x10204080
add esi, 4
lea edi, [ebx*4+edi+0x78649E7D]
mov edx, edi
rol edx, 16
lea edx, [edx*4+edx]
add ebx, edx
mov edx, ebx
sar edx, cl
add eax, edx
dec ebp
mov dword ptr [esi-4], eax
jnz noiseloop
pop ebp
mov gDitherA, edi
mov gDitherB, ebx
}
if(state) state->rng_a = gDitherA; else gDitherA_global = gDitherA;
if(state) state->rng_b = gDitherB; else gDitherB_global = gDitherB;
}
#endif
static MPT_FORCEINLINE int32 dither_rand(uint32 &a, uint32 &b)
{
a = (a << 1) | (a >> 31);
a ^= 0x10204080u;
a += 0x78649E7Du + (b * 4);
b += ((a << 16 ) | (a >> 16)) * 5;
return static_cast<int32>(b);
}
static void C_Dither(int32 *pBuffer, std::size_t count, uint32 nBits, DitherModPlugState *state)
{
if(nBits + MIXING_ATTENUATION + 1 >= 32) {
return;
}
static uint32 global_a = 0;
static uint32 global_b = 0;
uint32 a = state ? state->rng_a : global_a;
uint32 b = state ? state->rng_b : global_b;
while(count--)
{
*pBuffer += dither_rand(a, b) >> (nBits + MIXING_ATTENUATION + 1);
pBuffer++;
}
if(state) state->rng_a = a; else global_a = a;
if(state) state->rng_b = b; else global_b = b;
}
static void Dither_ModPlug(int32 *pBuffer, std::size_t count, std::size_t channels, uint32 nBits, DitherModPlugState &state)
{
#ifdef ENABLE_X86
X86_Dither(pBuffer, count * channels, nBits, &state);
#else
C_Dither(pBuffer, count * channels, nBits, &state);
#endif }
template<int targetbits, int channels, int ditherdepth = 1, bool triangular = false, bool shaped = true>
struct Dither_SimpleTemplate
{
MPT_NOINLINE void operator () (int32 *mixbuffer, std::size_t count, DitherSimpleState &state, mpt::fast_prng &prng)
{
STATIC_ASSERT(sizeof(int) == 4);
const int rshift = (32-targetbits) - MIXING_ATTENUATION;
MPT_CONSTANT_IF(rshift <= 0)
{
return;
}
const int round_mask = ~((1<<rshift)-1);
const int round_offset = 1<<(rshift-1);
const int noise_bits = rshift + (ditherdepth - 1);
const int noise_bias = (1<<(noise_bits-1));
DitherSimpleState s = state;
for(std::size_t i = 0; i < count; ++i)
{
for(std::size_t channel = 0; channel < channels; ++channel)
{
unsigned int unoise = 0;
MPT_CONSTANT_IF(triangular)
{
unoise = (mpt::random<unsigned int>(prng, noise_bits) + mpt::random<unsigned int>(prng, noise_bits)) >> 1;
} else
{
unoise = mpt::random<unsigned int>(prng, noise_bits);
}
int noise = static_cast<int>(unoise) - noise_bias; int val = *mixbuffer;
MPT_CONSTANT_IF(shaped)
{
val += (s.error[channel] >> 1);
}
int rounded = (val + noise + round_offset) & round_mask;;
s.error[channel] = val - rounded;
*mixbuffer = rounded;
mixbuffer++;
}
}
state = s;
}
};
static void Dither_Simple(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits, DitherSimpleState &state, mpt::fast_prng &prng)
{
switch(bits)
{
case 8:
switch(channels)
{
case 1:
Dither_SimpleTemplate<8,1>()(mixbuffer, count, state, prng);
break;
case 2:
Dither_SimpleTemplate<8,2>()(mixbuffer, count, state, prng);
break;
case 4:
Dither_SimpleTemplate<8,4>()(mixbuffer, count, state, prng);
break;
}
break;
case 16:
switch(channels)
{
case 1:
Dither_SimpleTemplate<16,1>()(mixbuffer, count, state, prng);
break;
case 2:
Dither_SimpleTemplate<16,2>()(mixbuffer, count, state, prng);
break;
case 4:
Dither_SimpleTemplate<16,4>()(mixbuffer, count, state, prng);
break;
}
break;
case 24:
switch(channels)
{
case 1:
Dither_SimpleTemplate<24,1>()(mixbuffer, count, state, prng);
break;
case 2:
Dither_SimpleTemplate<24,2>()(mixbuffer, count, state, prng);
break;
case 4:
Dither_SimpleTemplate<24,4>()(mixbuffer, count, state, prng);
break;
}
break;
}
}
void Dither::Reset()
{
state.Reset();
}
void Dither::SetMode(DitherMode mode_)
{
mode = mode_;
}
DitherMode Dither::GetMode() const
{
return mode;
}
void Dither::Process(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits)
{
switch(mode)
{
case DitherNone:
break;
case DitherModPlug:
Dither_ModPlug(mixbuffer, count, channels, bits, state.modplug);
break;
case DitherSimple:
Dither_Simple(mixbuffer, count, channels, bits, state.simple, state.prng);
break;
case DitherDefault:
default:
Dither_ModPlug(mixbuffer, count, channels, bits, state.modplug);
break;
}
}
OPENMPT_NAMESPACE_END