cog/Frameworks/OpenMPT.old/OpenMPT/soundbase/Dither.h

331 lines
8.3 KiB
C++

/*
* Dither.h
* --------
* Purpose: Dithering when converting to lower resolution sample formats.
* Notes : (currently none)
* Authors: Olivier Lapicque
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "BuildSettings.h"
#include "SampleTypes.h"
#include "SampleFormatConverters.h"
#include "../common/mptRandom.h"
OPENMPT_NAMESPACE_BEGIN
enum DitherMode
{
DitherNone = 0,
DitherDefault = 1, // chosen by OpenMPT code, might change
DitherModPlug = 2, // rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker)
DitherSimple = 3, // rectangular, 1 bit depth, simple 1st order noise shaping
NumDitherModes
};
struct Dither_None
{
public:
template <uint32 targetbits, typename Trng>
MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &)
{
return sample;
}
template <uint32 targetbits, typename Trng>
MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &)
{
return sample;
}
};
struct Dither_ModPlug
{
public:
template <uint32 targetbits, typename Trng>
MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &rng)
{
if constexpr(targetbits == 0)
{
MPT_UNREFERENCED_PARAMETER(rng);
return sample;
} else if constexpr(targetbits + MixSampleIntTraits::mix_headroom_bits() + 1 >= 32)
{
MPT_UNREFERENCED_PARAMETER(rng);
return sample;
} else
{
sample += mpt::rshift_signed(static_cast<int32>(mpt::random<uint32>(rng)), (targetbits + MixSampleIntTraits::mix_headroom_bits() + 1));
return sample;
}
}
template <uint32 targetbits, typename Trng>
MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng)
{
SC::ConvertToFixedPoint<MixSampleInt, MixSampleFloat, MixSampleIntTraits::mix_fractional_bits()> conv1;
SC::ConvertFixedPoint<MixSampleFloat, MixSampleInt, MixSampleIntTraits::mix_fractional_bits()> conv2;
return conv2(process<targetbits>(conv1(sample), prng));
}
};
template<int ditherdepth = 1, bool triangular = false, bool shaped = true>
struct Dither_SimpleImpl
{
private:
int32 error = 0;
public:
template <uint32 targetbits, typename Trng>
MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &prng)
{
if constexpr(targetbits == 0)
{
MPT_UNREFERENCED_PARAMETER(prng);
return sample;
} else
{
static_assert(sizeof(MixSampleInt) == 4);
constexpr int rshift = (32-targetbits) - MixSampleIntTraits::mix_headroom_bits();
if constexpr(rshift <= 1)
{
MPT_UNREFERENCED_PARAMETER(prng);
// nothing to dither
return sample;
} else
{
constexpr int rshiftpositive = (rshift > 1) ? rshift : 1; // work-around warnings about negative shift with C++14 compilers
constexpr int round_mask = ~((1<<rshiftpositive)-1);
constexpr int round_offset = 1<<(rshiftpositive-1);
constexpr int noise_bits = rshiftpositive + (ditherdepth - 1);
constexpr int noise_bias = (1<<(noise_bits-1));
int32 e = error;
unsigned int unoise = 0;
if constexpr(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; // un-bias
int val = sample;
if constexpr(shaped)
{
val += (e >> 1);
}
int rounded = (val + noise + round_offset) & round_mask;;
e = val - rounded;
sample = rounded;
error = e;
return sample;
}
}
}
template <uint32 targetbits, typename Trng>
MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng)
{
SC::ConvertToFixedPoint<MixSampleInt, MixSampleFloat, MixSampleIntTraits::mix_fractional_bits()> conv1;
SC::ConvertFixedPoint<MixSampleFloat, MixSampleInt, MixSampleIntTraits::mix_fractional_bits()> conv2;
return conv2(process<targetbits>(conv1(sample), prng));
}
};
using Dither_Simple = Dither_SimpleImpl<>;
template <typename Tdither, std::size_t channels>
class MultiChannelDither
{
private:
std::array<Tdither, channels> DitherChannels;
public:
void Reset()
{
DitherChannels.fill(Tdither());
}
template <uint32 targetbits, typename Trng>
MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample, Trng &prng)
{
return DitherChannels[channel].template process<targetbits>(sample, prng);
}
template <uint32 targetbits, typename Trng>
MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample, Trng &prng)
{
return DitherChannels[channel].template process<targetbits>(sample, prng);
}
};
template <typename Tdither, std::size_t channels>
class DitherTemplate;
template <std::size_t channels>
class DitherTemplate<Dither_None, channels>
: public MultiChannelDither<Dither_None, channels>
{
struct {} prng;
public:
template <typename Trd>
DitherTemplate(Trd &)
{
return;
}
template <uint32 targetbits>
MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample)
{
return MultiChannelDither<Dither_None, channels>::template process<targetbits>(channel, sample, prng);
}
template <uint32 targetbits>
MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample)
{
return MultiChannelDither<Dither_None, channels>::template process<targetbits>(channel, sample, prng);
}
};
template <std::size_t channels>
class DitherTemplate<Dither_ModPlug, channels>
: public MultiChannelDither<Dither_ModPlug, channels>
{
private:
mpt::rng::modplug_dither prng;
public:
template <typename Trd>
DitherTemplate(Trd &)
: prng(0, 0)
{
return;
}
template <uint32 targetbits>
MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample)
{
return MultiChannelDither<Dither_ModPlug, channels>::template process<targetbits>(channel, sample, prng);
}
template <uint32 targetbits>
MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample)
{
return MultiChannelDither<Dither_ModPlug, channels>::template process<targetbits>(channel, sample, prng);
}
};
template <std::size_t channels>
class DitherTemplate<Dither_Simple, channels>
: public MultiChannelDither<Dither_Simple, channels>
{
private:
mpt::fast_prng prng;
public:
template <typename Trd>
DitherTemplate(Trd & rd)
: prng(mpt::make_prng<mpt::fast_prng>(rd))
{
return;
}
template <uint32 targetbits>
MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample)
{
return MultiChannelDither<Dither_Simple, channels>::template process<targetbits>(channel, sample, prng);
}
template <uint32 targetbits>
MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample)
{
return MultiChannelDither<Dither_Simple, channels>::template process<targetbits>(channel, sample, prng);
}
};
class DitherNames
{
public:
static mpt::ustring GetModeName(DitherMode mode);
};
template <std::size_t channels>
class DitherChannels
: public DitherNames
{
private:
DitherTemplate<Dither_None, channels> ditherNone;
DitherTemplate<Dither_ModPlug, channels> ditherModPlug;
DitherTemplate<Dither_Simple, channels> ditherSimple;
DitherMode mode = DitherDefault;
public:
template <typename Trd>
DitherChannels(Trd & rd)
: ditherNone(rd)
, ditherModPlug(rd)
, ditherSimple(rd)
{
return;
}
void Reset()
{
ditherModPlug.Reset();
ditherSimple.Reset();
}
DitherTemplate<Dither_None, channels> & NoDither()
{
MPT_ASSERT(mode == DitherNone);
return ditherNone;
}
DitherTemplate<Dither_ModPlug, channels> & DefaultDither()
{
MPT_ASSERT(mode == DitherDefault);
return ditherModPlug;
}
DitherTemplate<Dither_ModPlug, channels> & ModPlugDither()
{
MPT_ASSERT(mode == DitherModPlug);
return ditherModPlug;
}
DitherTemplate<Dither_Simple, channels> & SimpleDither()
{
MPT_ASSERT(mode == DitherSimple);
return ditherSimple;
}
template <typename Tfn>
auto WithDither(Tfn fn)
{
switch(GetMode())
{
case DitherNone: return fn(NoDither()); break;
case DitherModPlug: return fn(ModPlugDither()); break;
case DitherSimple: return fn(SimpleDither()); break;
case DitherDefault: return fn(DefaultDither()); break;
default: return fn(DefaultDither()); break;
}
}
void SetMode(DitherMode mode_)
{
mode = mode_;
}
DitherMode GetMode() const
{
return mode;
}
};
using Dither = DitherChannels<4>;
OPENMPT_NAMESPACE_END