Skip to content

Commit

Permalink
Starting work on Fuzz Machine module
Browse files Browse the repository at this point in the history
  • Loading branch information
jatinchowdhury18 committed Jul 5, 2023
1 parent d2639c7 commit 1e94959
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 0 deletions.
72 changes: 72 additions & 0 deletions sim/FuzzFace/fuzz_face_dfl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# %%
import numpy as np
import matplotlib.pyplot as plt

# %%
FS = 48000
fc = 100
N = 20000

x = 0.125 * np.sin(2 * np.pi * np.arange(N) * fc / FS)
plt.plot(x)

# %%
def process(x, gain_01):
gr4 = 1 / 100.0e3
gain_factor = (gain_01 * 0.98 + 0.01)**5
grfp = 1 / ((1 - gain_factor) * 1.0e3)
grfm = 1 / (gain_factor * 1.0e3)
gc1 = 2 * FS * 2.2e-6
gc2 = 2 * FS * 20.0e-6

vt = 0.026 * 0.5
Iq = 0.5e-10

y = np.zeros_like(x)

ic1eq = 0
ic2eq = 0

vo = 0
io = 0
for n in range(len(x)):
vi = x[n]

# initial guess
v1 = -((-gr4*(grfp*ic2eq+(gc2+grfm+grfp)*io) + (gr4*(gc2+grfm)+(gc2+gr4+grfm)*grfp) * (ic1eq+io-gc1*vi)) / (gc1*gr4*(gc2+grfm) + gr4*(gc2+grfm)*grfp + gc1*(gc2+gr4+grfm)*grfp))

delta = 100
n_iters = 0
while np.abs(delta) > 1.0e-5 or n_iters > 5:
io = Iq * np.exp(-v1 / vt)
vo = (ic1eq + io + (gc1+gr4)*v1 - gc1*vi) / gr4
# vo = ((gc1+gr4) * grfp*ic2eq+(gc2+grfm+grfp) * (-gr4*ic1eq+gc1*io+gc1*gr4*vi)) / (gc1*gr4*(gc2+grfm) + gr4*(gc2+grfm)*grfp + gc1*(gc2+gr4+grfm)*grfp)

F = (gc1*(vi - v1) - ic1eq) + gr4*(vo - v1) - io
F_p = -gc1 - gr4 + (Iq/vt) * np.exp(-v1 / vt)
delta = F / F_p
v1 -= delta

n_iters += 1

io = Iq * np.exp(-v1 / vt)
vo = (ic1eq + io + (gc1+gr4)*v1 - gc1*vi) / gr4
# vo = ((gc1+gr4) * grfp*ic2eq+(gc2+grfm+grfp) * (-gr4*ic1eq+gc1*io+gc1*gr4*vi)) / (gc1*gr4*(gc2+grfm) + gr4*(gc2+grfm)*grfp + gc1*(gc2+gr4+grfm)*grfp)

v2 = -((io + gr4*v1 - (gr4+grfp)*vo) / grfp)
ic1eq = 2 * gc1 * (vi - v1) - ic1eq
ic2eq = 2 * gc2 * (v2) - ic2eq

y[n] = 4 * vo

return y - np.mean(y)

# %%

plt.plot(x[N - 2000:])

for g in [0, 0.25, 0.5, 0.75, 1.0]:
y = process(x, g)
plt.plot(y[N - 2000:], '--')

# %%
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ target_sources(BYOD PRIVATE
processors/drive/diode_circuits/DiodeClipper.cpp
processors/drive/diode_circuits/DiodeRectifier.cpp
processors/drive/flapjack/Flapjack.cpp
processors/drive/fuzz_machine/FuzzMachine.cpp
processors/drive/fuzz_machine/FuzzFaceSolver.cpp
processors/drive/hysteresis/Hysteresis.cpp
processors/drive/hysteresis/HysteresisProcessing.cpp
processors/drive/junior_b/JuniorB.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/processors/ProcessorStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "drive/diode_circuits/DiodeClipper.h"
#include "drive/diode_circuits/DiodeRectifier.h"
#include "drive/flapjack/Flapjack.h"
#include "drive/fuzz_machine/FuzzMachine.h"
#include "drive/hysteresis/Hysteresis.h"
#include "drive/junior_b/JuniorB.h"
#include "drive/king_of_tone/KingOfToneDrive.h"
Expand Down Expand Up @@ -89,6 +90,7 @@ ProcessorStore::StoreMap ProcessorStore::store = {
{ "Dirty Tube", &processorFactory<TubeAmp> },
{ "Distortion Plus", &processorFactory<MXRDistortion> },
{ "Flapjack", &processorFactory<Flapjack> },
{ "Fuzz Machine", &processorFactory<FuzzMachine> },
{ "GuitarML", &processorFactory<GuitarMLAmp> },
{ "Hysteresis", &processorFactory<Hysteresis> },
{ "Junior B", &processorFactory<JuniorB> },
Expand Down
71 changes: 71 additions & 0 deletions src/processors/drive/fuzz_machine/FuzzFaceSolver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "FuzzFaceSolver.h"

namespace
{
constexpr auto vt = 0.026 * 0.5;
constexpr auto Iq = 1.0e-10;
} // namespace

void FuzzFaceSolver::prepare (double sample_rate)
{
fs = sample_rate;
gc1 = 2.0 * fs * C1;
gc2 = 2.0 * fs * C2;

reset();
}

void FuzzFaceSolver::reset()
{
std::fill (ic1eq.begin(), ic1eq.end(), 0.0);
std::fill (ic2eq.begin(), ic2eq.end(), 0.0);
std::fill (vo.begin(), vo.end(), 0.0);
std::fill (io.begin(), io.end(), 0.0);
}

void FuzzFaceSolver::set_gain (double gain_01) noexcept
{
const auto gain_factor = chowdsp::Power::ipow<4> (gain_01 * 0.98 + 0.01);
grfp = 1.0 / ((1.0 - gain_factor) * Rf);
grfm = 1.0 / (gain_factor * Rf);
}

void FuzzFaceSolver::process (std::span<float> data, size_t ch) noexcept
{
for (auto& sample : data)
{
const auto vi = std::tanh (0.1 * (double) sample);

// initial guess
auto v1 = -((-gr4 * (grfp * ic2eq[ch] + (gc2 + grfm + grfp) * io[ch]) + (gr4 * (gc2 + grfm) + (gc2 + gr4 + grfm) * grfp) * (ic1eq[ch] + io[ch] - gc1 * vi)) / (gc1 * gr4 * (gc2 + grfm) + gr4 * (gc2 + grfm) * grfp + gc1 * (gc2 + gr4 + grfm) * grfp));

// Newton-Raphson Solver
double delta;
int n_iters = 0;
auto v1_exp = std::exp (-v1 / vt);
do
{
io[ch] = Iq * v1_exp;
vo[ch] = (ic1eq[ch] + io[ch] + (gc1 + gr4) * v1 - gc1 * vi) / gr4;

const auto F = (gc1 * (vi - v1) - ic1eq[ch]) + gr4 * (vo[ch] - v1) - io[ch];
const auto F_p = -gc1 - gr4 + (Iq / vt) * v1_exp;
delta = F / F_p;
v1 -= delta;

v1_exp = std::exp (-v1 / vt);
n_iters++;
} while (std::abs (delta) > 1.0e-5 && n_iters < 5);

// compute output
io[ch] = Iq * v1_exp;
vo[ch] = (ic1eq[ch] + io[ch] + (gc1 + gr4) * v1 - gc1 * vi) / gr4;

//update state
const auto v2 = -((io[ch] + gr4 * v1 - (gr4 + grfp) * vo[ch]) / grfp);
ic1eq[ch] = 2.0 * gc1 * (vi - v1) - ic1eq[ch];
ic2eq[ch] = 2.0 * gc2 * (v2) -ic2eq[ch];

sample = float (-100.0 * vo[ch]);
}
}
29 changes: 29 additions & 0 deletions src/processors/drive/fuzz_machine/FuzzFaceSolver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <pch.h>

struct FuzzFaceSolver
{
double fs = 48000.0;

double gr4 = 1.0 / 100.0e3;
double Rf = 1.0e3;
double grfp = 1.0 / (0.5 * Rf);
double grfm = 1.0 / (0.5 * Rf);

double C1 = 2.2e-6;
double C2 = 20.0e-6;
double gc1 = 2.0 * fs * C1;
double gc2 = 2.0 * fs * C2;

std::array<double, 2> ic1eq {};
std::array<double, 2> ic2eq {};
std::array<double, 2> vo {};
std::array<double, 2> io {};

void prepare (double sample_rate);
void reset();

void set_gain (double gain_01) noexcept;
void process (std::span<float> data, size_t channel_index) noexcept;
};
73 changes: 73 additions & 0 deletions src/processors/drive/fuzz_machine/FuzzMachine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "FuzzMachine.h"
#include "processors/ParameterHelpers.h"

FuzzMachine::FuzzMachine (UndoManager* um)
: BaseProcessor ("Fuzz Machine", createParameterLayout(), um)
{
using namespace ParameterHelpers;
fuzzParam.setParameterHandle (getParameterPointer<chowdsp::PercentParameter*> (vts, "fuzz"));
loadParameterPointer (volumeParam, vts, "vol");

uiOptions.backgroundColour = Colours::red.darker (0.15f);
uiOptions.powerColour = Colours::silver.brighter (0.1f);
uiOptions.info.description = "Fuzz effect based loosely on the \"Fuzz Face\" pedal.";
uiOptions.info.authors = StringArray { "Jatin Chowdhury" };
}

ParamLayout FuzzMachine::createParameterLayout()
{
using namespace ParameterHelpers;
auto params = createBaseParams();

createPercentParameter (params, "fuzz", "Fuzz", 0.5f);
createPercentParameter (params, "vol", "Volume", 0.5f);

return { params.begin(), params.end() };
}

void FuzzMachine::prepare (double sampleRate, int samplesPerBlock)
{
model.prepare (sampleRate);

fuzzParam.setRampLength (0.025);
fuzzParam.prepare (sampleRate, samplesPerBlock);

const auto spec = juce::dsp::ProcessSpec { sampleRate, (uint32_t) samplesPerBlock, 2 };
dcBlocker.prepare (spec);
dcBlocker.calcCoefs (30.0f, (float) sampleRate);

volume.setGainLinear (volumeParam->getCurrentValue());
volume.setRampDurationSeconds (0.05);
volume.prepare (spec);

// pre-buffering
AudioBuffer<float> buffer (2, samplesPerBlock);
float level = 100.0f;
while (level > 1.0e-4f)
{
buffer.clear();
processAudio (buffer);
level = buffer.getMagnitude (0, samplesPerBlock);
}
}

void FuzzMachine::processAudio (AudioBuffer<float>& buffer)
{
const auto numSamples = buffer.getNumSamples();
fuzzParam.process (numSamples);

for (auto [ch, n, data] : chowdsp::buffer_iters::sub_blocks<32, true> (buffer))
{
if (ch == 0)
model.set_gain (fuzzParam.getSmoothedBuffer()[n]);
model.process (data, ch);
}

if (! chowdsp::BufferMath::sanitizeBuffer (buffer))
model.reset();

dcBlocker.processBlock (buffer);

volume.setGainLinear (volumeParam->getCurrentValue());
volume.process (buffer);
}
28 changes: 28 additions & 0 deletions src/processors/drive/fuzz_machine/FuzzMachine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include "processors/BaseProcessor.h"

#include "FuzzFaceSolver.h"

class FuzzMachine : public BaseProcessor
{
public:
explicit FuzzMachine (UndoManager* um);

ProcessorType getProcessorType() const override { return Drive; }
static ParamLayout createParameterLayout();

void prepare (double sampleRate, int samplesPerBlock) override;
void processAudio (AudioBuffer<float>& buffer) override;

private:
chowdsp::SmoothedBufferValue<float> fuzzParam;
chowdsp::PercentParameter* volumeParam = nullptr;

FuzzFaceSolver model;

chowdsp::FirstOrderHPF<float> dcBlocker;
chowdsp::Gain<float> volume;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FuzzMachine)
};

0 comments on commit 1e94959

Please sign in to comment.