31template <
typename SampleType =
float>
34 static_assert(std::is_floating_point_v<SampleType>);
50 const int fft_size = 2 * (
num_bins - 1);
51 return (fft_size & (fft_size - 1)) == 0;
69 if (!valid(
config.magnitude) || !valid(
config.phase))
return false;
74 Config
config() const noexcept {
return config_; }
75 bool prepared() const noexcept {
return prepared_; }
76 int channels() const noexcept {
return channels_; }
77 int num_bins() const noexcept {
return num_bins_; }
83 bool process(
const std::complex<SampleType>*
const*
a,
84 const std::complex<SampleType>*
const* b,
85 std::complex<SampleType>*
const* out,
87 SampleType magnitude_amount,
88 SampleType phase_amount)
const noexcept {
91 magnitude_amount, phase_amount);
97 const std::complex<SampleType>*
const* b,
98 std::complex<SampleType>*
const* out,
99 int channels,
int first_bin,
int bin_count,
100 SampleType magnitude_amount,
101 SampleType phase_amount)
const noexcept {
102 if (!prepared_ ||
channels != channels_ || first_bin < 0 || bin_count < 0
103 || first_bin > num_bins_ || bin_count > num_bins_ - first_bin
104 || !std::isfinite(magnitude_amount) || !std::isfinite(phase_amount)
105 ||
a ==
nullptr || b ==
nullptr || out ==
nullptr)
107 for (
int ch = 0; ch < channels_; ++ch)
108 if (
a[ch] ==
nullptr || b[ch] ==
nullptr || out[ch] ==
nullptr)
111 const SampleType magnitude_mix =
112 std::clamp(magnitude_amount, SampleType{0}, SampleType{1});
113 const SampleType phase_mix =
114 std::clamp(phase_amount, SampleType{0}, SampleType{1});
116 for (
int ch = 0; ch < channels_; ++ch) {
117 for (
int bin = first_bin; bin < first_bin + bin_count; ++bin) {
118 const bool self_conjugate = bin == 0 || bin == num_bins_ - 1;
119 const auto input_a =
a[ch][bin];
120 const auto input_b = b[ch][bin];
121 DecodedBin da = decode(input_a);
122 DecodedBin db = decode(input_b);
123 if (!da.finite && !db.finite) {
127 if (magnitude_mix == SampleType{0} && phase_mix == SampleType{0}) {
128 const auto selected = da.finite ? input_a : input_b;
129 const auto& selected_decoded = da.finite ? da : db;
130 if (!selected_decoded.saturated
131 && (!self_conjugate || selected.imag() == SampleType{0})) {
132 out[ch][bin] = selected;
136 if (magnitude_mix == SampleType{1} && phase_mix == SampleType{1}) {
137 const auto selected = db.finite ? input_b : input_a;
138 const auto& selected_decoded = db.finite ? db : da;
139 if (!selected_decoded.saturated
140 && (!self_conjugate || selected.imag() == SampleType{0})) {
141 out[ch][bin] = selected;
145 if (!da.finite) da = db;
146 if (!db.finite) db = da;
150 if (da.magnitude == SampleType{0} && db.magnitude > SampleType{0})
152 if (db.magnitude == SampleType{0} && da.magnitude > SampleType{0})
155 const SampleType magnitude = interpolate_magnitude(
156 da.magnitude, db.magnitude, magnitude_mix, config_.magnitude);
157 if (magnitude == SampleType{0}) {
161 if (self_conjugate) {
166 const SampleType selected_phase =
167 phase_mix < SampleType{0.5} ? da.phase : db.phase;
168 const SampleType sign = std::cos(selected_phase) < SampleType{0}
171 out[ch][bin] = {sign * magnitude, SampleType{0}};
174 const SampleType phase = interpolate_phase(
175 da.phase, db.phase, phase_mix, config_.phase);
176 out[ch][bin] = std::polar(magnitude, phase);
184 SampleType magnitude = SampleType{0};
185 SampleType phase = SampleType{0};
187 bool saturated =
false;
190 static constexpr SampleType pi() noexcept {
191 return static_cast<SampleType
>(3.14159265358979323846264338327950288L);
204 static DecodedBin decode(std::complex<SampleType> value)
noexcept {
205 if (!std::isfinite(
value.real()) || !std::isfinite(
value.imag()))
return {};
206 const SampleType real =
value.real();
207 const SampleType imag =
value.imag();
208 const SampleType
scale = std::max(std::abs(real), std::abs(imag));
209 if (scale == SampleType{0})
210 return {SampleType{0}, SampleType{0},
true,
false};
212 const SampleType scaled_magnitude = std::hypot(real / scale, imag / scale);
213 const SampleType
maximum = std::numeric_limits<SampleType>::max();
214 const bool saturated =
scale >
maximum / scaled_magnitude;
215 const SampleType magnitude = saturated
217 :
scale * scaled_magnitude;
218 return {magnitude, std::atan2(imag, real),
true, saturated};
221 static SampleType interpolate_magnitude(
222 SampleType
a, SampleType b, SampleType amount,
224 if (amount == SampleType{0})
return a;
225 if (amount == SampleType{1})
return b;
227 return std::fma(amount, b -
a,
a);
229 const SampleType result = std::hypot(
230 std::sqrt(SampleType{1} - amount) *
a,
231 std::sqrt(amount) *
b);
232 return std::isfinite(result)
234 : std::numeric_limits<SampleType>::max();
237 static SampleType shortest_phase(SampleType
a, SampleType b,
238 SampleType amount)
noexcept {
239 if (amount == SampleType{0})
return a;
240 if (amount == SampleType{1})
return b;
241 const SampleType delta = std::remainder(b -
a, SampleType{2} * pi());
242 return std::fma(amount, delta,
a);
245 static SampleType interpolate_phase(
246 SampleType
a, SampleType b, SampleType amount,
249 return shortest_phase(
a, b, amount);
250 if (amount == SampleType{0})
return a;
251 if (amount == SampleType{1})
return b;
253 const SampleType real = std::fma(amount, std::cos(b) - std::cos(
a),
255 const SampleType imag = std::fma(amount, std::sin(b) - std::sin(
a),
257 if (std::hypot(real, imag) <= std::numeric_limits<SampleType>::epsilon())
258 return shortest_phase(
a, b, amount);
259 return std::atan2(imag, real);
265 bool prepared_ =
false;
Definition spectral_morph.hpp:32
static bool supports_configuration(int channels, int num_bins) noexcept
Definition spectral_morph.hpp:46
bool set_config(Config config) noexcept
Definition spectral_morph.hpp:68
Config config() const noexcept
Definition spectral_morph.hpp:74
bool process_partition(const std::complex< SampleType > *const *a, const std::complex< SampleType > *const *b, std::complex< SampleType > *const *out, int channels, int first_bin, int bin_count, SampleType magnitude_amount, SampleType phase_amount) const noexcept
Morph [first_bin, first_bin + bin_count).
Definition spectral_morph.hpp:96
void reset() noexcept
Stateless lifecycle symmetry for frame-processing chains.
Definition spectral_morph.hpp:66
bool process(const std::complex< SampleType > *const *a, const std::complex< SampleType > *const *b, std::complex< SampleType > *const *out, int channels, int num_bins, SampleType magnitude_amount, SampleType phase_amount) const noexcept
Morph an entire coherent frame group.
Definition spectral_morph.hpp:83
int channels() const noexcept
Definition spectral_morph.hpp:76
static constexpr int maximum_bins
Definition spectral_morph.hpp:38
int num_bins() const noexcept
Definition spectral_morph.hpp:77
bool prepare(int channels, int num_bins) noexcept
Fix the coherent frame geometry.
Definition spectral_morph.hpp:56
bool prepared() const noexcept
Definition spectral_morph.hpp:75
static constexpr int maximum_channels
Definition spectral_morph.hpp:36
static constexpr int minimum_bins
Definition spectral_morph.hpp:37
@ b
Definition pitch.hpp:31
Definition additive_bank.hpp:218
SpectralMorphT< double > SpectralMorph64
Definition spectral_morph.hpp:269
SpectralMorphT< float > SpectralMorph
Definition spectral_morph.hpp:268
@ maximum
Definition spectrum_trace.hpp:34
@ a
Definition formant_filter_bank.hpp:20
@ linear
2-point. Cheapest; audible imaging when pitching up.
Definition granular.hpp:133
SpectralMagnitudeInterpolation
Definition spectral_morph.hpp:21
@ equal_power
Definition spectral_morph.hpp:23
@ linear
Definition spectral_morph.hpp:22
SpectralPhaseInterpolation
Definition spectral_morph.hpp:26
@ normalized_vector
Definition spectral_morph.hpp:28
@ shortest_arc
Definition spectral_morph.hpp:27
void scale(const float *a, float scalar, float *dst, std::size_t count) noexcept
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
Definition spectral_morph.hpp:40
SpectralMagnitudeInterpolation magnitude
Definition spectral_morph.hpp:41
SpectralPhaseInterpolation phase
Definition spectral_morph.hpp:43