← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
audio_stft_mag_phase_checked.c
Go to the documentation of this file.
1#include "ckernel_tts.h"
3
4#include <math.h>
5#include <stdint.h>
6#include <string.h>
7
8int audio_stft_mag_phase_plan_f32(size_t samples, size_t n_fft, size_t hop,
9 size_t *frames, size_t *spectral_elements) {
10 if (!frames || !spectral_elements || n_fft < 2 || (n_fft & 1) ||
11 !hop || samples <= n_fft / 2 || samples > INT64_MAX / 2 ||
12 n_fft > INT64_MAX / 2 || samples > SIZE_MAX / sizeof(float) ||
13 n_fft > SIZE_MAX / sizeof(float)) return CK_AUDIO_EXTENT_INVALID;
14 const size_t count = samples / hop + 1;
15 const size_t bins = n_fft / 2 + 1;
16 if (count > SIZE_MAX / (2 * bins) ||
17 count * (2 * bins) > SIZE_MAX / sizeof(float))
19 *frames = count;
20 *spectral_elements = count * 2 * bins;
21 return CK_AUDIO_EXTENT_OK;
22}
23
24/* Centered reflect-pad STFT, periodic-Hann window, unnormalized forward DFT.
25 * Output rows are magnitude bins followed by phase bins, matching a channel
26 * concatenation of torch.abs(torch.stft(...)) and torch.angle(...).
27 * Tables and staging scratch are supplied by the caller. */
28static int stft_trace_region(const double *trace, size_t elements) {
29 return trace && (uintptr_t)trace % _Alignof(double) == 0 &&
30 elements <= SIZE_MAX / sizeof(double) &&
31 (uintptr_t)trace <= UINTPTR_MAX - elements * sizeof(double);
32}
33
34static int stft_trace_overlaps(const double *trace, size_t elements,
35 const float *other, size_t other_elements) {
36 return (uintptr_t)trace < (uintptr_t)other + other_elements * sizeof(float) &&
37 (uintptr_t)other < (uintptr_t)trace + elements * sizeof(double);
38}
39
40static int stft_mag_phase_impl(const float *samples, size_t samples_capacity,
41 const float *window, size_t window_capacity,
42 const float *cos_table, const float *sin_table, size_t table_capacity,
43 float *output, size_t output_capacity, size_t output_stride,
44 float *scratch, size_t scratch_capacity,
45 size_t samples_count, size_t n_fft, size_t hop, size_t frames,
46 double *complex_trace, size_t complex_trace_capacity) {
47 size_t required_frames, spectral_elements, span, bins, table_need;
48 const int planned = audio_stft_mag_phase_plan_f32(samples_count, n_fft,
49 hop, &required_frames, &spectral_elements);
50 if (planned != CK_AUDIO_EXTENT_OK) return planned;
51 if (frames != required_frames || output_stride < frames ||
52 n_fft > SIZE_MAX / (n_fft / 2 + 1)) return CK_AUDIO_EXTENT_INVALID;
53 bins = n_fft / 2 + 1;
54 table_need = bins * n_fft;
55 if (!ck_checked_span(2 * bins, frames, output_stride, &span))
57 if (samples_capacity < samples_count || window_capacity < n_fft ||
58 table_capacity < table_need || output_capacity < span ||
59 scratch_capacity < spectral_elements) return CK_AUDIO_EXTENT_LIMIT;
60 if (!ck_checked_region(samples, samples_count) ||
61 !ck_checked_region(window, n_fft) ||
62 !ck_checked_region(cos_table, table_need) ||
63 !ck_checked_region(sin_table, table_need) ||
64 !ck_checked_region(output, span) ||
65 !ck_checked_region(scratch, spectral_elements) ||
66 ck_checked_overlap(output, span, samples, samples_count) ||
67 ck_checked_overlap(output, span, window, n_fft) ||
68 ck_checked_overlap(output, span, cos_table, table_need) ||
69 ck_checked_overlap(output, span, sin_table, table_need) ||
70 ck_checked_overlap(output, span, scratch, spectral_elements) ||
71 ck_checked_overlap(scratch, spectral_elements, samples, samples_count) ||
72 ck_checked_overlap(scratch, spectral_elements, window, n_fft) ||
73 ck_checked_overlap(scratch, spectral_elements, cos_table, table_need) ||
74 ck_checked_overlap(scratch, spectral_elements, sin_table, table_need))
76 if (!ck_checked_finite(samples, samples_count) ||
77 !ck_checked_finite(window, n_fft) ||
78 !ck_checked_finite(cos_table, table_need) ||
79 !ck_checked_finite(sin_table, table_need))
81 if (complex_trace) {
82 if (complex_trace_capacity < spectral_elements)
84 if (!stft_trace_region(complex_trace, spectral_elements) ||
85 stft_trace_overlaps(complex_trace, spectral_elements, samples, samples_count) ||
86 stft_trace_overlaps(complex_trace, spectral_elements, window, n_fft) ||
87 stft_trace_overlaps(complex_trace, spectral_elements, cos_table, table_need) ||
88 stft_trace_overlaps(complex_trace, spectral_elements, sin_table, table_need) ||
89 stft_trace_overlaps(complex_trace, spectral_elements, output, span) ||
90 stft_trace_overlaps(complex_trace, spectral_elements, scratch, spectral_elements))
92 } else if (complex_trace_capacity) return CK_AUDIO_EXTENT_INVALID;
93 const size_t center = n_fft / 2;
94 for (size_t frame = 0; frame < frames; ++frame) {
95 for (size_t bin = 0; bin < bins; ++bin) {
96 double real = 0.0, imag = 0.0;
97 for (size_t tap = 0; tap < n_fft; ++tap) {
98 /* samples_count > center guarantees one reflection suffices. */
99 const int64_t unreflected = (int64_t)(frame * hop + tap) -
100 (int64_t)center;
101 size_t source;
102 if (unreflected < 0) source = (size_t)(-unreflected);
103 else if ((uint64_t)unreflected >= samples_count)
104 source = 2 * (samples_count - 1) - (size_t)unreflected;
105 else source = (size_t)unreflected;
106 const double value = (double)samples[source] * window[tap];
107 real += value * cos_table[bin * n_fft + tap];
108 imag += value * sin_table[bin * n_fft + tap];
109 }
110 /* The Nyquist bin of a real-input even FFT has exactly zero
111 * imaginary part. Trig-table roundoff otherwise flips +pi to
112 * -pi, which is not interchangeable for downstream convolutions. */
113 if (bin == bins - 1) imag = 0.0;
114 const float magnitude = (float)hypot(real, imag);
115 const float phase = (float)atan2(imag, real);
116 if (!isfinite(magnitude) || !isfinite(phase))
118 if (complex_trace) {
119 const size_t index = 2 * (bin * frames + frame);
120 complex_trace[index] = real;
121 complex_trace[index + 1] = imag;
122 }
123 scratch[bin * frames + frame] = magnitude;
124 scratch[(bins + bin) * frames + frame] = phase;
125 }
126 }
127 for (size_t row = 0; row < 2 * bins; ++row)
128 memcpy(output + row * output_stride, scratch + row * frames,
129 frames * sizeof(float));
130 return CK_AUDIO_EXTENT_OK;
131}
132
133int audio_stft_mag_phase_checked_f32(const float *samples, size_t samples_capacity,
134 const float *window, size_t window_capacity,
135 const float *cos_table, const float *sin_table, size_t table_capacity,
136 float *output, size_t output_capacity, size_t output_stride,
137 float *scratch, size_t scratch_capacity,
138 size_t samples_count, size_t n_fft, size_t hop, size_t frames) {
139 return stft_mag_phase_impl(samples, samples_capacity, window,
140 window_capacity, cos_table, sin_table, table_capacity, output,
141 output_capacity, output_stride, scratch, scratch_capacity,
142 samples_count, n_fft, hop, frames, NULL, 0);
143}
144
145/* Diagnostic-only: records the actual FP64 real/imag values passed to atan2.
146 * Production calls the same implementation with no trace buffer. */
148 size_t samples_capacity, const float *window, size_t window_capacity,
149 const float *cos_table, const float *sin_table, size_t table_capacity,
150 float *output, size_t output_capacity, size_t output_stride,
151 float *scratch, size_t scratch_capacity,
152 size_t samples_count, size_t n_fft, size_t hop, size_t frames,
153 double *complex_trace, size_t complex_trace_capacity) {
154 if (!complex_trace) return CK_AUDIO_EXTENT_INVALID;
155 return stft_mag_phase_impl(samples, samples_capacity, window,
156 window_capacity, cos_table, sin_table, table_capacity, output,
157 output_capacity, output_stride, scratch, scratch_capacity,
158 samples_count, n_fft, hop, frames, complex_trace,
159 complex_trace_capacity);
160}
int audio_stft_mag_phase_checked_f32(const float *samples, size_t samples_capacity, const float *window, size_t window_capacity, const float *cos_table, const float *sin_table, size_t table_capacity, float *output, size_t output_capacity, size_t output_stride, float *scratch, size_t scratch_capacity, size_t samples_count, size_t n_fft, size_t hop, size_t frames)
int audio_stft_mag_phase_trace_checked_f32(const float *samples, size_t samples_capacity, const float *window, size_t window_capacity, const float *cos_table, const float *sin_table, size_t table_capacity, float *output, size_t output_capacity, size_t output_stride, float *scratch, size_t scratch_capacity, size_t samples_count, size_t n_fft, size_t hop, size_t frames, double *complex_trace, size_t complex_trace_capacity)
int audio_stft_mag_phase_plan_f32(size_t samples, size_t n_fft, size_t hop, size_t *frames, size_t *spectral_elements)
static int stft_mag_phase_impl(const float *samples, size_t samples_capacity, const float *window, size_t window_capacity, const float *cos_table, const float *sin_table, size_t table_capacity, float *output, size_t output_capacity, size_t output_stride, float *scratch, size_t scratch_capacity, size_t samples_count, size_t n_fft, size_t hop, size_t frames, double *complex_trace, size_t complex_trace_capacity)
static int stft_trace_region(const double *trace, size_t elements)
static int stft_trace_overlaps(const double *trace, size_t elements, const float *other, size_t other_elements)
static int ck_checked_finite(const float *ptr, size_t n)
static int ck_checked_span(size_t rows, size_t width, size_t stride, size_t *span)
static int ck_checked_region(const float *ptr, size_t n)
static int ck_checked_overlap(const float *a, size_t na, const float *b, size_t nb)
@ CK_AUDIO_EXTENT_INVALID
Definition ckernel_tts.h:42
@ CK_AUDIO_EXTENT_LIMIT
Definition ckernel_tts.h:43
@ CK_AUDIO_EXTENT_OVERFLOW
Definition ckernel_tts.h:44
@ CK_AUDIO_EXTENT_OK
Definition ckernel_tts.h:41
const uint8_t size_t count
Definition true_bpe.h:209