119 std::size_t length = tables.empty() ? 0 : tables.front().size();
120 for (
const auto& t : tables) {
121 if (t.size() != length)
return false;
126 table_length_ = length;
127 if (table_length_ > 0) {
128 tables_.reserve(tables.size());
129 for (
auto& src : tables) {
130 std::vector<double> table(table_length_, 0.0);
131 for (std::size_t
i = 0;
i < table_length_; ++
i)
132 table[
i] = quantize(src[
i], bit_depth_);
133 tables_.push_back(std::move(table));
136 current_table_ = std::min<int>(current_table_,
138 :
static_cast<int>(tables_.size()) - 1);
152 if (tables_.size() < 2) { current_table_ = 0;
return; }
153 const double clamped = std::clamp(pos, 0.0, 1.0);
154 const double scaled = clamped *
static_cast<double>(tables_.size() - 1);
155 current_table_ =
static_cast<int>(std::lround(scaled));
156 current_table_ = std::clamp(current_table_, 0,
157 static_cast<int>(tables_.size()) - 1);
165 reconstruction_ = enabled;
173 oversample_ = factor > 1 ? factor : 1;
181 std::fill(ring_.begin(), ring_.end(), 0.0);
190 double next(
double increment) {
191 if (tables_.empty() || table_length_ == 0)
return 0.0;
192 const double step = increment *
static_cast<double>(table_length_);
194 if (!reconstruction_active()) {
195 const double s = sample_zoh();
204 const double sub_step = step /
static_cast<double>(oversample_);
206 for (
int m = 0; m < oversample_; ++m) {
207 push_ring(sample_zoh());
208 advance_phase(sub_step);
209 if (m == oversample_ - 1) out = convolve_ring();
224 return i < tables_.size() ? std::span<const double>(tables_[
i])
225 : std::span<const double>();
240 static double quantize(
double x,
int bits) {
241 if (bits <= 0 || bits >= 53)
return x;
242 const double levels = std::ldexp(1.0, bits - 1) - 1.0;
246 bool reconstruction_active()
const {
247 return reconstruction_ && oversample_ > 1 && !coeffs_.empty();
253 double sample_zoh()
const {
254 const auto& table = tables_[
static_cast<std::size_t
>(current_table_)];
255 std::size_t idx =
static_cast<std::size_t
>(table_phase_);
256 if (idx >= table_length_) idx = table_length_ - 1;
260 void advance_phase(
double step) {
261 table_phase_ += step;
262 const double L =
static_cast<double>(table_length_);
263 if (table_phase_ >= L || table_phase_ < 0.0)
264 table_phase_ -= L * std::floor(table_phase_ / L);
265 if (table_phase_ >= L) table_phase_ = 0.0;
268 void push_ring(
double x) {
269 ring_[ring_pos_] =
x;
270 ring_pos_ = ring_pos_ + 1 == ring_.size() ? 0 : ring_pos_ + 1;
273 double convolve_ring()
const {
274 const std::size_t
n = coeffs_.size();
276 std::size_t idx = ring_pos_ == 0 ?
n - 1 : ring_pos_ - 1;
277 for (std::size_t j = 0;
j <
n; ++
j) {
278 acc += coeffs_[
j] * ring_[idx];
279 idx = idx == 0 ?
n - 1 : idx - 1;
293 void rebuild_filter() {
297 if (!reconstruction_ || oversample_ <= 1)
return;
299 const double host_nyquist_norm = 0.5 /
static_cast<double>(oversample_);
300 const double cutoff = host_nyquist_norm;
301 const double transition = 0.20 * host_nyquist_norm;
303 const std::size_t taps =
306 ring_.assign(coeffs_.size(), 0.0);
309 std::vector<std::vector<double>> tables_;
310 std::size_t table_length_ = 0;
312 int current_table_ = 0;
314 double sample_rate_ = 48000.0;
315 double table_phase_ = 0.0;
317 bool reconstruction_ =
true;
319 std::vector<double> coeffs_;
320 std::vector<double> ring_;
321 std::size_t ring_pos_ = 0;
Dedicated variable-clock ZOH wavetable oscillator (the lo-fi tier).
Definition wt_lofi.hpp:90
void set_oversample_factor(int factor)
Set the reconstruction oversample factor (>= 1; 1 disables the stage even when reconstruction is enab...
Definition wt_lofi.hpp:172
static constexpr int kDefaultOversampleFactor
Default oversample factor for the reconstruction stage.
Definition wt_lofi.hpp:103
static constexpr double kReconstructionStopbandDb
Reconstruction-filter stopband depth (dB) handed to the Kaiser design.
Definition wt_lofi.hpp:105
int current_table() const
Definition wt_lofi.hpp:216
void set_reconstruction(bool enabled)
Enable (default) or disable the oversample โ reconstruction โ decimate stage.
Definition wt_lofi.hpp:164
double sample_rate() const
The configured sample rate.
Definition wt_lofi.hpp:231
double next(double increment)
Generate one sample and advance by increment cycles (frequency รท sample rate).
Definition wt_lofi.hpp:190
void reset()
Reset the playback phase to the start of the cycle and clear the reconstruction filter state.
Definition wt_lofi.hpp:179
void prepare(double sample_rate)
Set the sample rate. Safe to call again on a rate change.
Definition wt_lofi.hpp:143
int bit_depth() const
Definition wt_lofi.hpp:217
std::span< const double > stored_table(std::size_t i) const
Read-only view of a stored table โ the actual (post-quantization) sample data the reader plays.
Definition wt_lofi.hpp:223
int oversample_factor() const
Definition wt_lofi.hpp:233
std::size_t table_count() const
Definition wt_lofi.hpp:215
bool reconstruction_enabled() const
Definition wt_lofi.hpp:232
static constexpr int kDefaultBitDepth
Default stored-table resolution.
Definition wt_lofi.hpp:95
std::size_t table_length() const
Definition wt_lofi.hpp:214
void set_scan(double pos)
Select the table nearest scan position pos (0 = first, 1 = last), clamped.
Definition wt_lofi.hpp:151
bool set_tables(std::vector< std::vector< double > > tables, int bit_depth=kDefaultBitDepth)
Install the raw single-cycle table set (off the audio thread), returning whether the set was accepted...
Definition wt_lofi.hpp:117
static constexpr int kFullPrecision
Sentinel bit depth: store the table at full double precision (no quantization).
Definition wt_lofi.hpp:98
@ levels
Definition multi_channel_meter.hpp:48
@ i
Definition formant_filter_bank.hpp:20
std::size_t kaiser_length_for_transition(double stopband_db, double normalized_width)
Definition windowed_sinc_design.hpp:36
std::vector< double > design_windowed_sinc(std::size_t taps, double cutoff, double beta)
Definition windowed_sinc_design.hpp:58
double kaiser_beta_for_stopband(double stopband_db)
Definition windowed_sinc_design.hpp:27
@ j
Definition input_events.hpp:40
@ n
Definition input_events.hpp:40
@ x
Definition input_events.hpp:41
Shared Kaiser-windowed-sinc FIR design helpers.