BA Barış Akın~/blog

·7 min read·76 views

BarisAMP: a free guitar amp, pedal and NAM plugin written in Rust

A JCM800 tone stack computed from the circuit, NAM inference that matches NeuralAmpModelerCore sample for sample, zero-latency cabinet convolution and a 12-band noise suppressor that learns: what is inside BarisAMP, and how it ships as VST3, CLAP and AU.

BarisAMP: a free guitar amp, pedal and NAM plugin written in Rust

BarisAMP is a complete guitar rig in one app: amp, pedals, cabinets and neural amp captures. It runs on macOS and Windows as a standalone app or as a VST3, CLAP or (on macOS) Audio Unit plugin. It is free; downloads are at barisamp.baltavista.com.

In this post I'll look inside it: how the amp model is computed from the circuit, how NAM captures are run, the zero-latency cabinet convolution, the noise suppressor that learns your rig, and how the plugin is packaged for DAWs. It is written in Rust, with nih-plug for the plugin side and egui for the interface.

The BarisAMP interface: a gold amp panel above rows of pedals, effects and EQ

Signal chain

Denoise → Gate → Comp → Wah → Pitch → Swell → Drive → Amp / NAM
        → Cabinet → EQ → Mod → Delay → Reverb → Looper → Limiter

Everything up to the cabinet is mono: all of the interface's input channels are summed, so the guitar works in whichever input it is plugged into. Modulation, the ping-pong delay and the reverbs make the signal stereo. Modulation can also be moved before the amp, like a pedal.

The amp model

The built-in amp follows the layout of a JCM800: two triode preamp stages, a passive tone stack and a power amp. Each triode stage is an asymmetric soft clipper:

fn triode(x: f32, bias: f32) -> f32 {
    (x + bias).tanh() - bias.tanh()
}

bias shifts the operating point the way a tube's grid bias does. Because the curve is not symmetric around zero, it produces even harmonics; the trailing - bias.tanh() removes the DC it creates. Between the stages there are 30 Hz high-passes standing in for the real amp's coupling capacitors, and low-passes that soften the highs.

The HIGH and LOW inputs work like a JCM800's too: LOW skips the first triode stage and puts a fixed linear gain in its place, for a cleaner, quieter sound.

Clipping is nonlinear and adds harmonics above Nyquist. Those fold back down (aliasing) and sound like a metallic fizz. So the amp and the drive pedals run at 4x the sample rate: the signal is zero-stuffed up, filtered with 8th-order Butterworth filters, processed, and filtered back down.

A tone stack computed from the circuit

Many amp sims make BASS, MIDDLE and TREBLE three separate EQ bands. A real amp doesn't: in the passive "FMV" tone stack of Fender, Marshall and Vox amps the three pots interact, and even with all of them at noon there is a dip in the mids.

BarisAMP models this circuit as in D. Yeh and J. O. Smith's "Discretization of the '59 Fender Bassman Tone Stack". The circuit's third-order analog transfer function is computed directly from the component values and pot positions, then discretized with the bilinear transform. The values are the JCM800's: a 220k treble, 1M bass and 22k middle pot, a 33k slope resistor, 470 pF and two 22 nF. Turning a knob recomputes the coefficients.

A passive tone stack loses a lot of level, which in a real amp the following stage makes up. Here too, the stack's loss at 1 kHz with every pot at noon is measured and given back.

Neural Amp Modeler

Besides the built-in amp, switching AMP to NAM loads Neural Amp Modeler captures. A NAM model is a neural network trained on input and output recordings of a real amp, and the community shares thousands of .nam files.

The inference code is ported to Rust from NeuralAmpModelerCore (MIT): WaveNet (standard, lite, feather, nano), LSTM and the A2 container format. Tests check the port: the example models get the same input, and the output has to match NeuralAmpModelerCore's sample for sample.

A few practical details:

  • Level. Models can differ wildly in output level. When the file carries loudness metadata, the model is normalized to -18 dB; otherwise the NAM trainer's standard input signal is played through the model and the level is measured.
  • Sample rate. If a model was trained at 48 kHz and the DAW runs at 44.1 kHz, the signal is resampled to the model's rate and back.
  • Speed. Most of a WaveNet's work is dilated convolutions. On Apple Silicon they are computed in tiles of 4 outputs × 16 frames with the sums held in NEON registers, close to the core's FMA peak. That made the WaveNet about 2.3x faster. On an Apple M4 a standard WaveNet uses about 2 % of one core, and the built-in amp less than 1 %.

I didn't even try the GPU: at audio buffer sizes the round trip costs more than the work, and its timing isn't guaranteed.

Ten drive pedals

Each pedal is a simplified model of a classic circuit: the filtering around the gain stage, the gain itself, and the kind of clipping.

PedalInspired byCharacter
ScreamerIbanez TS808only the mids, softly clipped in the op amp feedback; the clean signal is mixed back in
TransparentKlon Centaura clean path blended with a germanium clipped path
BluesBoss BD-2two discrete stages, asymmetric soft clipping
RodentProCo RATan op amp whose bandwidth shrinks as gain rises, hard diodes to ground
Dist OneBoss DS-1transistor boost, hard silicon clipping, mid-scooped tone
FuzzBig Mufftwo cascaded clipping stages
Yellow 250DOD 250a 741 whose gain rises above ~720 Hz, silicon diodes
Fuzz FaceDallas Arbitera germanium feedback pair, round and lopsided clipping
Treble BoosterRangemasterone germanium transistor that lifts and gently saturates the upper mids
Clean BoostMXR Micro Ampflat gain from 0 to +20 dB

The pedals' natural levels are very different, so each has an output trim; at half DRIVE they all sit near unity gain, and switching pedals doesn't suddenly jump in volume.

A zero-latency cabinet

The cabinet is an impulse response (IR) convolution. 21 IRs are built in (a Marshall 1960AX with Greenbacks, a 4x12 with V30s and others), and you can load your own WAV files.

Convolving a long IR sample by sample is too expensive, and doing it with an FFT adds a block of latency. BarisAMP combines the two:

  • The IR's first 64 taps run as a direct FIR, so the output never waits.
  • The rest is split into 64-sample partitions and processed with overlap-save FFT convolution. Each tail partition only needs input blocks that are already complete, so it adds no latency either.

A noise suppressor that learns

At high gain the biggest problem is hiss. A classic noise gate opens while you play and lets all of the hiss through. BarisAMP's DENOISE works differently:

  1. The signal is split into 12 bands with crossovers from 150 Hz to 10 kHz. The split uses zero-latency Linkwitz-Riley (24 dB/octave) filters, and each band also runs through the allpasses of the crossovers above it, so the bands sum back with a flat magnitude.
  2. Pressing LEARN measures every band's noise level for 2 seconds while the strings are quiet.
  3. While playing, each band is turned down softly, at 3:1, as it falls toward its learned noise level.

The result: while a note sustains, the hiss in the high bands the note doesn't reach stays suppressed.

The rest

Pedal, auto and LFO wah; octave, Octavia, harmonizer and whammy; a Slow Gear style volume swell; chorus, flanger, phaser, tremolo, Uni-Vibe and a rotary speaker with a separate horn and drum; analog, tape, digital, ping-pong, Echorec-style multi-head and reverse delay (tap tempo, and sync to the DAW's tempo); room, hall, plate, spring and shimmer reverb with freeze. On top of that a chromatic tuner, a 60-second looper with overdub, an 8-band EQ with a live spectrum, a compressor and a limiter. The factory presets include sounds inspired by Gilmour, Malmsteen, Metallica and Slayer.

Rules of the audio thread

The audio thread never allocates; in debug builds nih-plug's assert_process_allocs enforces it. IRs and NAM models are loaded on another thread, and the finished object is just swapped into the audio thread. Loading a new model while playing doesn't cause a dropout.

VST3, CLAP and Audio Unit

The plugin itself is the CLAP that nih-plug builds. I turned off nih-plug's own VST3 export, because its VST3 bindings are GPLv3. Instead clap-wrapper, with Steinberg's VST3 SDK and Apple's AudioUnitSDK, wraps the CLAP as a VST3 and an AUv2. A small patch to clap-wrapper offers the channel layouts (1→2, 2→2, 1→1) that Logic's validation requires, carries the patch number into the AU version, and reads parameter values back after the VST3 state is loaded.

Some things must never change: the VST3 class ID and the AU identifiers ("aufx", "Bamp", "Bars") are the same as in the first release. If they changed, DAWs wouldn't find the plugin in saved projects. Releases are validated with auval -strict and Steinberg's validator, and the Windows VST3 is also tested on GitHub Actions.

The macOS packages are for Apple Silicon (macOS 11 or later), the Windows ones for 64-bit Windows 10 and 11. The Windows builds are cross-compiled from macOS with mingw-w64.

Try it

You can download BarisAMP from barisamp.baltavista.com. Loading a NAM capture and putting a cabinet IR you like behind it is a good way to start.

BA
Barış Akın

Software solutions architect with 25+ years of experience. FinTech and distributed systems; creator of OxiDB, Rill and Cobra.