Skip to content

Commit

Permalink
Cleanup in distortion module
Browse files Browse the repository at this point in the history
  • Loading branch information
xconverge committed Jan 26, 2025
1 parent 3a58cb6 commit c4eddcb
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 32 deletions.
90 changes: 58 additions & 32 deletions Software/GuitarPedal/Effect-Modules/distortion_module.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#include "distortion_module.h"
#include <q/fx/lowpass.hpp>
#include <q/fx/biquad.hpp>

using namespace bkshepherd;

static const char *s_clippingOptions[5] = {"Hard Clip", "Soft Clip", "Fuzz", "Tube", "Multi Stage"};

cycfi::q::highpass preFilter(40.0f, 48000);
cycfi::q::lowpass postFilter(8000.0f, 48000);
constexpr uint8_t oversamplingFactor = 32;

static const int s_paramCount = 6;
static const ParameterMetaData s_metaData[s_paramCount] = {
{
Expand Down Expand Up @@ -80,9 +84,27 @@ void DistortionModule::Init(float sample_rate) {
m_tone.Init(sample_rate);
// Set the frequency for the tilt pivot for the tone control
m_tone.SetFreq(1000.0f);

m_oversampling = GetParameterAsBool(5);
InitializeFilters();
}

void DistortionModule::ParameterChanged(int parameter_id) {}
void DistortionModule::InitializeFilters() {
preFilter.config(40.0f, GetSampleRate());

if (m_oversampling) {
postFilter.config(8000.0f, GetSampleRate() * oversamplingFactor);
} else {
postFilter.config(8000.0f, GetSampleRate());
}
}

void DistortionModule::ParameterChanged(int parameter_id) {
if (parameter_id == 5) {
m_oversampling = GetParameterAsBool(5);
InitializeFilters();
}
}

float hardClipping(float input, float threshold) {
if (input > threshold) {
Expand All @@ -97,37 +119,28 @@ float hardClipping(float input, float threshold) {
float softClipping(float input, float gain) { return std::tanh(input * gain); }

float fuzzEffect(float input, float intensity) {
// Pre-filter: High-pass to remove low-frequency content
static float prevInput = 0.0f;
float highPassed = input - prevInput;
prevInput = input;

// Symmetrical clipping with extreme compression
float fuzzed = std::tanh(highPassed * intensity);
float fuzzed = softClipping(input, intensity);

// Introduce a slight asymmetry for a classic fuzz character and adds harmonic content
fuzzed += 0.05f * std::sin(highPassed * 20.0f);
fuzzed += 0.05f * std::sin(input * 20.0f);

// Dynamic response: Adjust the intensity based on the input signal's amplitude
float dynamicIntensity = intensity * (1.0f + 0.5f * std::abs(input));
fuzzed = std::tanh(fuzzed * dynamicIntensity);
const float dynamicIntensity = intensity * (1.0f + 0.5f * std::abs(input));
fuzzed = softClipping(fuzzed, dynamicIntensity);

// Post-filter: Low-pass to smooth out harsh high frequencies
static float prevOutput = 0.0f;
float alpha = 0.1f; // Adjust alpha for desired smoothing
float smoothed = alpha * fuzzed + (1.0f - alpha) * prevOutput;
prevOutput = smoothed;

return smoothed;
return fuzzed;
}

float tubeSaturation(float input, float gain) { return std::atan(input * gain); }

float multiStage(float sample, float drive, float intensity) {
// First stage
const float stage1 = softClipping(sample, drive * intensity * 2.0f);

// Second stage
const float stage2 = softClipping(stage1, drive * intensity);

// Power amp, mimic second tube clipping, possibly negative feedback
const float result = tubeSaturation(stage2, drive * intensity);

Expand All @@ -137,13 +150,14 @@ float multiStage(float sample, float drive, float intensity) {
// Helper functions for oversampling
std::vector<float> upsample(const std::vector<float> &input, int factor, float sample_rate) {
std::vector<float> output(input.size() * factor, 0.0f);
cycfi::q::one_pole_lowpass lowpass_filter(sample_rate / (2.0f * factor));

for (size_t i = 0; i < input.size(); ++i) {
output[i * factor] = input[i]; // Insert input samples, leaving zeros in between
// Insert input samples, leaving zeros in between
output[i * factor] = input[i];
}

// Apply the one-pole low-pass filter to smooth interpolated samples
// Apply the low-pass filter to smooth interpolated samples
cycfi::q::lowpass lowpass_filter(sample_rate / (2.0f * static_cast<float>(factor)), sample_rate);
for (size_t i = 1; i < output.size(); ++i) {
output[i] = lowpass_filter(output[i]);
}
Expand All @@ -167,11 +181,11 @@ void processDistortion(float &sample, // Sample to process
sample *= gain;

switch (clippingType) {
case 0: // Soft Clipping
sample = softClipping(sample, gain);
case 0: // Hard Clipping
sample = hardClipping(sample, 1.0f - intensity);
break;
case 1: // Hard Clipping
sample = hardClipping(sample, 0.9f);
case 1: // Soft Clipping
sample = softClipping(sample, gain);
break;
case 2: // Fuzz
sample = fuzzEffect(sample, intensity * 10.0f);
Expand All @@ -186,45 +200,57 @@ void processDistortion(float &sample, // Sample to process
}

void DistortionModule::ProcessMono(float in) {
float distorted = in;

// Apply high-pass filter to remove excessive low frequencies
distorted = preFilter(distorted);

const float gain = m_gainMin + (GetParameterAsFloat(1) * (m_gainMax - m_gainMin));
const int clippingType = GetParameterAsBinnedValue(3) - 1;
const float intensity = GetParameterAsFloat(4);

float distorted = in;
if (GetParameterAsBool(5)) {
const uint8_t oversamplingFactor = 32;
if (m_oversampling) {
// Prepare signal for oversampling
std::vector<float> monoInput = {distorted};
std::vector<float> oversampledInput = upsample(monoInput, oversamplingFactor, GetSampleRate()); // 4x oversampling

// Apply gain and distortion processing
for (float &sample : oversampledInput) {
processDistortion(sample, gain, clippingType, intensity);

// Post-filter: Low-pass to smooth out harsh high frequencies
sample = postFilter(sample);
}

// Downsample back to original sample rate
const std::vector<float> downsampledOutput = downsample(oversampledInput, oversamplingFactor);
distorted = downsampledOutput[0];

// Apply gain compensation for oversampling
distorted *= oversamplingFactor;
} else {
processDistortion(distorted, gain, clippingType, intensity);

// Post-filter: Low-pass to smooth out harsh high frequencies
distorted = postFilter(distorted);
}

// Normalize the volume between the types of distortion
switch (clippingType) {
case 0:
distorted *= 0.8f;
distorted *= 1.4f;
break;
case 1:
distorted *= 1.0f;
distorted *= 0.8f;
break;
case 2:
distorted *= 2.8f;
distorted *= 1.0f;
break;
case 3:
distorted *= 0.9f;
break;
case 4:
distorted *= 0.3f;
distorted *= 0.5f;
break;
}

Expand Down
4 changes: 4 additions & 0 deletions Software/GuitarPedal/Effect-Modules/distortion_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@ class DistortionModule : public BaseEffectModule {

private:
float ProcessTiltToneControl(float in);
void InitializeFilters();

float m_levelMin = 0.0f;
float m_levelMax = 1.0f;

float m_gainMin = 1.0f;
float m_gainMax = 20.0f;

Tone m_tone;

bool m_oversampling;
};
} // namespace bkshepherd
#endif
Expand Down

0 comments on commit c4eddcb

Please sign in to comment.