Vital
Loading...
Searching...
No Matches
file_source.cpp
Go to the documentation of this file.
1/*
2Summary:
3FileSource is a wavetable component that constructs wavetables from an external audio file buffer. It supports various interpolation/fade styles for shaping the final waveform and different phase manipulation strategies (e.g., randomizing phases for a vocoder effect). FileSourceKeyframe encapsulates one particular configuration of start position, window fade, and style settings. Together, they enable flexible and creative wavetable generation from raw audio samples.
4 */
5
6#include "file_source.h"
7
24
26 const FileSourceKeyframe* source = dynamic_cast<const FileSourceKeyframe*>(keyframe);
27 VITAL_ASSERT(source);
28 start_position_ = source->start_position_;
29 window_fade_ = source->window_fade_;
30}
31
33 const WavetableKeyframe* to_keyframe,
34 float t) {
35 const FileSourceKeyframe* from = dynamic_cast<const FileSourceKeyframe*>(from_keyframe);
36 const FileSourceKeyframe* to = dynamic_cast<const FileSourceKeyframe*>(to_keyframe);
37 VITAL_ASSERT(from);
38 VITAL_ASSERT(to);
39
40 start_position_ = linearTween(from->start_position_, to->start_position_, t);
41 window_fade_ = linearTween(from->window_fade_, to->window_fade_, t);
42}
43
45 const float* buffer = getCubicInterpolationBuffer();
46 // Uses cubic interpolation to get a smooth sample value at a given position.
47 float clamped_position = vital::utils::clamp(position, 0.0f, sample_buffer_->size - 1);
48 int start_index = clamped_position;
49 float t = clamped_position - start_index;
50
52 vital::matrix value_matrix = vital::utils::getValueMatrix(buffer, start_index);
53 value_matrix.transpose();
54
55 return interpolation_matrix.multiplyAndSumRows(value_matrix)[0];
56}
57
59 // Finds max and min sample values to compute a scaling factor for normalization.
60 const float* buffer = getDataBuffer();
61 if (buffer == nullptr)
62 return 1.0f;
63
64 double cycles_in = start_position_ / window_size_;
65 int cycle = cycles_in;
66
67 double start_index = cycle * window_size_;
68
69 float max = 0.0f;
70 float min = 0.0f;
71 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i) {
72 double t = i / (vital::WaveFrame::kWaveformSize * 1.0);
73 double position = std::min<double>(start_index + t * window_size_, sample_buffer_->size - 1);
74 int from_index = position;
75 int to_index = std::min(sample_buffer_->size - 1, from_index + 1);
76
77 VITAL_ASSERT(from_index >= 0 && from_index < sample_buffer_->size);
78 VITAL_ASSERT(to_index >= 0 && to_index < sample_buffer_->size);
79
80 float from_sample = buffer[from_index];
81 float to_sample = buffer[to_index];
82 max = std::max(from_sample, max);
83 max = std::max(to_sample, max);
84 min = std::min(from_sample, min);
85 min = std::min(to_sample, min);
86 }
87
88 return 2.0f / std::max(max - min, 0.001f);
89}
90
92 if (sample_buffer_->size <= 0) {
93 wave_frame->clear();
94 return;
95 }
96
97 // Choose rendering method based on fade_style_ and apply phase_style_ if necessary
99 renderWaveBlend(wave_frame);
100 else if (fade_style_ == kNoInterpolate)
101 renderNoInterpolate(wave_frame);
102 else if (fade_style_ == kTimeInterpolate)
103 renderTimeInterpolate(wave_frame);
104 else if (fade_style_ == kFreqInterpolate)
105 renderFreqInterpolate(wave_frame);
106
108 // Override phases in frequency domain.
109 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i) {
110 float amplitude = std::abs(wave_frame->frequency_domain[i]);
111 wave_frame->frequency_domain[i] = std::polar(amplitude, overridden_phase_[i]);
112 }
113 }
114
115 wave_frame->toTimeDomain();
116}
117
118// Blend one segment of the wave with another using a windowed overlap technique.
120 double window_ratio = window_size_ / vital::WaveFrame::kWaveformSize;
121 int waveform_middle = vital::WaveFrame::kWaveformSize / 2;
122 int start_index = start_position_ / window_ratio + window_size_ / 2.0f + waveform_middle;
123 start_index = start_index % vital::WaveFrame::kWaveformSize;
124
125 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i) {
126 double t = i / (vital::WaveFrame::kWaveformSize * 1.0);
127 double position = start_position_ + t * window_size_;
128 int write_index = (start_index + i) % vital::WaveFrame::kWaveformSize;
129 wave_frame->time_domain[write_index] = getScaledInterpolatedSample(position);
130 }
131
132 int fade_samples = window_fade_ * vital::WaveFrame::kWaveformSize;
133 double fade_size = fade_samples * window_ratio;
134 for (int i = 0; i < fade_samples; ++i) {
135 double t = i / (fade_samples - 1.0f);
136 double fade = 0.5 + 0.5 * cos(vital::kPi * t);
137
138 int write_index = (start_index + i) % vital::WaveFrame::kWaveformSize;
139 double position = start_position_ + window_size_ + t * fade_size;
140 double existing_value = wave_frame->time_domain[write_index];
141 double fade_value = getScaledInterpolatedSample(position);
142 wave_frame->time_domain[write_index] = linearTween(existing_value, fade_value, fade);
143 }
144 wave_frame->toFrequencyDomain();
145}
146
147// Use a single cycle from the buffer without interpolation between cycles.
149 double cycles_in = start_position_ / window_size_;
150 int cycle = cycles_in;
151
152 double start_index = cycle * window_size_;
153
154 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i) {
155 double t = i / (vital::WaveFrame::kWaveformSize * 1.0);
156 double position = start_index + t * window_size_;
157 wave_frame->time_domain[i] = getScaledInterpolatedSample(position);
158 }
159
160 wave_frame->toFrequencyDomain();
161}
162
163// Interpolate between two adjacent cycles in time domain.
165 double cycles_in = start_position_ / window_size_;
166 int from_cycle = cycles_in;
167 int to_cycle = from_cycle + 1;
168 float transition = cycles_in - from_cycle;
169
170 double start_index_from = from_cycle * window_size_;
171 double start_index_to = to_cycle * window_size_;
172
173 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i) {
174 double t = i / (vital::WaveFrame::kWaveformSize * 1.0);
175 double from_position = start_index_from + t * window_size_;
176 double to_position = start_index_to + t * window_size_;
177 float from_sample = getScaledInterpolatedSample(from_position);
178 float to_sample = getScaledInterpolatedSample(to_position);
179 wave_frame->time_domain[i] = vital::utils::interpolate(from_sample, to_sample, transition);
180 }
181
182 wave_frame->toFrequencyDomain();
183}
184
185// Interpolate between cycles in frequency domain using two WaveSourceKeyframes.
187 double cycles_in = start_position_ / window_size_;
188 int from_cycle = cycles_in;
189 int to_cycle = from_cycle + 1;
190 float transition = cycles_in - from_cycle;
191
192 double start_index_from = from_cycle * window_size_;
193 double start_index_to = to_cycle * window_size_;
194
197 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i) {
198 double t = i / (vital::WaveFrame::kWaveformSize * 1.0);
199 double from_position = start_index_from + t * window_size_;
200 double to_position = start_index_to + t * window_size_;
201 from_wave_frame->time_domain[i] = getScaledInterpolatedSample(from_position);
202 to_wave_frame->time_domain[i] = getScaledInterpolatedSample(to_position);
203 }
204
205 from_wave_frame->toFrequencyDomain();
206 to_wave_frame->toFrequencyDomain();
207 interpolate_from_frame_->linearFrequencyInterpolate(from_wave_frame, to_wave_frame, transition);
209}
210
213 data["start_position"] = start_position_;
214 data["window_fade"] = window_fade_;
215 data["window_size"] = window_size_;
216 return data;
217}
218
221 start_position_ = data["start_position"];
222 window_fade_ = data["window_fade"];
223 window_size_ = data["window_size"];
224}
225
233
236 interpolate(keyframe, position);
237 return keyframe;
238}
239
259
263
264
266 double max_position = 0;
267 for (int i = 0; i < numFrames(); ++i)
268 max_position = std::max(max_position, getKeyframe(i)->getStartPosition());
269
270 // Serialize file source state: normalization, window size, fade style, audio data
272 data["normalize_gain"] = normalize_gain_;
273 data["normalize_mult"] = normalize_mult_;
274 data["window_size"] = window_size_;
275 data["fade_style"] = fade_style_;
276 data["phase_style"] = phase_style_;
277 data["random_seed"] = random_seed_;
278 data["audio_sample_rate"] = sample_buffer_.sample_rate;
279
280 int save_samples = max_position + 2 * window_size_ + kExtraSaveSamples;
281 int num_samples = std::min(sample_buffer_.size, save_samples);
282 String encoded = "";
283
284 // Base64-encode a subset of the audio samples for saving.
285 if (getDataBuffer()) {
286 std::unique_ptr<int16_t[]> pcm_data = std::make_unique<int16_t[]>(num_samples);
287 vital::utils::floatToPcmData(pcm_data.get(), getDataBuffer(), num_samples);
288 encoded = Base64::toBase64(pcm_data.get(), num_samples * sizeof(int16_t));
289 }
290 data["audio_file"] = encoded.toStdString();
291 return data;
292}
293
295 // Restore state from JSON, including loading the audio buffer from Base64-encoded data.
296 normalize_gain_ = data["normalize_gain"];
297 if (data.count("normalize_mult"))
298 normalize_mult_ = data["normalize_mult"];
299 else
300 normalize_mult_ = true;
301 window_size_ = data["window_size"];
303 if (data.count("fade_style"))
304 fade_style_ = data["fade_style"];
305
307 if (data.count("phase_style"))
308 phase_style_ = data["phase_style"];
309
310 if (data.count("random_seed"))
311 random_seed_ = data["random_seed"];
312
314
316
317 int sample_rate = vital::kDefaultSampleRate;
318 if (data.count("audio_sample_rate"))
319 sample_rate = data["audio_sample_rate"];
320
321 MemoryOutputStream decoded;
322 std::string audio_data = data["audio_file"];
323 Base64::convertFromBase64(decoded, audio_data);
324
325 // Convert PCM back to float and load it into sample_buffer_.
326 int size = static_cast<int>(decoded.getDataSize()) / sizeof(int16_t);
327 std::unique_ptr<float[]> float_data = std::make_unique<float[]>(size);
328 vital::utils::pcmToFloatData(float_data.get(), (int16_t*)decoded.getData(), size);
329 loadBuffer(float_data.get(), size, sample_rate);
330}
331
333 WavetableKeyframe* wavetable_keyframe = keyframes_[index].get();
334 return dynamic_cast<FileSource::FileSourceKeyframe*>(wavetable_keyframe);
335}
336
338 if (phase_style_ == phase_style)
339 return;
340
341 phase_style_ = phase_style;
342 if (phase_style_ == kVocode)
343 random_seed_++;
344
346}
347
349 // Sets overridden_phase_ based on the chosen phase style, e.g. random phases for vocoding.
350 if (phase_style_ == kClear) {
351 for (int i = 0; i < vital::WaveFrame::kWaveformSize / 2; ++i) {
352 overridden_phase_[2 * i] = -0.5f * vital::kPi;
353 overridden_phase_[2 * i + 1] = 0.5f * vital::kPi;
354 }
355 }
356 else if (phase_style_ == kVocode) {
358 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i)
360 }
361}
362
363void FileSource::loadBuffer(const float* buffer, int size, int sample_rate) {
364 sample_buffer_.sample_rate = sample_rate;
365 sample_buffer_.size = size;
366 sample_buffer_.data = std::make_unique<float[]>(size + kExtraBufferSamples);
367 memcpy(sample_buffer_.data.get() + 1, buffer, size * sizeof(float));
368
370
371 // Duplicate samples at edges for safe interpolation.
372 for (int i = 1; i < kExtraBufferSamples; ++i)
374}
375
376void FileSource::detectPitch(int max_period) {
377 // Use PitchDetector to guess a suitable window size from pitch analysis.
378 int start = (sample_buffer_.size - kPitchDetectMaxPeriod) / 3;
380 float period = pitch_detector_.matchPeriod(max_period);
381 setWindowSize(period);
382}
383
385 // Check if audio matches a WaveEdit format and adjust window size accordingly.
386 static constexpr int kWaveEditFrameLength = 256;
387 static constexpr int kFrequencyDomainTotals = 8;
388 static constexpr int kWaveEditNumFrames = 64;
389 if (sample_buffer_.size != kWaveEditFrameLength * kWaveEditNumFrames)
390 return;
391
392 vital::WaveFrame wave_frame;
393 const float* buffer = getDataBuffer();
394 for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i)
395 wave_frame.time_domain[i] = buffer[i];
396
397 wave_frame.toFrequencyDomain();
398
399 int size_mult = vital::WaveFrame::kWaveformSize / kWaveEditFrameLength;
400 std::unique_ptr<float[]> totals = std::make_unique<float[]>(size_mult);
401 for (int i = 0; i < size_mult; ++i) {
402 for (int j = 0; j < kFrequencyDomainTotals; ++j)
403 totals[i] += std::abs(wave_frame.frequency_domain[i + 1 + j * size_mult]);
404 }
405
406 for (int i = 0; i < size_mult - 1; ++i) {
407 if (totals[i] > totals[size_mult - 1])
408 return;
409 }
410
411 setWindowSize(kWaveEditFrameLength);
412}
A specific WavetableKeyframe that uses the FileSource’s audio buffer.
Definition file_source.h:74
void renderWaveBlend(vital::WaveFrame *wave_frame)
Definition file_source.cpp:119
force_inline void setFadeStyle(FadeStyle fade_style)
Definition file_source.h:122
void setInterpolateFromFrame(WaveSourceKeyframe *frame)
Definition file_source.h:138
FileSourceKeyframe(SampleBuffer *sample_buffer)
Constructs a keyframe tied to a given SampleBuffer.
Definition file_source.cpp:13
float getNormalizationScale()
Computes the normalization scale factor for the current wave segment.
Definition file_source.cpp:58
double window_size_
Definition file_source.h:154
double window_fade_
Definition file_source.h:153
PhaseStyle phase_style_
Definition file_source.h:156
void setInterpolateToFrame(WaveSourceKeyframe *frame)
Definition file_source.h:142
void render(vital::WaveFrame *wave_frame) override
Renders the waveform of this keyframe into a WaveFrame.
Definition file_source.cpp:91
FadeStyle fade_style_
Definition file_source.h:155
const float * overridden_phase_
Definition file_source.h:148
double start_position_
Definition file_source.h:152
void renderFreqInterpolate(vital::WaveFrame *wave_frame)
Definition file_source.cpp:186
void jsonToState(json data) override
Restores the keyframe's state from a JSON object.
Definition file_source.cpp:219
WaveSourceKeyframe * interpolate_from_frame_
Definition file_source.h:149
void renderTimeInterpolate(vital::WaveFrame *wave_frame)
Definition file_source.cpp:164
WaveSourceKeyframe * interpolate_to_frame_
Definition file_source.h:150
float getScaledInterpolatedSample(float time)
Definition file_source.cpp:44
force_inline void setPhaseStyle(PhaseStyle phase_style)
Definition file_source.h:123
void copy(const WavetableKeyframe *keyframe) override
Copies the state from another keyframe of the same type.
Definition file_source.cpp:25
void renderNoInterpolate(vital::WaveFrame *wave_frame)
Definition file_source.cpp:148
force_inline void setOverriddenPhaseBuffer(const float *buffer)
Definition file_source.h:124
json stateToJson() override
Serializes the state of this keyframe to a JSON object.
Definition file_source.cpp:211
void interpolate(const WavetableKeyframe *from_keyframe, const WavetableKeyframe *to_keyframe, float t) override
Linearly interpolates between two keyframes.
Definition file_source.cpp:32
SampleBuffer * sample_buffer_
Definition file_source.h:147
force_inline void setWindowSize(double window_size)
Definition file_source.h:121
FadeStyle fade_style_
Definition file_source.h:218
int random_seed_
Definition file_source.h:224
FileSourceKeyframe compute_frame_
Definition file_source.h:212
PhaseStyle phase_style_
Definition file_source.h:219
json stateToJson() override
Serializes the component’s state and all keyframes to a JSON object.
Definition file_source.cpp:265
void loadBuffer(const float *buffer, int size, int sample_rate)
Loads audio data into the file source buffer.
Definition file_source.cpp:363
force_inline const float * getDataBuffer()
Definition file_source.h:204
force_inline const float * getCubicInterpolationBuffer()
Definition file_source.h:209
void detectPitch(int max_period=vital::WaveFrame::kWaveformSize)
Attempts to detect pitch in the loaded sample to determine window size automatically.
Definition file_source.cpp:376
bool normalize_mult_
Definition file_source.h:221
@ kFreqInterpolate
Interpolate in frequency domain between cycles.
Definition file_source.h:42
@ kTimeInterpolate
Interpolate in time domain between cycles.
Definition file_source.h:41
@ kWaveBlend
Blend windowed segments into each other.
Definition file_source.h:39
@ kNoInterpolate
Use a single segment, no blending.
Definition file_source.h:40
void render(vital::WaveFrame *wave_frame, float position) override
Renders the waveform at a given position into a WaveFrame.
Definition file_source.cpp:240
WaveSourceKeyframe interpolate_from_frame_
Definition file_source.h:213
bool normalize_gain_
Definition file_source.h:220
static constexpr int kExtraSaveSamples
Extra samples saved for safe interpolation and boundary conditions.
Definition file_source.h:28
WaveSourceKeyframe interpolate_to_frame_
Definition file_source.h:214
WavetableComponentFactory::ComponentType getType() override
Returns the type of this WavetableComponent.
Definition file_source.cpp:260
void setPhaseStyle(PhaseStyle phase_style)
Definition file_source.cpp:337
vital::utils::RandomGenerator random_generator_
Definition file_source.h:225
SampleBuffer sample_buffer_
Definition file_source.h:216
void writePhaseOverrideBuffer()
Definition file_source.cpp:348
void detectWaveEditTable()
Detects if the source audio can form a WaveEdit-style wavetable (special format).
Definition file_source.cpp:384
const SampleBuffer * buffer() const
Definition file_source.h:171
PitchDetector pitch_detector_
Definition file_source.h:226
float overridden_phase_[vital::WaveFrame::kWaveformSize]
Definition file_source.h:217
static constexpr int kExtraBufferSamples
Additional buffer samples for safe reading beyond boundaries.
Definition file_source.h:30
static constexpr int kPitchDetectMaxPeriod
Maximum period for pitch detection to limit CPU usage.
Definition file_source.h:32
double window_size_
Definition file_source.h:222
WavetableKeyframe * createKeyframe(int position) override
Creates a new keyframe at a given position.
Definition file_source.cpp:234
void setWindowSize(double window_size)
Definition file_source.h:177
FileSourceKeyframe * getKeyframe(int index)
Definition file_source.cpp:332
FileSource()
Definition file_source.cpp:226
void jsonToState(json data) override
Restores the component’s state from a JSON object.
Definition file_source.cpp:294
PhaseStyle
Methods for handling phase information in the transformed wave.
Definition file_source.h:50
@ kNone
Keep phases as-is.
Definition file_source.h:51
@ kVocode
Assign random phases for vocoding-like effect.
Definition file_source.h:53
@ kClear
Clear phases to a known pattern.
Definition file_source.h:52
float matchPeriod(int max_period)
High-level method to find the best matching period using the YIN approach.
Definition pitch_detector.cpp:95
void loadSignal(const float *signal, int size)
Loads a signal into the PitchDetector for analysis.
Definition pitch_detector.cpp:17
void linearFrequencyInterpolate(const vital::WaveFrame *from, const vital::WaveFrame *to, float t)
Linear interpolation in frequency domain.
Definition wave_source.cpp:82
vital::WaveFrame * wave_frame()
Provides direct access to the stored WaveFrame.
Definition wave_source.h:107
ComponentType
Enumerates all known WavetableComponents, including sources and modifiers.
Definition wavetable_component_factory.h:28
@ kFileSource
A file-based audio source.
Definition wavetable_component_factory.h:31
int numFrames() const
Gets the number of keyframes.
Definition wavetable_component.h:155
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
void copy(const WaveFrame *other)
Copies another WaveFrame's time and frequency domain data into this one.
Definition wave_frame.cpp:57
void setSampleRate(float rate)
Sets the sample rate for this wave frame.
Definition wave_frame.h:73
void setFrequencyRatio(float ratio)
Sets the frequency ratio for this wave frame.
Definition wave_frame.h:66
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
void toTimeDomain()
Converts the currently loaded frequency-domain data into time-domain representation.
Definition wave_frame.cpp:77
void normalize(bool allow_positive_gain=false)
Normalizes the time-domain waveform samples to have a maximum absolute value of 1....
Definition wave_frame.cpp:38
void clear()
Clears the waveform data, resetting it to default states.
Definition wave_frame.cpp:7
force_inline mono_float next()
Returns the next random float in [min, max].
Definition utils.h:85
force_inline void seed(int new_seed)
Reseeds the internal random engine with new_seed.
Definition utils.h:133
#define VITAL_ASSERT(x)
Definition common.h:11
#define force_inline
Definition common.h:23
nlohmann::json json
Definition line_generator.h:7
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
void floatToPcmData(int16_t *pcm_data, const float *float_data, int size)
Converts floating-point audio data to 16-bit PCM data.
Definition utils.cpp:77
force_inline matrix getCatmullInterpolationMatrix(poly_float t)
Creates a Catmull-Rom interpolation matrix from a poly_float t.
Definition poly_utils.h:227
force_inline matrix getValueMatrix(const mono_float *buffer, poly_int indices)
Creates a matrix of 4 poly_float lanes from a single buffer at varying indices.
Definition poly_utils.h:262
void pcmToFloatData(float *float_data, const int16_t *pcm_data, int size)
Converts 16-bit PCM data to floating-point audio data.
Definition utils.cpp:94
force_inline poly_float interpolate(poly_float from, poly_float to, mono_float t)
Performs a linear interpolation between two poly_floats using a scalar t in [0..1].
Definition poly_utils.h:182
Contains classes and functions used within the Vital synthesizer framework.
constexpr int kDefaultSampleRate
Default sample rate in Hz.
Definition common.h:41
constexpr mono_float kPi
Pi constant.
Definition common.h:36
A simple structure holding a buffer of samples loaded from the file source.
Definition file_source.h:60
int size
Number of samples in the buffer.
Definition file_source.h:63
int sample_rate
Sample rate of the audio data.
Definition file_source.h:64
std::unique_ptr< float[]> data
Pointer to raw audio data.
Definition file_source.h:62
A structure representing a 4x1 matrix of poly_float rows.
Definition matrix.h:19
force_inline poly_float multiplyAndSumRows(const matrix &other)
Multiplies and sums corresponding rows of this matrix with another matrix.
Definition matrix.h:93
force_inline void transpose()
Transposes the matrix in-place.
Definition matrix.h:44