Vital
Loading...
Searching...
No Matches
wave_warp_modifier.cpp
Go to the documentation of this file.
1/*
2Summary:
3WaveWarpModifier allows nonlinear reshaping of a waveform by warping it horizontally (time-axis) and vertically (amplitude-axis). Users can set warp powers and choose asymmetric transformations, creating a wide range of waveform distortions. Interpolation between keyframes and these nonlinear transformations can produce evolving, dynamic waves that move beyond simple linear scaling.
4 */
5
7
8#include "futils.h"
9#include "wave_frame.h"
10#include "utils.h"
11
12namespace {
20 inline double highResPowerScale(float value, float power) {
21 static constexpr float kMinPower = 0.01f;
22 if (fabsf(power) < kMinPower)
23 return value;
24
25 double abs_value = fabsf(value);
26
27 double numerator = exp(power * abs_value) - 1.0f;
28 double denominator = exp(power) - 1.0f;
29 if (value >= 0.0f)
30 return numerator / denominator;
31 return -numerator / denominator;
32 }
33}
34
41
43 const WaveWarpModifierKeyframe* source = dynamic_cast<const WaveWarpModifierKeyframe*>(keyframe);
44 horizontal_power_ = source->horizontal_power_;
45 vertical_power_ = source->vertical_power_;
46}
47
49 const WavetableKeyframe* to_keyframe,
50 float t) {
51 // Linearly interpolate horizontal and vertical warp powers.
52 const WaveWarpModifierKeyframe* from = dynamic_cast<const WaveWarpModifierKeyframe*>(from_keyframe);
53 const WaveWarpModifierKeyframe* to = dynamic_cast<const WaveWarpModifierKeyframe*>(to_keyframe);
54
55 horizontal_power_ = linearTween(from->horizontal_power_, to->horizontal_power_, t);
56 vertical_power_ = linearTween(from->vertical_power_, to->vertical_power_, t);
57}
58
60 // Initially copy time_domain into frequency_domain as a buffer.
61 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i)
62 wave_frame->frequency_domain[i] = wave_frame->time_domain[i];
63
64 // Warp horizontally: map each sample index through a nonlinear curve controlled by horizontal_power_.
65 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i) {
66 float horizontal = i / (vital::WaveFrame::kWaveformSize - 1.0f);
67 float warped_horizontal = 0.0f;
69 warped_horizontal = highResPowerScale(horizontal, horizontal_power_);
70 }
71 else {
72 // Symmetric mapping: map [-1,1] and then re-map back to [0,1].
73 warped_horizontal = 0.5f * highResPowerScale(2.0f * horizontal - 1.0f, horizontal_power_) + 0.5f;
74 }
75
76 // Compute new sample by interpolating the original wave at this warped horizontal position.
77 float float_index = (vital::WaveFrame::kWaveformSize - 1) * warped_horizontal;
78 int index = float_index;
80
81 float vertical_from = wave_frame->frequency_domain[index].real();
82 float vertical_to = wave_frame->frequency_domain[index + 1].real();
83 float vertical = linearTween(vertical_from, vertical_to, float_index - index);
84 vertical = vital::utils::clamp(vertical, -1.0f, 1.0f);
85
86 // Warp vertically: apply vertical_power_ using highResPowerScale.
88 wave_frame->time_domain[i] = 2.0f * highResPowerScale(0.5f * vertical + 0.5f, vertical_power_) - 1.0f;
89 else
90 wave_frame->time_domain[i] = highResPowerScale(vertical, vertical_power_);
91 }
92 wave_frame->toFrequencyDomain();
93}
94
97 data["horizontal_power"] = horizontal_power_;
98 data["vertical_power"] = vertical_power_;
99 return data;
100}
101
104 horizontal_power_ = data["horizontal_power"];
105 vertical_power_ = data["vertical_power"];
106}
107
110 interpolate(keyframe, position);
111 return keyframe;
112}
113
114void WaveWarpModifier::render(vital::WaveFrame* wave_frame, float position) {
115 // Interpolate and apply current asymmetric flags to the compute_frame_ before rendering.
116 interpolate(&compute_frame_, position);
119 compute_frame_.render(wave_frame);
120}
121
125
128 data["horizontal_asymmetric"] = horizontal_asymmetric_;
129 data["vertical_asymmetric"] = vertical_asymmetric_;
130 return data;
131}
132
135 horizontal_asymmetric_ = data["horizontal_asymmetric"];
136 vertical_asymmetric_ = data["vertical_asymmetric"];
137}
138
140 WavetableKeyframe* wavetable_keyframe = keyframes_[index].get();
141 return dynamic_cast<WaveWarpModifier::WaveWarpModifierKeyframe*>(wavetable_keyframe);
142}
A keyframe class holding horizontal and vertical warp parameters at a given position.
Definition wave_warp_modifier.h:35
float vertical_power_
Controls vertical (amplitude-axis) warping.
Definition wave_warp_modifier.h:96
bool vertical_asymmetric_
If true, vertical warping is asymmetric.
Definition wave_warp_modifier.h:99
float horizontal_power_
Controls horizontal (time-axis) warping.
Definition wave_warp_modifier.h:95
WaveWarpModifierKeyframe()
Constructs a WaveWarpModifierKeyframe with default warp powers and symmetric warping.
Definition wave_warp_modifier.cpp:35
void interpolate(const WavetableKeyframe *from_keyframe, const WavetableKeyframe *to_keyframe, float t) override
Linearly interpolates between two keyframes.
Definition wave_warp_modifier.cpp:48
void render(vital::WaveFrame *wave_frame) override
Renders the waveform of this keyframe into a WaveFrame.
Definition wave_warp_modifier.cpp:59
void jsonToState(json data) override
Restores the keyframe's state from a JSON object.
Definition wave_warp_modifier.cpp:102
void setVerticalAsymmetric(bool vertical_asymmetric)
Sets whether vertical warping is asymmetric.
Definition wave_warp_modifier.h:92
void copy(const WavetableKeyframe *keyframe) override
Copies the state from another keyframe of the same type.
Definition wave_warp_modifier.cpp:42
void setHorizontalAsymmetric(bool horizontal_asymmetric)
Sets whether horizontal warping is asymmetric.
Definition wave_warp_modifier.h:85
json stateToJson() override
Serializes the state of this keyframe to a JSON object.
Definition wave_warp_modifier.cpp:95
bool horizontal_asymmetric_
If true, horizontal warping is asymmetric.
Definition wave_warp_modifier.h:98
WaveWarpModifierKeyframe compute_frame_
Keyframe used for intermediate computation.
Definition wave_warp_modifier.h:153
WavetableKeyframe * createKeyframe(int position) override
Creates a new keyframe at a given position.
Definition wave_warp_modifier.cpp:108
bool horizontal_asymmetric_
Controls horizontal warping symmetry.
Definition wave_warp_modifier.h:154
void render(vital::WaveFrame *wave_frame, float position) override
Renders the waveform at a given position into a WaveFrame.
Definition wave_warp_modifier.cpp:114
json stateToJson() override
Serializes the component’s state and all keyframes to a JSON object.
Definition wave_warp_modifier.cpp:126
void jsonToState(json data) override
Restores the component’s state from a JSON object.
Definition wave_warp_modifier.cpp:133
bool vertical_asymmetric_
Controls vertical warping symmetry.
Definition wave_warp_modifier.h:155
WavetableComponentFactory::ComponentType getType() override
Returns the type of this WavetableComponent.
Definition wave_warp_modifier.cpp:122
WaveWarpModifierKeyframe * getKeyframe(int index)
Retrieves a WaveWarpModifierKeyframe by index.
Definition wave_warp_modifier.cpp:139
ComponentType
Enumerates all known WavetableComponents, including sources and modifiers.
Definition wavetable_component_factory.h:28
@ kWaveWarp
Modifier that warps the waveform.
Definition wavetable_component_factory.h:41
virtual json stateToJson()
Serializes the component’s state and all keyframes to a JSON object.
Definition wavetable_component.cpp:49
void interpolate(WavetableKeyframe *dest, float position)
Interpolates a destination keyframe at a given position.
Definition wavetable_component.cpp:68
virtual void jsonToState(json data)
Restores the component’s state from a JSON object.
Definition wavetable_component.cpp:37
std::vector< std::unique_ptr< WavetableKeyframe > > keyframes_
The list of keyframes sorted by position.
Definition wavetable_component.h:219
Represents a single state of a waveform at a specific position in a wavetable.
Definition wavetable_keyframe.h:35
virtual json stateToJson()
Serializes the state of this keyframe to a JSON object.
Definition wavetable_keyframe.cpp:37
virtual void jsonToState(json data)
Restores the keyframe's state from a JSON object.
Definition wavetable_keyframe.cpp:41
Represents a single frame of a wavetable, containing both time-domain and frequency-domain data.
Definition wave_frame.h:16
std::complex< float > frequency_domain[kWaveformSize]
The frequency-domain representation (complex spectrum).
Definition wave_frame.h:125
void toFrequencyDomain()
Converts the currently loaded time-domain data into frequency-domain representation.
Definition wave_frame.cpp:64
static constexpr int kWaveformSize
The size of the waveform (number of samples per frame).
Definition wave_frame.h:21
mono_float time_domain[2 *kWaveformSize]
The time-domain data, extended buffer size for FFT alignment.
Definition wave_frame.h:124
Contains faster but less accurate versions of utility math functions, such as exponential,...
nlohmann::json json
Definition line_generator.h:7
force_inline mono_float exp(mono_float exponent)
Definition futils.h:132
force_inline int iclamp(int value, int min_val, int max_val)
Clamps an integer between [min_val, max_val].
Definition utils.h:250
force_inline poly_float clamp(poly_float value, mono_float min, mono_float max)
Clamps each lane of a vector to [min, max].
Definition poly_utils.h:306
Provides various utility functions, classes, and constants for audio, math, and general-purpose opera...