MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Analysis.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * @file Analysis.hpp
5 * @brief Discrete sequence analysis primitives for MayaFlux::Kinesis
6 *
7 * Pure numerical functions operating on contiguous double-precision spans.
8 * No MayaFlux type dependencies. All functions are domain-agnostic — the
9 * same primitives serve audio, visual, control, and any other sampled sequence.
10 *
11 * ## Design constraints
12 * - Inputs are immutable spans; outputs are value-returning vectors
13 * - All windowed functions require pre-computed num_windows from the caller
14 * - Parallelism is handled internally via MayaFlux::Parallel
15 * - Spectral functions carry an Eigen::FFT dependency and are not portable
16 * to compute shader contexts; a future kernel-compatible variant will be
17 * a separate function set
18 *
19 * ## SIMD portability
20 * All non-spectral, non-position functions are auto-vectorisable with
21 * -O2 -march=native. Spectral functions are not — they delegate to Eigen::FFT.
22 * Position-finding functions (zero crossings, peaks, onsets) produce sparse
23 * output and are inherently scalar.
24 */
25
27
28// ============================================================================
29// Window utilities
30// ============================================================================
31
32/**
33 * @brief Compute the number of analysis windows for a given data size
34 * @param data_size Number of samples
35 * @param window_size Samples per window
36 * @param hop_size Samples between window starts
37 * @return Number of complete windows, 0 if data_size < window_size
38 */
39[[nodiscard]] inline size_t num_windows(size_t data_size, uint32_t window_size, uint32_t hop_size) noexcept
40{
41 if (data_size < window_size)
42 return 0;
43 return (data_size - window_size) / hop_size + 1;
44}
45
46// ============================================================================
47// Energy
48// ============================================================================
49
50/**
51 * @brief RMS energy per window
52 * @param data Input span
53 * @param n_windows Pre-computed window count from num_windows()
54 * @param hop_size Samples between window starts
55 * @param window_size Samples per window
56 * @return Per-window RMS values
57 */
58[[nodiscard]] std::vector<double> rms(
59 std::span<const double> data,
60 size_t n_windows,
61 uint32_t hop_size,
62 uint32_t window_size);
63
64/**
65 * @brief Peak amplitude per window
66 * @param data Input span
67 * @param n_windows Pre-computed window count from num_windows()
68 * @param hop_size Samples between window starts
69 * @param window_size Samples per window
70 * @return Per-window peak absolute values
71 */
72[[nodiscard]] std::vector<double> peak(
73 std::span<const double> data,
74 size_t n_windows,
75 uint32_t hop_size,
76 uint32_t window_size);
77
78/**
79 * @brief Power (sum of squares) per window
80 * @param data Input span
81 * @param n_windows Pre-computed window count from num_windows()
82 * @param hop_size Samples between window starts
83 * @param window_size Samples per window
84 * @return Per-window power values
85 */
86[[nodiscard]] std::vector<double> power(
87 std::span<const double> data,
88 size_t n_windows,
89 uint32_t hop_size,
90 uint32_t window_size);
91
92/**
93 * @brief Dynamic range in dB per window
94 *
95 * Computes 20*log10(max_abs / max(min_abs, 1e-10)) per window.
96 *
97 * @param data Input span
98 * @param n_windows Pre-computed window count from num_windows()
99 * @param hop_size Samples between window starts
100 * @param window_size Samples per window
101 * @return Per-window dynamic range values in dB
102 */
103[[nodiscard]] std::vector<double> dynamic_range(
104 std::span<const double> data,
105 size_t n_windows,
106 uint32_t hop_size,
107 uint32_t window_size);
108
109/**
110 * @brief Zero-crossing rate per window
111 *
112 * Normalised by (window_size - 1), giving crossings per sample.
113 *
114 * @param data Input span
115 * @param n_windows Pre-computed window count from num_windows()
116 * @param hop_size Samples between window starts
117 * @param window_size Samples per window
118 * @return Per-window ZCR values
119 */
120[[nodiscard]] std::vector<double> zero_crossing_rate(
121 std::span<const double> data,
122 size_t n_windows,
123 uint32_t hop_size,
124 uint32_t window_size);
125
126/**
127 * @brief Spectral energy per window using Hann-windowed FFT
128 *
129 * @note Eigen::FFT dependency — not portable to compute shader contexts.
130 *
131 * @param data Input span
132 * @param n_windows Pre-computed window count from num_windows()
133 * @param hop_size Samples between window starts
134 * @param window_size Samples per window (determines FFT size)
135 * @return Per-window summed spectral energy, normalised by window_size
136 */
137[[nodiscard]] std::vector<double> spectral_energy(
138 std::span<const double> data,
139 size_t n_windows,
140 uint32_t hop_size,
141 uint32_t window_size);
142
143/**
144 * @brief Low-frequency spectral energy per window
145 *
146 * Computes energy in the lowest (low_bin_fraction * N/2) bins of the
147 * magnitude spectrum. Defaults to the bottom eighth of the spectrum.
148 *
149 * @note Eigen::FFT dependency — not portable to compute shader contexts.
150 *
151 * @param data Input span
152 * @param n_windows Pre-computed window count from num_windows()
153 * @param hop_size Samples between window starts
154 * @param window_size Samples per window
155 * @param low_bin_fraction Fraction of positive-frequency bins to include (default: 0.125)
156 * @return Per-window low-frequency energy values
157 */
158[[nodiscard]] std::vector<double> low_frequency_energy(
159 std::span<const double> data,
160 size_t n_windows,
161 uint32_t hop_size,
162 uint32_t window_size,
163 double low_bin_fraction = 0.125);
164
165// ============================================================================
166// Statistics
167// ============================================================================
168
169/**
170 * @brief Arithmetic mean per window
171 */
172[[nodiscard]] std::vector<double> mean(
173 std::span<const double> data,
174 size_t n_windows,
175 uint32_t hop_size,
176 uint32_t window_size);
177
178/**
179 * @brief Variance per window
180 * @param sample_variance If true, divides by (N-1); otherwise by N
181 */
182[[nodiscard]] std::vector<double> variance(
183 std::span<const double> data,
184 size_t n_windows,
185 uint32_t hop_size,
186 uint32_t window_size,
187 bool sample_variance = true);
188
189/**
190 * @brief Standard deviation per window
191 * @param sample_variance If true, uses sample variance (N-1)
192 */
193[[nodiscard]] std::vector<double> std_dev(
194 std::span<const double> data,
195 size_t n_windows,
196 uint32_t hop_size,
197 uint32_t window_size,
198 bool sample_variance = true);
199
200/**
201 * @brief Skewness (standardised third central moment) per window
202 *
203 * Returns 0 when variance is below epsilon rather than branching on zero.
204 */
205[[nodiscard]] std::vector<double> skewness(
206 std::span<const double> data,
207 size_t n_windows,
208 uint32_t hop_size,
209 uint32_t window_size);
210
211/**
212 * @brief Excess kurtosis (fourth central moment - 3) per window
213 *
214 * Normal distribution yields 0. Returns 0 when variance is below epsilon.
215 */
216[[nodiscard]] std::vector<double> kurtosis(
217 std::span<const double> data,
218 size_t n_windows,
219 uint32_t hop_size,
220 uint32_t window_size);
221
222/**
223 * @brief Median per window via nth_element partial sort
224 */
225[[nodiscard]] std::vector<double> median(
226 std::span<const double> data,
227 size_t n_windows,
228 uint32_t hop_size,
229 uint32_t window_size);
230
231/**
232 * @brief Arbitrary percentile per window via linear interpolation
233 * @param percentile Value in [0, 100]
234 */
235[[nodiscard]] std::vector<double> percentile(
236 std::span<const double> data,
237 size_t n_windows,
238 uint32_t hop_size,
239 uint32_t window_size,
240 double percentile_value);
241
242/**
243 * @brief Shannon entropy per window using Sturges-rule histogram
244 * @param num_bins Histogram bin count; 0 = auto via Sturges rule
245 */
246[[nodiscard]] std::vector<double> entropy(
247 std::span<const double> data,
248 size_t n_windows,
249 uint32_t hop_size,
250 uint32_t window_size,
251 size_t num_bins = 0);
252
253/**
254 * @brief Minimum value per window
255 */
256[[nodiscard]] std::vector<double> min(
257 std::span<const double> data,
258 size_t n_windows,
259 uint32_t hop_size,
260 uint32_t window_size);
261
262/**
263 * @brief Maximum value per window
264 */
265[[nodiscard]] std::vector<double> max(
266 std::span<const double> data,
267 size_t n_windows,
268 uint32_t hop_size,
269 uint32_t window_size);
270
271/**
272 * @brief Value range (max - min) per window
273 */
274[[nodiscard]] std::vector<double> range(
275 std::span<const double> data,
276 size_t n_windows,
277 uint32_t hop_size,
278 uint32_t window_size);
279
280/**
281 * @brief Sum per window
282 */
283[[nodiscard]] std::vector<double> sum(
284 std::span<const double> data,
285 size_t n_windows,
286 uint32_t hop_size,
287 uint32_t window_size);
288
289/**
290 * @brief Sample count per window (as double for pipeline uniformity)
291 *
292 * Returns the actual number of samples in each window, which may differ
293 * from window_size at the final window if data does not divide evenly.
294 */
295[[nodiscard]] std::vector<double> count(
296 std::span<const double> data,
297 size_t n_windows,
298 uint32_t hop_size,
299 uint32_t window_size);
300
301/**
302 * @brief Median absolute deviation per window
303 */
304[[nodiscard]] std::vector<double> mad(
305 std::span<const double> data,
306 size_t n_windows,
307 uint32_t hop_size,
308 uint32_t window_size);
309
310/**
311 * @brief Coefficient of variation (std_dev / mean) per window
312 *
313 * Returns 0 when |mean| < 1e-15.
314 *
315 * @param sample_variance If true, uses sample variance (N-1)
316 */
317[[nodiscard]] std::vector<double> coefficient_of_variation(
318 std::span<const double> data,
319 size_t n_windows,
320 uint32_t hop_size,
321 uint32_t window_size,
322 bool sample_variance = true);
323
324/**
325 * @brief Mode per window via tolerance-bucketed frequency count
326 *
327 * Buckets values at tolerance 1e-10 and returns the mean of the most
328 * frequent bucket.
329 */
330[[nodiscard]] std::vector<double> mode(
331 std::span<const double> data,
332 size_t n_windows,
333 uint32_t hop_size,
334 uint32_t window_size);
335
336/**
337 * @brief Mean z-score per window
338 *
339 * Returns the mean of ((x - mean) / std_dev) over each window.
340 * Returns 0 when std_dev is zero.
341 *
342 * @param sample_variance If true, uses sample variance (N-1)
343 */
344[[nodiscard]] std::vector<double> mean_zscore(
345 std::span<const double> data,
346 size_t n_windows,
347 uint32_t hop_size,
348 uint32_t window_size,
349 bool sample_variance = true);
350
351// ============================================================================
352// Position finders
353// ============================================================================
354
355/**
356 * @brief Sample indices of zero crossings in the full span
357 *
358 * @note Produces sparse output; not vectorisable or suitable for compute kernels.
359 *
360 * @param data Input span
361 * @param threshold Crossing threshold (default: 0.0)
362 * @return Sorted sample indices where sign changes occur
363 */
364[[nodiscard]] std::vector<size_t> zero_crossing_positions(
365 std::span<const double> data,
366 double threshold = 0.0);
367
368/**
369 * @brief Sample indices of local peak maxima in the full span
370 *
371 * A peak at index i satisfies: |data[i]| > threshold,
372 * |data[i]| >= |data[i-1]|, |data[i]| >= |data[i+1]|,
373 * and i - last_peak >= min_distance.
374 *
375 * @note Produces sparse output; not vectorisable or suitable for compute kernels.
376 *
377 * @param data Input span
378 * @param threshold Minimum absolute value to qualify as a peak
379 * @param min_distance Minimum sample gap between accepted peaks
380 * @return Sorted sample indices of detected peaks
381 */
382[[nodiscard]] std::vector<size_t> peak_positions(
383 std::span<const double> data,
384 double threshold = 0.0,
385 size_t min_distance = 1);
386
387/**
388 * @brief Sample indices of onsets detected via spectral flux
389 *
390 * Onset positions are local maxima of the half-wave-rectified spectral
391 * flux curve that exceed the normalised threshold. Sequential processing
392 * is required — this function is not parallelisable.
393 *
394 * @note Eigen::FFT dependency. Produces sparse output.
395 * Not suitable for compute shader contexts.
396 *
397 * @param data Input span
398 * @param window_size FFT window size in samples
399 * @param hop_size Hop size between successive FFT frames
400 * @param threshold Normalised flux threshold in [0, 1] (default: 0.1)
401 * @return Sorted sample indices of detected onsets
402 */
403[[nodiscard]] std::vector<size_t> onset_positions(
404 std::span<const double> data,
405 uint32_t window_size,
406 uint32_t hop_size,
407 double threshold = 0.1);
408
409} // namespace MayaFlux::Kinesis::Discrete
Eigen::Index count
std::vector< double > dynamic_range(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Dynamic range in dB per window.
Definition Analysis.cpp:89
std::vector< double > mean(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Arithmetic mean per window.
Definition Analysis.cpp:200
std::vector< double > spectral_energy(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Spectral energy per window using Hann-windowed FFT.
Definition Analysis.cpp:133
std::vector< double > peak(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Peak amplitude per window.
Definition Analysis.cpp:51
std::vector< double > range(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Value range (max - min) per window.
Definition Analysis.cpp:452
std::vector< size_t > zero_crossing_positions(std::span< const double > data, double threshold)
Sample indices of zero crossings in the full span.
Definition Analysis.cpp:634
std::vector< double > entropy(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size, size_t num_bins)
Shannon entropy per window using Sturges-rule histogram.
Definition Analysis.cpp:375
std::vector< double > median(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Median per window via nth_element partial sort.
Definition Analysis.cpp:325
std::vector< double > variance(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size, bool sample_variance)
Variance per window.
Definition Analysis.cpp:219
std::vector< double > zero_crossing_rate(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Zero-crossing rate per window.
Definition Analysis.cpp:112
std::vector< size_t > peak_positions(std::span< const double > data, double threshold, size_t min_distance)
Sample indices of local peak maxima in the full span.
Definition Analysis.cpp:646
std::vector< double > min(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Minimum value per window.
Definition Analysis.cpp:420
std::vector< double > std_dev(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size, bool sample_variance)
Standard deviation per window.
Definition Analysis.cpp:251
std::vector< size_t > onset_positions(std::span< const double > data, uint32_t window_size, uint32_t hop_size, double threshold)
Sample indices of onsets detected via spectral flux.
Definition Analysis.cpp:670
std::vector< double > max(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Maximum value per window.
Definition Analysis.cpp:436
std::vector< double > skewness(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Skewness (standardised third central moment) per window.
Definition Analysis.cpp:259
std::vector< double > mad(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Median absolute deviation per window.
Definition Analysis.cpp:501
std::vector< double > mean_zscore(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size, bool sample_variance)
Mean z-score per window.
Definition Analysis.cpp:590
std::vector< double > low_frequency_energy(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size, double low_bin_fraction)
Low-frequency spectral energy per window.
Definition Analysis.cpp:164
std::vector< double > sum(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Sum per window.
Definition Analysis.cpp:469
size_t num_windows(size_t data_size, uint32_t window_size, uint32_t hop_size) noexcept
Compute the number of analysis windows for a given data size.
Definition Analysis.hpp:39
std::vector< double > coefficient_of_variation(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size, bool sample_variance)
Coefficient of variation (std_dev / mean) per window.
Definition Analysis.cpp:545
std::vector< double > mode(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Mode per window via tolerance-bucketed frequency count.
Definition Analysis.cpp:559
std::vector< double > kurtosis(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Excess kurtosis (fourth central moment - 3) per window.
Definition Analysis.cpp:292
std::vector< double > rms(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
RMS energy per window.
Definition Analysis.cpp:32
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
std::vector< double > percentile(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size, double percentile_value)
Arbitrary percentile per window via linear interpolation.
Definition Analysis.cpp:350