Vital
Loading...
Searching...
No Matches
distortion.cpp
Go to the documentation of this file.
1#include "distortion.h"
2
3#include "synth_constants.h"
4
5#include <climits>
6
7namespace vital {
8
9 namespace {
20 force_inline poly_float linearFold(poly_float value, poly_float drive) {
21 poly_float adjust = value * drive * 0.25f + 0.75f;
22 poly_float range = utils::mod(adjust);
23 return poly_float::abs(range * -4.0f + 2.0f) - 1.0f;
24 }
25
35 force_inline poly_float sinFold(poly_float value, poly_float drive) {
36 poly_float adjust = value * drive * -0.25f + 0.5f;
37 poly_float range = utils::mod(adjust);
38 return futils::sin1(range);
39 }
40
48 force_inline poly_float softClip(poly_float value, poly_float drive) {
49 return futils::tanh(value * drive);
50 }
51
59 force_inline poly_float hardClip(poly_float value, poly_float drive) {
60 return utils::clamp(value * drive, -1.0f, 1.0f);
61 }
62
72 force_inline poly_float bitCrush(poly_float value, poly_float drive) {
73 return utils::round(value / drive) * drive;
74 }
75
87 force_inline int compactAudio(poly_float* audio_out, const poly_float* audio_in, int num_samples) {
88 int num_full = num_samples / 2;
89 for (int i = 0; i < num_full; ++i) {
90 int in_index = 2 * i;
91 audio_out[i] = utils::compactFirstVoices(audio_in[in_index], audio_in[in_index + 1]);
92 }
93
94 int num_remaining = num_samples % 2;
95
96 if (num_remaining)
97 audio_out[num_full] = audio_in[num_samples - 1];
98
99 return num_full + num_remaining;
100 }
101
112 force_inline void expandAudio(poly_float* audio_out, const poly_float* audio_in, int num_samples) {
113 int num_full = num_samples / 2;
114 if (num_samples % 2)
115 audio_out[num_samples - 1] = audio_in[num_full];
116
117 for (int i = num_full - 1; i >= 0; --i) {
118 int out_index = 2 * i;
119 audio_out[out_index] = audio_in[i];
120 audio_out[out_index + 1] = utils::swapVoices(audio_in[i]);
121 }
122 }
123 } // namespace
124
136 switch(type) {
137 case kSoftClip:
138 return softClip(value, drive);
139 case kHardClip:
140 return hardClip(value, drive);
141 case kLinearFold:
142 return linearFold(value, drive);
143 case kSinFold:
144 return sinFold(value, drive);
145 case kBitCrush:
146 return bitCrush(value, drive);
147 case kDownSample:
148 return bitCrush(value, poly_float(1.001f) - poly_float(kPeriodScale) / drive);
149 default:
150 return value;
151 }
152 }
153
160 Distortion::Distortion() : Processor(kNumInputs, kNumOutputs),
161 last_distorted_value_(0.0f), current_samples_(0.0f), type_(kNumTypes) { }
162
176 template<poly_float(*distort)(poly_float, poly_float), poly_float(*scale)(poly_float)>
177 void Distortion::processTimeInvariant(int num_samples, const poly_float* audio_in, const poly_float* drive,
178 poly_float* audio_out) {
179 for (int i = 0; i < num_samples; ++i) {
180 // Convert drive in dB to a linear scale appropriate for this distortion
181 poly_float current_drive = scale(drive[i]);
182 poly_float sample = audio_in[i];
183 audio_out[i] = distort(sample, current_drive);
184 VITAL_ASSERT(utils::isContained(audio_out[i]));
185 }
186 }
187
188 void Distortion::processDownSample(int num_samples, const poly_float* audio_in, const poly_float* drive,
189 poly_float* audio_out) {
190 mono_float sample_rate = getSampleRate();
191 poly_float current_samples = current_samples_;
192
193 for (int i = 0; i < num_samples; ++i) {
194 poly_float current_period = downSampleScale(drive[i]) * sample_rate;
195 current_samples += 1.0f;
196
197 poly_float current_sample = audio_in[i];
198 poly_float current_downsample = current_sample & constants::kFirstMask;
199 current_downsample += utils::swapVoices(current_downsample);
200
201 poly_mask update = poly_float::greaterThanOrEqual(current_samples, current_period);
202 last_distorted_value_ = utils::maskLoad(last_distorted_value_, current_downsample, update);
203 current_samples = utils::maskLoad(current_samples, current_samples - current_period, update);
204 audio_out[i] = last_distorted_value_;
205 }
206
207 current_samples_ = current_samples;
208 }
209
210 void Distortion::processWithInput(const poly_float* audio_in, int num_samples) {
212
213 int type = static_cast<int>(input(kType)->at(0)[0]);
214 poly_float* audio_out = output(kAudioOut)->buffer;
215 poly_float* drive_out = output(kDriveOut)->buffer;
216
217 int compact_samples = compactAudio(audio_out, audio_in, num_samples);
218 compactAudio(drive_out, input(kDrive)->source->buffer, num_samples);
219
220 if (type != type_) {
221 type_ = type;
222 last_distorted_value_ = 0.0f;
223 current_samples_ = 0.0f;
224 }
225
226 switch(type) {
227 case kSoftClip:
228 processTimeInvariant<softClip, driveDbScale>(compact_samples, audio_out, drive_out, audio_out);
229 break;
230 case kHardClip:
231 processTimeInvariant<hardClip, driveDbScale>(compact_samples, audio_out, drive_out, audio_out);
232 break;
233 case kLinearFold:
234 processTimeInvariant<linearFold, driveDbScale>(compact_samples, audio_out, drive_out, audio_out);
235 break;
236 case kSinFold:
237 processTimeInvariant<sinFold, driveDbScale>(compact_samples, audio_out, drive_out, audio_out);
238 break;
239 case kBitCrush:
240 processTimeInvariant<bitCrush, bitCrushScale>(compact_samples, audio_out, drive_out, audio_out);
241 break;
242 case kDownSample:
243 processDownSample(compact_samples, audio_out, drive_out, audio_out);
244 break;
245 default:
246 utils::copyBuffer(audio_out, audio_in, num_samples);
247 return;
248 }
249
250 expandAudio(audio_out, audio_out, num_samples);
251 }
252
253 void Distortion::process(int num_samples) {
255 processWithInput(input(kAudio)->source->buffer, num_samples);
256 }
257} // namespace vital
static force_inline poly_float downSampleScale(poly_float db)
Scales a drive (dB) value for downsampling distortion.
Definition distortion.h:104
virtual void processWithInput(const poly_float *audio_in, int num_samples) override
Processes a block of audio using a provided input buffer.
Definition distortion.cpp:210
@ kAudio
Audio input buffer.
Definition distortion.h:43
@ kType
Distortion type (see Type enum)
Definition distortion.h:44
@ kDrive
Drive amount in dB.
Definition distortion.h:45
virtual void process(int num_samples) override
Processes a block of audio using the stored input buffer.
Definition distortion.cpp:253
static constexpr float kPeriodScale
Factor used to scale downsampling period relative to sample rate.
Definition distortion.h:31
static poly_float getDrivenValue(int type, poly_float value, poly_float drive)
Applies the specified distortion to a single sample given the drive multiplier.
Definition distortion.cpp:135
void processTimeInvariant(int num_samples, const poly_float *audio_in, const poly_float *drive, poly_float *audio_out)
Processes samples with a time-invariant distortion function (no dynamic changes).
Definition distortion.cpp:177
@ kDriveOut
Drive values used in calculations (for reference or debugging)
Definition distortion.h:55
@ kAudioOut
Distorted audio output.
Definition distortion.h:54
void processDownSample(int num_samples, const poly_float *audio_in, const poly_float *drive, poly_float *audio_out)
Processes samples using a downsampling approach for distortion.
Definition distortion.cpp:188
Distortion()
Constructs a Distortion object with the default number of inputs/outputs.
Definition distortion.cpp:160
@ kSinFold
Sine-based waveform folding.
Definition distortion.h:67
@ kSoftClip
Soft clipping (tanh-based waveshaping)
Definition distortion.h:64
@ kHardClip
Hard clipping.
Definition distortion.h:65
@ kLinearFold
Linear waveform folding.
Definition distortion.h:66
@ kBitCrush
Bitcrushing (quantizing samples)
Definition distortion.h:68
@ kDownSample
Downsampling.
Definition distortion.h:69
Base class for all signal-processing units in Vital.
Definition processor.h:212
force_inline Input * input(unsigned int index=0) const
Retrieves the Input pointer at a given index.
Definition processor.h:587
force_inline int getSampleRate() const
Retrieves the current (effective) sample rate.
Definition processor.h:326
bool inputMatchesBufferSize(int input=0)
Checks whether the buffer size of a particular input matches the size needed by this Processor.
Definition processor.cpp:42
bool checkInputAndOutputSize(int num_samples)
Checks if all inputs and outputs have buffers big enough for num_samples.
Definition processor.cpp:52
force_inline Output * output(unsigned int index=0) const
Retrieves the Output pointer at a given index.
Definition processor.h:616
#define VITAL_ASSERT(x)
Definition common.h:11
#define force_inline
Definition common.h:23
const poly_mask kFirstMask
A mask identifying the first voice slots in a polyphonic vector.
Definition synth_constants.h:266
force_inline poly_float tanh(poly_float value)
Approximates tanh function using a complex polynomial.
Definition futils.h:347
force_inline mono_float sin1(mono_float phase)
Definition futils.h:424
force_inline poly_float mod(poly_float value)
Returns the fractional part of each lane by subtracting the floored value.
Definition poly_utils.h:814
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
force_inline poly_float round(poly_float value)
Rounds each lane to the nearest integer as a poly_float.
Definition poly_utils.h:807
force_inline bool isContained(poly_float value)
Checks if all lanes in a poly_float are within a broad range [-8000..8000].
Definition poly_utils.h:631
force_inline poly_float swapVoices(poly_float value)
Swaps the first half of the lanes with the second half.
Definition poly_utils.h:437
force_inline void copyBuffer(mono_float *dest, const mono_float *source, int size)
Copies data from a source mono buffer to a destination mono buffer.
Definition poly_utils.h:586
force_inline poly_float maskLoad(poly_float zero_value, poly_float one_value, poly_mask reset_mask)
Selects between two values (zero_value or one_value) based on a mask in each lane.
Definition poly_utils.h:351
force_inline poly_float compactFirstVoices(poly_float one, poly_float two)
Packs the first voice from two different poly_floats into a single vector.
Definition poly_utils.h:504
Contains classes and functions used within the Vital synthesizer framework.
float mono_float
Definition common.h:33
force_inline poly_float at(int i) const
Returns the sample at index i from the source buffer.
Definition processor.h:141
poly_float * buffer
Pointer to the output buffer.
Definition processor.h:110
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
static force_inline mask_simd_type vector_call greaterThanOrEqual(simd_type one, simd_type two)
Compares two SIMD float registers, element-wise, for greater than or equal.
Definition poly_values.h:987
static force_inline simd_type vector_call abs(simd_type value)
Computes the absolute value of each element in the SIMD float register.
Definition poly_values.h:935
Represents a vector of integer values using SIMD instructions.
Definition poly_values.h:56