Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
nonlin_ambience_design.hpp
Go to the documentation of this file.
1#pragma once
2
220
225#include <pulp/signal/rng.hpp>
228#include <pulp/signal/units.hpp>
230
231#include <algorithm>
232#include <array>
233#include <cmath>
234#include <cstddef>
235#include <cstdint>
236#include <limits>
237#include <vector>
238
239namespace pulp::signal {
240
243enum class NonlinProgram {
245 gated = 1,
248};
249
254
257inline constexpr int kNumAllpass = 2;
258
262inline constexpr double kAllpassDelaysMs[2] = {4.77, 6.13};
263
266inline constexpr double kDiffusionDefault = 0.70;
267inline constexpr double kDiffusionMax = 0.85;
268
274inline constexpr double kNdMin = 800.0;
275inline constexpr double kNdMax = 4000.0;
276
280inline constexpr double kDensityRefPct = 60.0;
281
286inline constexpr double kGammaDefault = 2.0;
287
290inline constexpr double kAmbDropDb = 60.0;
291
295inline constexpr double kFadeInFrac = 0.02;
296
299inline constexpr double kGateHold = 0.70;
300inline constexpr double kGateFall = 0.05;
301
305inline constexpr double kRevRise = 0.85;
306inline constexpr double kRevPow = 1.0;
307
311inline constexpr int kNlHumps = 2;
312inline constexpr double kNlDepth = 0.4;
313inline constexpr double kNlHold = 0.85;
314
318inline constexpr double kL1Budget = 4.0;
319
323inline constexpr int kSegments = 8;
324
327inline constexpr double kFcBright = 16000.0;
328inline constexpr double kFcDark = 6000.0;
329
332inline constexpr double kSwapFadeMs = 20.0;
333
336inline constexpr double kParamSmoothMs = 20.0;
337
344inline constexpr std::uint32_t kSeedRodd = 0x9E3779B9u;
345
348inline constexpr std::uint32_t kDefaultSeed = 0x51EDu;
349
353inline constexpr int kTapGuard = 32;
354
358inline constexpr double kConverterFcLo = 80.0;
359inline constexpr double kConverterFcHi = 15000.0;
360
364inline constexpr int kConverterBitsMax = 16;
365inline constexpr int kConverterBitSweep = 4;
366
369inline constexpr double kConverterDcPole = 0.995;
370
374inline constexpr double kMinLengthMs = 20.0;
375inline constexpr double kMaxLengthMs = 2000.0;
376inline constexpr double kMaxPredelayMs = 200.0;
377
379inline constexpr double kMinDensityPct = 10.0;
380
385inline constexpr double kNormFloor = 1e-20;
386
389constexpr double allpass_l1_gain(double g) { return 1.0 + 2.0 * (g < 0.0 ? -g : g); }
390
393inline constexpr double kDcBlockerL1Gain = 2.0;
394
400inline double worst_case_gain(double diffusion = kDiffusionDefault,
401 bool converter_on = false) {
402 double g = 1.0;
403 for (int i = 0; i < kNumAllpass; ++i) g *= allpass_l1_gain(diffusion);
404 return g * kL1Budget * (converter_on ? kDcBlockerL1Gain : 1.0);
405}
406
413inline double program_envelope(NonlinProgram program,
414 double tau,
415 double gate_hold = kGateHold,
416 double attack = kRevRise) {
417 if (!(tau >= 0.0) || tau > 1.0) return 0.0;
418
419 const double fade_in = kFadeInFrac > 0.0 ? std::min(1.0, tau / kFadeInFrac) : 1.0;
420
421 switch (program) {
423 // α from the drop in dB: E(1) = 10^(−A_drop/20).
424 constexpr double kLn10 = 2.302585092994045684;
425 const double alpha = kAmbDropDb * kLn10 / 20.0;
426 return fade_in * std::exp(-alpha * tau);
427 }
429 const double h = gate_hold;
430 const double w = kGateFall;
431 if (tau <= h) return 1.0;
432 if (tau <= h + w) return 1.0 - (tau - h) / w;
433 return 0.0;
434 }
436 const double r = attack > 0.0 ? attack : 1e-6;
437 if (tau <= r) return std::pow(tau / r, kRevPow);
438 return 1.0; // brief plateau; the hard cut is at τ > 1, handled above
439 }
441 const double h = attack;
442 if (tau > h) return 0.0;
443 constexpr double kTwoPi = 6.283185307179586476925286766559;
444 const double ripple =
445 0.5 - 0.5 * std::cos(kTwoPi * static_cast<double>(kNlHumps) * tau);
446 return fade_in * ((1.0 - kNlDepth) + kNlDepth * ripple);
447 }
448 }
449 return 0.0;
450}
451
456inline double pulse_density(double u, double density_pct, double gamma) {
457 const double nd_min = kNdMin * (density_pct / kDensityRefPct);
458 const double clamped = std::clamp(u, 0.0, 1.0);
459 return nd_min + (kNdMax - nd_min) * std::pow(clamped, gamma);
460}
461
465 int delay = 0;
466 double magnitude = 0.0;
467 double gain = 0.0;
468 int segment = 0;
469 bool audible = false;
470};
471
473 double position_samples,
474 double grid_samples,
475 int window_samples,
476 int predelay_samples,
477 double gate_hold,
478 double attack,
479 const VelvetNoiseDrawT<double>& draw) noexcept {
480 VelvetTapDesign result;
481 // Public fail-closed domain: a non-empty window, non-negative finite grid
482 // coordinates, normalized jitter, a ternary pulse sign, finite envelope
483 // controls, and a predelay whose largest in-window tap cannot overflow.
484 // Invalid input produces the default inaudible design before any floating
485 // to integer conversion, division, square root, or signed addition.
486 if (window_samples <= 0 || predelay_samples < 0 ||
487 predelay_samples > std::numeric_limits<int>::max() - (window_samples - 1) ||
488 !std::isfinite(position_samples) || position_samples < 0.0 ||
489 !std::isfinite(grid_samples) || grid_samples < 0.0 ||
490 !std::isfinite(draw.jitter) || draw.jitter < 0.0 || draw.jitter >= 1.0 ||
491 (draw.sign != -1 && draw.sign != 1) ||
492 !std::isfinite(gate_hold) || !std::isfinite(attack)) {
493 return result;
494 }
495
496 const double candidate = position_samples +
497 draw.jitter * std::max(0.0, grid_samples - 1.0);
498 if (!std::isfinite(candidate)) return result;
499 const double bounded = std::clamp(candidate, 0.0,
500 static_cast<double>(window_samples - 1));
501 const int delay = static_cast<int>(std::lround(bounded));
502 const double tau = static_cast<double>(delay) / static_cast<double>(window_samples);
503 const double env = program_envelope(program, tau, gate_hold, attack);
504 result.delay = predelay_samples + delay;
505 result.audible = env > 0.0;
506 if (!result.audible) return result;
507 result.magnitude = env * std::sqrt(grid_samples);
508 result.gain = static_cast<double>(draw.sign) * result.magnitude;
509 int segment = std::clamp(static_cast<int>(tau * kSegments), 0, kSegments - 1);
510 result.segment = program == NonlinProgram::reverse ? kSegments - 1 - segment : segment;
511 return result;
512}
513
514} // namespace nonlin_ambience
515
516} // namespace pulp::signal
The one shared old→new crossfade gain law for the SIGNAL side.
The calibration register: every number this module ships that is not cited to the literature lives he...
Definition nonlin_ambience_design.hpp:253
constexpr double kRevRise
Reverse program: normalized rise point r and swell exponent p (p < 1 concave, p > 1 convex).
Definition nonlin_ambience_design.hpp:305
constexpr double kConverterFcHi
Definition nonlin_ambience_design.hpp:359
constexpr double kFcBright
Segment tilt corners.
Definition nonlin_ambience_design.hpp:327
constexpr double kMinDensityPct
Lower bound of the density control's documented 10 .. 100 % range.
Definition nonlin_ambience_design.hpp:379
constexpr double kParamSmoothMs
Smoothing time for the continuous gain parameters.
Definition nonlin_ambience_design.hpp:336
constexpr double kMaxLengthMs
Definition nonlin_ambience_design.hpp:375
constexpr int kTapGuard
Headroom above the expected tap count, so the jittered grid can never outgrow the pre-sized table.
Definition nonlin_ambience_design.hpp:353
constexpr double kFcDark
Definition nonlin_ambience_design.hpp:328
constexpr double kNlHold
Definition nonlin_ambience_design.hpp:313
double worst_case_gain(double diffusion=kDiffusionDefault, bool converter_on=false)
The shipped worst-case gain as a closed form of the shipped constants: Π_i (1 + 2·g_i) · G_L1,...
Definition nonlin_ambience_design.hpp:400
constexpr double kRevPow
Definition nonlin_ambience_design.hpp:306
constexpr double kNormFloor
Floor below which the normalization sum is treated as empty, so a program whose taps all land in a ze...
Definition nonlin_ambience_design.hpp:385
constexpr double kL1Budget
The per-channel L1 budget of the tap-gain vector.
Definition nonlin_ambience_design.hpp:318
constexpr double kAllpassDelaysMs[2]
Allpass delays in ms, rounded to the nearest sample and then nudged to the nearest prime sample count...
Definition nonlin_ambience_design.hpp:262
constexpr double kDensityRefPct
The density_pct value at which Nd_min equals kNdMin exactly, so the shipped default reproduces the sh...
Definition nonlin_ambience_design.hpp:280
constexpr double kGammaDefault
Density growth exponent γ.
Definition nonlin_ambience_design.hpp:286
constexpr double kAmbDropDb
Ambience drop across the window, in dB; sets α = A_drop·ln(10)/20.
Definition nonlin_ambience_design.hpp:290
constexpr double kGateHold
Gated program: normalized hold point h and fall width w.
Definition nonlin_ambience_design.hpp:299
constexpr double allpass_l1_gain(double g)
The time-domain L1 (peak) gain of one Schroeder allpass with coefficient g: Σ|h| = g + (1−g²)/(1−g) =...
Definition nonlin_ambience_design.hpp:389
constexpr double kGateFall
Definition nonlin_ambience_design.hpp:300
constexpr double kMinLengthMs
Largest window and pre-delay prepare() sizes storage for, in ms.
Definition nonlin_ambience_design.hpp:374
constexpr int kConverterBitsMax
Converter word length at converter_amount = 0 and the depth of the sweep: bits = kConverterBitsMax − ...
Definition nonlin_ambience_design.hpp:364
constexpr double kMaxPredelayMs
Definition nonlin_ambience_design.hpp:376
constexpr double kFadeInFrac
Linear rise over the first fraction of the window, which kills the first-tap click on the programs th...
Definition nonlin_ambience_design.hpp:295
constexpr int kConverterBitSweep
Definition nonlin_ambience_design.hpp:365
constexpr std::uint32_t kSeedRodd
seed_R = seed_L ⊕ kSeedRodd.
Definition nonlin_ambience_design.hpp:344
constexpr std::uint32_t kDefaultSeed
Default seed_L.
Definition nonlin_ambience_design.hpp:348
constexpr double kDiffusionMax
Definition nonlin_ambience_design.hpp:267
constexpr double kConverterFcLo
Converter-character pre-filter corners (§5).
Definition nonlin_ambience_design.hpp:358
constexpr double kDcBlockerL1Gain
The time-domain L1 gain of the DC blocker, which is exactly 2 for any pole p < 1: h[0] = 1,...
Definition nonlin_ambience_design.hpp:393
VelvetTapDesign design_velvet_tap(NonlinProgram program, double position_samples, double grid_samples, int window_samples, int predelay_samples, double gate_hold, double attack, const VelvetNoiseDrawT< double > &draw) noexcept
Definition nonlin_ambience_design.hpp:472
double pulse_density(double u, double density_pct, double gamma)
Instantaneous pulse density in pulses/second at normalized time u ∈ [0,1]: Nd(u) = Nd_min + (Nd_max −...
Definition nonlin_ambience_design.hpp:456
constexpr int kNumAllpass
Number of pre-diffusion Schroeder allpasses.
Definition nonlin_ambience_design.hpp:257
constexpr double kNdMax
Definition nonlin_ambience_design.hpp:275
constexpr double kSwapFadeMs
Equal-power crossfade length applied when a topology change swaps tap banks.
Definition nonlin_ambience_design.hpp:332
double program_envelope(NonlinProgram program, double tau, double gate_hold=kGateHold, double attack=kRevRise)
The dimensionless program envelope E(τ), τ ∈ [0, 1] (series law 7 — scale invariant,...
Definition nonlin_ambience_design.hpp:413
constexpr int kSegments
Number of time segments the tap range is split into for spectral tilt.
Definition nonlin_ambience_design.hpp:323
constexpr int kNlHumps
NonLin2 program: number of raised-cosine humps, ripple depth, gate point.
Definition nonlin_ambience_design.hpp:311
constexpr double kNlDepth
Definition nonlin_ambience_design.hpp:312
constexpr double kDiffusionDefault
Allpass coefficient, exposed as diffusion.
Definition nonlin_ambience_design.hpp:266
constexpr double kNdMin
Instantaneous pulse density at the start and end of the window, in pulses/second.
Definition nonlin_ambience_design.hpp:274
constexpr double kConverterDcPole
DC-blocker pole used by the converter stage.
Definition nonlin_ambience_design.hpp:369
Definition additive_bank.hpp:218
@ bounded
Definition fm_operator_engine.hpp:35
@ delay
Definition envelope.hpp:57
@ attack
Definition envelope.hpp:58
@ u
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
NonlinProgram
The four programs.
Definition nonlin_ambience_design.hpp:243
@ nonlin2
Rippled body, shaped attack, hard gate.
Definition nonlin_ambience_design.hpp:247
@ gated
Flat body, hard cut.
Definition nonlin_ambience_design.hpp:245
@ ambience
Short falling cloud — the "natural" one.
Definition nonlin_ambience_design.hpp:244
@ reverse
Rise to a peak, then cut.
Definition nonlin_ambience_design.hpp:246
Deterministic randomness for modulation: a counter-free PRNG, a Gaussian draw, a stateless purpose-ke...
Definition velvet_noise.hpp:14
Pure intermediate for one candidate in a velvet tap grid.
Definition nonlin_ambience_design.hpp:464
double magnitude
Definition nonlin_ambience_design.hpp:466
double gain
Definition nonlin_ambience_design.hpp:467
bool audible
Definition nonlin_ambience_design.hpp:469
int segment
Definition nonlin_ambience_design.hpp:468
int delay
Definition nonlin_ambience_design.hpp:465
First-order topology-preserving transform (TPT) filter.
The real-unit vocabulary shared by every DSP block in the catalog.
Coordinate-keyed velvet-noise grid draws.