MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Transform.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * @file Transform.hpp
5 * @brief Discrete sequence transformation primitives for MayaFlux::Kinesis
6 *
7 * Pure numerical functions operating on contiguous double-precision spans.
8 * No MayaFlux type dependencies. Domain-agnostic — the same primitives
9 * serve audio, visual, control, and any other sampled sequence.
10 *
11 * All mutating functions operate in-place on the supplied span.
12 * Functions that change the output size return a new vector.
13 * Callers loop over channels; no multichannel wrappers are provided.
14 *
15 * SIMD notes:
16 * linear, clamp, reverse, fade, normalize, quantize, interpolate_linear,
17 * and interpolate_cubic are written to be auto-vectorisable under
18 * -O2 -march=native (GCC/Clang). Transcendental functions (power,
19 * exponential, logarithmic) remain scalar; they are not hot-path
20 * and require SVML or a polynomial approximation for SIMD throughput.
21 */
22
24
25// ============================================================================
26// Pointwise arithmetic
27// ============================================================================
28
29/**
30 * @brief Linear map y = a*x + b applied in-place
31 * @param data Target span
32 * @param a Scale factor
33 * @param b Offset
34 */
35void linear(std::span<double> data, double a, double b) noexcept;
36
37/**
38 * @brief Power map y = x^exponent applied in-place
39 * Scalar transcendental — not SIMD hot-path.
40 * @param data Target span
41 * @param exponent Exponent
42 */
43void power(std::span<double> data, double exponent) noexcept;
44
45/**
46 * @brief Exponential map y = a * base^(b*x) applied in-place
47 * Scalar transcendental — not SIMD hot-path.
48 * @param data Target span
49 * @param a Scale factor
50 * @param b Rate
51 * @param base Base (default: e)
52 */
53void exponential(std::span<double> data, double a, double b,
54 double base = std::numbers::e) noexcept;
55
56/**
57 * @brief Logarithmic map y = a * log_base(b*x + c) applied in-place
58 * Values where (b*x + c) <= 0 are mapped to 0.
59 * Scalar transcendental — not SIMD hot-path.
60 * @param data Target span
61 * @param a Scale factor
62 * @param b Input scale
63 * @param c Input offset
64 * @param base Logarithm base (default: e)
65 */
66void logarithmic(std::span<double> data, double a, double b, double c,
67 double base = std::numbers::e) noexcept;
68
69/**
70 * @brief Trigonometric map y = amplitude * f(frequency*x + phase) applied in-place
71 * @tparam TrigFunc Callable matching double(double)
72 * @param data Target span
73 * @param func Trigonometric function (std::sin, std::cos, etc.)
74 * @param frequency Frequency scaling applied to x
75 * @param amplitude Amplitude scaling applied to output
76 * @param phase Phase offset added to x before the function
77 */
78template <typename TrigFunc>
79 requires std::invocable<TrigFunc, double>
80 && std::same_as<std::invoke_result_t<TrigFunc, double>, double>
81void apply_trig(std::span<double> data,
82 TrigFunc func,
83 double frequency,
84 double amplitude,
85 double phase) noexcept
86{
87 std::ranges::transform(data, data.begin(),
88 [&](double x) { return amplitude * func(frequency * x + phase); });
89}
90
91/**
92 * @brief Clamp values to [lo, hi] in-place
93 * @param data Target span
94 * @param lo Lower bound
95 * @param hi Upper bound
96 */
97void clamp(std::span<double> data, double lo, double hi) noexcept;
98
99/**
100 * @brief Quantize to n-bit resolution in-place
101 * Input is assumed to be in [-1, 1]; values outside are clamped first.
102 * Uses lrint bias trick to avoid scalar std::round and improve
103 * auto-vectorisation.
104 * @param data Target span
105 * @param bits Bit depth (1–53)
106 */
107void quantize(std::span<double> data, uint8_t bits) noexcept;
108
109/**
110 * @brief Normalize to [target_min, target_max] in-place
111 * Single-pass min/max reduction followed by a single transform pass.
112 * No-op when all values are equal.
113 * @param data Target span
114 * @param target_min Output minimum
115 * @param target_max Output maximum
116 */
117void normalize(std::span<double> data,
118 double target_min = -1.0,
119 double target_max = 1.0) noexcept;
120
121// ============================================================================
122// Temporal shape
123// ============================================================================
124
125/**
126 * @brief Reverse temporal order in-place
127 * @param data Target span
128 */
129void reverse(std::span<double> data) noexcept;
130
131/**
132 * @brief Apply equal-power (cosine) fade-in then fade-out envelope in-place
133 * The cosine taper maintains constant perceived loudness at the
134 * crossover point (0 dB centre gain vs −6 dB for a linear taper).
135 * @param data Target span
136 * @param fade_in_ratio Fraction of data length used for fade-in [0, 1]
137 * @param fade_out_ratio Fraction of data length used for fade-out [0, 1]
138 */
139void fade(std::span<double> data,
140 double fade_in_ratio,
141 double fade_out_ratio) noexcept;
142
143/**
144 * @brief Extract a contiguous slice by ratio, returning a new vector
145 * @param data Source span
146 * @param start_ratio Normalised start position [0, 1]
147 * @param end_ratio Normalised end position [0, 1], must be > start_ratio
148 * @return Slice data; empty if parameters are degenerate
149 */
150[[nodiscard]] std::vector<double> slice(
151 std::span<const double> data,
152 double start_ratio,
153 double end_ratio);
154
155/**
156 * @brief Prepend delay_samples zero-valued (or fill_value) samples, returning a new vector
157 * @param data Source span
158 * @param delay_samples Number of samples to prepend
159 * @param fill_value Value used for the prepended region
160 * @return Delayed output of size data.size() + delay_samples
161 */
162[[nodiscard]] std::vector<double> delay(
163 std::span<const double> data,
164 uint32_t delay_samples,
165 double fill_value = 0.0);
166
167// ============================================================================
168// Resampling / interpolation
169// ============================================================================
170
171/**
172 * @brief Linear interpolation from src into dst (caller sizes dst)
173 * Branchless inner loop with precomputed step; dst may be larger or
174 * smaller than src. The final index is clamped via std::min rather
175 * than a conditional branch, permitting auto-vectorisation.
176 * @param src Source span (read-only)
177 * @param dst Destination span (written in-place)
178 */
179void interpolate_linear(std::span<const double> src,
180 std::span<double> dst) noexcept;
181
182/**
183 * @brief Catmull-Rom cubic interpolation from src into dst (caller sizes dst)
184 * Branchless boundary clamping; Horner evaluation form.
185 * @param src Source span (read-only, at least 2 samples)
186 * @param dst Destination span (written in-place)
187 */
188void interpolate_cubic(std::span<const double> src,
189 std::span<double> dst) noexcept;
190
191/**
192 * @brief Time-stretch via linear interpolation resampling
193 * Fast but alias-naive: no anti-aliasing pre-filter is applied when
194 * stretch_factor < 1. Suitable for control signals and non-critical
195 * upsampling. For audio time-stretching use
196 * Discrete::phase_vocoder_stretch (PhaseVocoder.hpp).
197 * @param data Source span
198 * @param stretch_factor >1 = longer/slower, <1 = shorter/faster; 1 returns copy
199 * @return Resampled vector
200 */
201[[nodiscard]] std::vector<double> time_stretch(
202 std::span<const double> data,
203 double stretch_factor);
204
205} // namespace MayaFlux::Kinesis::Discrete
size_t a
size_t b
double frequency
void exponential(std::span< double > data, double a, double b, double base) noexcept
Exponential map y = a * base^(b*x) applied in-place Scalar transcendental — not SIMD hot-path.
Definition Transform.cpp:23
void fade(std::span< double > data, double fade_in_ratio, double fade_out_ratio) noexcept
Apply equal-power (cosine) fade-in then fade-out envelope in-place The cosine taper maintains constan...
Definition Transform.cpp:95
void linear(std::span< double > data, double a, double b) noexcept
Linear map y = a*x + b applied in-place.
Definition Transform.cpp:11
void clamp(std::span< double > data, double lo, double hi) noexcept
Clamp values to [lo, hi] in-place.
Definition Transform.cpp:44
void interpolate_cubic(std::span< const double > src, std::span< double > dst) noexcept
Catmull-Rom cubic interpolation from src into dst (caller sizes dst) Branchless boundary clamping; Ho...
std::vector< double > slice(std::span< const double > data, double start_ratio, double end_ratio)
Extract a contiguous slice by ratio, returning a new vector.
std::vector< double > delay(std::span< const double > data, uint32_t delay_samples, double fill_value)
Prepend delay_samples zero-valued (or fill_value) samples, returning a new vector.
void reverse(std::span< double > data) noexcept
Reverse temporal order in-place.
Definition Transform.cpp:90
std::vector< double > time_stretch(std::span< const double > data, double stretch_factor)
Time-stretch via linear interpolation resampling Fast but alias-naive: no anti-aliasing pre-filter is...
void normalize(std::span< double > data, double target_min, double target_max) noexcept
Normalize to [target_min, target_max] in-place Single-pass min/max reduction followed by a single tra...
Definition Transform.cpp:61
void logarithmic(std::span< double > data, double a, double b, double c, double base) noexcept
Logarithmic map y = a * log_base(b*x + c) applied in-place Values where (b*x + c) <= 0 are mapped to ...
Definition Transform.cpp:34
void quantize(std::span< double > data, uint8_t bits) noexcept
Quantize to n-bit resolution in-place Input is assumed to be in [-1, 1]; values outside are clamped f...
Definition Transform.cpp:50
void interpolate_linear(std::span< const double > src, std::span< double > dst) noexcept
Linear interpolation from src into dst (caller sizes dst) Branchless inner loop with precomputed step...
std::vector< double > power(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Power (sum of squares) per window.
Definition Analysis.cpp:70
void apply_trig(std::span< double > data, TrigFunc func, double frequency, double amplitude, double phase) noexcept
Trigonometric map y = amplitude * f(frequency*x + phase) applied in-place.
Definition Transform.hpp:81