MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Transform.cpp
Go to the documentation of this file.
1#include "Transform.hpp"
2
4
6
7// ============================================================================
8// Pointwise arithmetic
9// ============================================================================
10
11void linear(std::span<double> data, double a, double b) noexcept
12{
13 std::ranges::transform(data, data.begin(),
14 [a, b](double x) { return a * x + b; });
15}
16
17void power(std::span<double> data, double exponent) noexcept
18{
19 std::ranges::transform(data, data.begin(),
20 [exponent](double x) { return std::pow(x, exponent); });
21}
22
23void exponential(std::span<double> data, double a, double b, double base) noexcept
24{
25 if (base == std::numbers::e) {
26 std::ranges::transform(data, data.begin(),
27 [a, b](double x) { return a * std::exp(b * x); });
28 } else {
29 std::ranges::transform(data, data.begin(),
30 [a, b, base](double x) { return a * std::pow(base, b * x); });
31 }
32}
33
34void logarithmic(std::span<double> data, double a, double b, double c, double base) noexcept
35{
36 const double log_factor = (base == std::numbers::e) ? 1.0 : (1.0 / std::log(base));
37 std::ranges::transform(data, data.begin(),
38 [a, b, c, log_factor](double x) {
39 const double arg = b * x + c;
40 return (arg > 0.0) ? a * std::log(arg) * log_factor : 0.0;
41 });
42}
43
44void clamp(std::span<double> data, double lo, double hi) noexcept
45{
46 std::ranges::transform(data, data.begin(),
47 [lo, hi](double x) { return std::clamp(x, lo, hi); });
48}
49
50void quantize(std::span<double> data, uint8_t bits) noexcept
51{
52 const double levels = std::pow(2.0, bits) - 1.0;
53 const double inv_levels = 1.0 / levels;
54 std::ranges::transform(data, data.begin(),
55 [levels, inv_levels](double x) {
56 const double clamped = std::clamp(x, -1.0, 1.0);
57 return static_cast<double>(std::lrint(clamped * levels)) * inv_levels;
58 });
59}
60
61void normalize(std::span<double> data, double target_min, double target_max) noexcept
62{
63 if (data.empty())
64 return;
65
66 double lo = data[0];
67 double hi = data[0];
68 for (double v : data) {
69 if (v < lo)
70 lo = v;
71 if (v > hi)
72 hi = v;
73 }
74
75 if (hi == lo)
76 return;
77
78 const double inv_src = 1.0 / (hi - lo);
79 const double dst_range = target_max - target_min;
80 std::ranges::transform(data, data.begin(),
81 [lo, inv_src, dst_range, target_min](double x) {
82 return (x - lo) * inv_src * dst_range + target_min;
83 });
84}
85
86// ============================================================================
87// Temporal shape
88// ============================================================================
89
90void reverse(std::span<double> data) noexcept
91{
92 std::ranges::reverse(data);
93}
94
95void fade(std::span<double> data, double fade_in_ratio, double fade_out_ratio) noexcept
96{
97 if (data.empty())
98 return;
99
100 const size_t n = data.size();
101 const auto in_end = static_cast<size_t>(fade_in_ratio * static_cast<double>(n));
102 const size_t out_start = n - static_cast<size_t>(fade_out_ratio * static_cast<double>(n));
103
104 const double pi = std::numbers::pi;
105
106 if (in_end > 0) {
107 const auto in_len = static_cast<double>(in_end - 1);
108 for (size_t i = 0; i < in_end; ++i) {
109 const double t = (in_len > 0.0)
110 ? static_cast<double>(i) / in_len
111 : 1.0;
112 data[i] *= std::sin(t * pi * 0.5);
113 }
114 }
115
116 if (out_start < n) {
117 const auto out_len = static_cast<double>(n - 1 - out_start);
118 for (size_t i = out_start; i < n; ++i) {
119 const double t = (out_len > 0.0)
120 ? static_cast<double>(i - out_start) / out_len
121 : 1.0;
122 data[i] *= std::cos(t * pi * 0.5);
123 }
124 }
125}
126
127std::vector<double> slice(std::span<const double> data,
128 double start_ratio,
129 double end_ratio)
130{
131 if (data.empty() || end_ratio <= start_ratio)
132 return {};
133
134 const size_t s = static_cast<size_t>(std::clamp(start_ratio, 0.0, 1.0) * static_cast<double>(data.size()));
135 const size_t e = static_cast<size_t>(std::clamp(end_ratio, 0.0, 1.0) * static_cast<double>(data.size()));
136
137 if (s >= e)
138 return {};
139
140 return { data.begin() + s, data.begin() + e };
141}
142
143std::vector<double> delay(std::span<const double> data,
144 uint32_t delay_samples,
145 double fill_value)
146{
147 std::vector<double> out(data.size() + delay_samples, fill_value);
148 std::ranges::copy(data, out.begin() + delay_samples);
149 return out;
150}
151
152// ============================================================================
153// Resampling / interpolation
154// ============================================================================
155
156void interpolate_linear(std::span<const double> src, std::span<double> dst) noexcept
157{
158 const size_t dst_n = dst.size();
159 const size_t src_n = src.size();
160
161 if (dst_n == 0 || src_n == 0)
162 return;
163
164 if (dst_n == 1 || src_n == 1) {
165 std::ranges::fill(dst, src[0]);
166 return;
167 }
168
169 const double step = static_cast<double>(src_n - 1) / static_cast<double>(dst_n - 1);
170
171 Parallel::for_each(Parallel::par_unseq,
172 std::views::iota(size_t { 0 }, dst_n).begin(),
173 std::views::iota(size_t { 0 }, dst_n).end(),
174 [&src, &dst, step, src_n](size_t i) {
175 const double pos = static_cast<double>(i) * step;
176 const auto idx = static_cast<size_t>(pos);
177 const size_t idx1 = std::min(idx + 1, src_n - 1);
178 const double frac = pos - static_cast<double>(idx);
179 dst[i] = src[idx] + frac * (src[idx1] - src[idx]);
180 });
181}
182
183void interpolate_cubic(std::span<const double> src, std::span<double> dst) noexcept
184{
185 const size_t dst_n = dst.size();
186 const size_t src_n = src.size();
187
188 if (dst_n == 0 || src_n == 0)
189 return;
190
191 if (dst_n == 1 || src_n == 1) {
192 std::ranges::fill(dst, src[0]);
193 return;
194 }
195
196 const double step = static_cast<double>(src_n - 1) / static_cast<double>(dst_n - 1);
197 const size_t last = src_n - 1;
198
199 Parallel::for_each(Parallel::par_unseq,
200 std::views::iota(size_t { 0 }, dst_n).begin(),
201 std::views::iota(size_t { 0 }, dst_n).end(),
202 [&src, &dst, step, last](size_t i) {
203 const double pos = static_cast<double>(i) * step;
204 const auto idx = static_cast<size_t>(pos);
205 const double f = pos - static_cast<double>(idx);
206
207 const size_t i0 = idx > 0 ? idx - 1 : 0;
208 const size_t i1 = idx;
209 const size_t i2 = std::min(idx + 1, last);
210 const size_t i3 = std::min(idx + 2, last);
211
212 const double y0 = src[i0], y1 = src[i1], y2 = src[i2], y3 = src[i3];
213 const double a = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
214 const double b = y0 - 2.5 * y1 + 2.0 * y2 - 0.5 * y3;
215 const double c = -0.5 * y0 + 0.5 * y2;
216 dst[i] = ((a * f + b) * f + c) * f + y1;
217 });
218}
219
220std::vector<double> time_stretch(std::span<const double> data, double stretch_factor)
221{
222 if (data.empty() || stretch_factor <= 0.0)
223 return {};
224
225 if (stretch_factor == 1.0)
226 return { data.begin(), data.end() };
227
228 const auto out_size = static_cast<size_t>(
229 static_cast<double>(data.size()) * stretch_factor);
230
231 if (out_size == 0)
232 return {};
233
234 std::vector<double> out(out_size);
235 interpolate_linear(data, out);
236 return out;
237}
238
239} // namespace MayaFlux::Kinesis::Discrete
size_t a
size_t b
Discrete sequence transformation primitives for MayaFlux::Kinesis.
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