23 if (bits >= 24.0)
return x;
24 const double levels = std::pow(2.0, std::max(bits, 1.0) - 1.0);
40 const double dz = std::clamp(dead_zone, 0.0, 0.9);
41 const double magnitude = std::fabs(x);
42 if (magnitude <= dz)
return 0.0;
43 const double rescaled = (magnitude - dz) / (1.0 - dz);
44 return std::copysign(std::tanh(1.5 * rescaled), x);
81template <
typename Sample =
double>
83 static_assert(std::is_floating_point_v<Sample>);
84 const Sample limit = std::clamp(threshold, Sample{0}, Sample{0.9});
85 const Sample magnitude = std::fabs(x);
86 if (magnitude <= limit)
88 const Sample normalized = std::min((magnitude - limit) / (Sample{1} - limit), Sample{1});
90 ? normalized * normalized * (Sample{3} - Sample{2} * normalized)
92 return std::copysign(shaped, x);
112template <
typename SampleType =
float>
118 sample_rate_ = sr > 0.0 ? sr : sample_rate_;
128 hold_rate_ = std::max(hz, 1.0);
134 jitter_ = std::clamp(amount, 0.0, 1.0);
143 smoothing_ = std::clamp(amount, 0.0, 1.0);
152 if (sample_rate_ != other.sample_rate_)
154 if (hold_rate_ != other.hold_rate_)
156 if (jitter_ != other.jitter_)
158 if (smoothing_ != other.smoothing_)
172 return (!hold_bypassed_ && std::fabs(held_) > 1.0e-12) ||
173 (!bypass_smoothing_ && output_level_ > 1.0e-8);
182 phase_ -= std::floor(phase_);
183 held_ =
static_cast<double>(input);
185 phase_ += step_ * (1.0 + jitter_ * next_jitter());
186 const SampleType output =
188 ?
static_cast<SampleType
>(held_)
189 : smoother_.process_lowpass(
static_cast<SampleType
>(held_));
190 output_level_ = std::fabs(
static_cast<double>(output));
195 double next_jitter() {
199 return static_cast<double>(rng_) * (1.0 / 2147483648.0) - 1.0;
203 const double rate = std::min(hold_rate_, sample_rate_);
204 step_ = rate / sample_rate_;
205 hold_bypassed_ = rate >= sample_rate_;
206 smoother_.prepare(
static_cast<SampleType
>(sample_rate_));
207 bypass_smoothing_ = smoothing_ <= 0.0;
208 if (!bypass_smoothing_) {
212 const double top = 0.5 * sample_rate_;
213 const double bottom = 0.5 * rate;
214 smoother_.set_cutoff(
static_cast<SampleType
>(top * std::pow(bottom / top, smoothing_)));
218 double sample_rate_ = 44100.0;
219 double hold_rate_ = std::numeric_limits<double>::max();
220 double jitter_ = 0.0;
221 double smoothing_ = 0.0;
226 double output_level_ = 0.0;
227 TptFilterT<SampleType> smoother_;
228 bool hold_bypassed_ =
true;
229 bool bypass_smoothing_ =
true;
250template <
typename SampleType =
float>
257 bits_ = std::isfinite(bits) ? std::clamp(bits, 1.0, 24.0) : 24.0;
258 quantizer_.set_bits(
static_cast<SampleType
>(bits_));
266 quantizer_.set_noise_shaping(order);
273 void set_jitter(
double amount) { reducer_.set_jitter(amount); }
278 void set_dead_zone(
double amount) { dead_zone_ = std::clamp(amount, 0.0, 0.9); }
281 reducer_.set_seed(seed);
282 quantizer_.set_seed(seed);
289 dead_zone_ = other.dead_zone_;
290 reducer_.sync_configuration_from(other.reducer_);
291 quantizer_.sync_configuration_from(other.quantizer_);
298 bool has_tail() const noexcept {
return reducer_.has_tail(); }
308 x =
static_cast<double>(quantizer_.process(input));
310 x =
static_cast<double>(reducer_.process(
static_cast<SampleType
>(x)));
312 return static_cast<SampleType
>(x);
323 double dead_zone_ = 0.0;
Stateful deterministic wrapper for per-sample and block processing.
Definition dither.hpp:131
The three lo-fi stages in the order the hardware they model applies them: quantise the word,...
Definition lofi_chain.hpp:251
void set_dead_zone(double amount)
Fraction of full scale the output stage cannot represent, 0 to 0.9.
Definition lofi_chain.hpp:278
void set_smoothing(double amount)
Definition lofi_chain.hpp:275
void process(SampleType *buffer, int num_samples)
Definition lofi_chain.hpp:315
bool has_tail() const noexcept
Definition lofi_chain.hpp:298
void set_hold_rate_hz(double hz)
Hold clock in Hz.
Definition lofi_chain.hpp:271
void reset()
Definition lofi_chain.hpp:294
void set_noise_shaping(NoiseShapingOrder order)
Error feedback is opt-in and may be used with or without dither.
Definition lofi_chain.hpp:265
void set_seed(std::uint32_t seed)
Definition lofi_chain.hpp:280
SampleType process(SampleType input)
Definition lofi_chain.hpp:300
void set_sample_rate(double sr)
Definition lofi_chain.hpp:253
void sync_configuration_from(const LofiChainT &other)
Mirror parameter configuration while preserving this chain's independent reducer/filter state.
Definition lofi_chain.hpp:287
void set_jitter(double amount)
Definition lofi_chain.hpp:273
void set_bits(double bits)
Word length in bits. 24 or above bypasses the quantiser.
Definition lofi_chain.hpp:256
void set_dither_mode(DitherMode mode)
Quantizer dither is opt-in so existing chains remain bit-exact.
Definition lofi_chain.hpp:262
Sample-and-hold rate reducer with clock jitter and a reconstruction filter.
Definition lofi_chain.hpp:113
void set_jitter(double amount)
Clock jitter as a fraction of the hold period, 0 to 1.
Definition lofi_chain.hpp:133
void reset()
Definition lofi_chain.hpp:163
void set_sample_rate(double sr)
Definition lofi_chain.hpp:117
void sync_configuration_from(const SampleRateReducerT &other)
Mirror another reducer's parameter configuration without copying its held sample, clock phase,...
Definition lofi_chain.hpp:151
SampleType process(SampleType input)
Definition lofi_chain.hpp:176
void set_seed(std::uint32_t seed)
Definition lofi_chain.hpp:147
bool has_tail() const noexcept
Definition lofi_chain.hpp:171
void set_hold_rate_hz(double hz)
Rate the held samples are clocked at, in Hz.
Definition lofi_chain.hpp:127
static constexpr std::uint32_t default_seed
Definition lofi_chain.hpp:115
void set_smoothing(double amount)
Strength of the reconstruction lowpass, 0 (bypassed, raw images) to 1 (corner at the hold rate's Nyqu...
Definition lofi_chain.hpp:142
Deterministic TPDF dither and bounded error-feedback quantization.
Definition additive_bank.hpp:218
LofiChainT< float > LofiChain
Definition lofi_chain.hpp:328
@ buffer
A borrowed static buffer with a traversable playhead.
Definition granular.hpp:110
NoiseShapingOrder
Definition dither.hpp:21
@ none
Definition dither.hpp:22
FloorCurve
The curve applied above a floor threshold by floor_shape.
Definition lofi_chain.hpp:48
@ smoothstep
Smoothstep, which leaves the threshold with zero slope so the onset of audibility is gradual rather t...
Definition lofi_chain.hpp:53
float smoothstep(float p)
Cubic smoothstep, 3t^2 - 2t^3.
Definition mod_tools.hpp:75
SampleRateReducerT< double > SampleRateReducer64
Definition lofi_chain.hpp:327
double dead_zone_saturate(double x, double dead_zone)
A dead-zone saturator: a hard gap around zero, then a soft-limited slope.
Definition lofi_chain.hpp:39
Sample floor_shape(Sample x, std::type_identity_t< Sample > threshold, FloorCurve curve)
A noise-floor shaper: silence below threshold, rescaled above it.
Definition lofi_chain.hpp:82
LofiChainT< double > LofiChain64
Definition lofi_chain.hpp:329
@ levels
Definition multi_channel_meter.hpp:48
@ i
Definition formant_filter_bank.hpp:20
double quantize_bits(double x, double bits)
Quantises to bits of resolution, mid-tread, symmetric about zero.
Definition lofi_chain.hpp:22
DitherMode
Definition dither.hpp:16
@ none
Definition dither.hpp:17
@ linear
2-point. Cheapest; audible imaging when pitching up.
Definition granular.hpp:133
SampleRateReducerT< float > SampleRateReducer
Definition lofi_chain.hpp:326
First-order topology-preserving transform (TPT) filter.