Skip to content

huberp/phu-compressor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

14 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PHU Compressor

Build Release License: MIT Platform Format JUCE Coffee

A VST3 single-band OTT-style compressor combining upward and downward compression in a single plugin. An interactive transfer curve and a beat-synced rolling waveform display show both gain stages and their detector levels simultaneously, making it easy to see exactly what is happening to the dynamic range.

PHU Compressor


Contents


Highlights

πŸ”ΌπŸ”½ Upward + downward compression in the classic OTT order β€” the upward stage lifts quiet parts first; the downward stage then acts as a natural ceiling on the result. This makes the chain self-regulating: the downward compressor never over-clamps content that was already quiet, and the upward boost never pushes content above the downward threshold.

πŸ“ Interactive transfer curve β€” large coloured handles on the transfer curve are draggable directly in the display. Moving a handle updates the corresponding threshold parameter in real-time. The combined (upward + downward) gain curve is drawn as a single continuous line so the interaction between both stages is always visible at a glance.

πŸ“Š Beat-synced waveform display β€” the right half of the panel renders input level, downward gain reduction, and upward boost as overlaid curves. In Beat Sync mode the display is anchored to the DAW's PPQ position so the waveform stays grid-aligned with the DAW transport regardless of display duration, BPM changes, or sample rate. In non-sync mode a classic scrolling waveform is shown instead.

πŸŽ›οΈ Flexible detector β€” each stage has its own detector feeding independent one-pole ballistics. The detector can operate in RMS (rolling window, O(1)) or PeakMax (rolling window max) mode. In BPM-sync mode the detector window is set as a musical fraction (1/32 to 4 beats) and tracks tempo changes automatically, so the compressor's response time stays musically consistent regardless of BPM.

🎚️ Independent display toggles β€” detector level curves (up/down) and gain-reduction overlays (up/down) can each be shown or hidden independently, keeping the display readable at any complexity level.


User Guide

Installation

  1. Download the latest release from Releases
  2. Copy the .vst3 bundle to your DAW's VST3 folder:
    • Windows: C:\Program Files\Common Files\VST3\
    • Linux: ~/.vst3/ or /usr/lib/vst3/
  3. Rescan plugins in your DAW
  4. Load PHU COMPRESSOR on any track

No external dependencies β€” the binary is self-contained.

Controls

Downward group β€” compresses signals that exceed the threshold

Parameter Range Description
Thresh (dB) βˆ’60 … 0 dB Level above which downward compression engages.
Ratio 1:1 … 20:1 Compression ratio above threshold.
Attack (ms) 0.1 … 500 ms Time for gain reduction to fully engage after the threshold is crossed.
Release (ms) 1 … 2000 ms Time for gain reduction to recover after the signal falls back below threshold.
Level toggle Show/hide the downward detector curve in the display.

Upward group β€” boosts signals that fall below the threshold

Parameter Range Description
Thresh (dB) βˆ’60 … 0 dB Level below which upward compression (boost) engages.
Ratio 1:1 … 20:1 Upward expansion ratio below threshold.
Attack (ms) 0.1 … 500 ms Time for the boost to engage.
Release (ms) 1 … 2000 ms Time for the boost to release.
Level toggle Show/hide the upward detector curve in the display.

Detector group β€” controls the level detector shared by both stages

Parameter Range Description
Type RMS / PeakMax Sets the detection algorithm. RMS gives smooth, musical response; PeakMax reacts faster to transients.
Sync toggle When enabled, the detector window is set as a musical beat fraction that tracks BPM. When disabled, a manual millisecond window is used.
Beat Div 1/32 … 4 Beat fraction used as the detector window in Sync mode. 1/32 is fast (transient-accurate); 4 beats is very smooth.
RMS Window (ms) 1 … 6000 ms Manual detector window length (visible when Sync is off, RMS mode).
Peak Window (ms) 1 … 6000 ms Manual detector window length (visible when Sync is off, PeakMax mode).

Display toggles (below the control panels)

Toggle Effect
Down GR Show/hide the downward gain reduction overlay (orange, top of display).
Up GR Show/hide the upward boost overlay (pink/magenta).
Beat Sync Switch the waveform display between beat-anchored and scrolling modes.

Building

Prerequisites

Tool Minimum version
CMake 3.15
C++ compiler C++17 β€” MSVC 2022, GCC 11, or Clang 14
JUCE 8.0.12 (included as git submodule)

Clone

git clone https://github.com/huberp/phu-compressor.git
cd phu-compressor
git submodule update --init --recursive

Windows

cmake --preset vs2026-x64
cmake --build --preset release

Output: build/vs2026-x64/src/phu-compressor_artefacts/Release/VST3/

Linux

sudo bash scripts/install-linux-deps.sh
cmake --preset linux-release
cmake --build --preset linux-build

If the build times out: cmake --build --preset linux-build -j2

Output: build/linux-release/src/phu-compressor_artefacts/VST3/

For a full Linux dependency walkthrough see docs/LINUX_BUILD.md.


Architecture

Core Components

Component Location Responsibility
OttCompressor src/OttCompressor.h Top-level compressor. Owns two VolumeDetectors and two CompressorStages (one upward, one downward). Implements the classic OTT signal order: upward detection β†’ upward boost β†’ downward detection β†’ downward compression. A fast peak-follower (5 ms decay) provides per-channel transient protection without per-sample carrier noise.
CompressorStage src/CompressorStage.h One compressor stage (downward or upward). Computes the target gain from the raw (unsmoothed) detector level, then smooths the gain envelope toward that target with separate one-pole attack and release coefficients. Gain ballistics operate on the gain domain, not the level domain, to prevent boost tails on transients from the upward stage.
VolumeDetector src/VolumeDetector.h Rolling-window level detector (RMS or PeakMax). RMS uses a running sum for O(1) per sample. PeakMax tracks the max in the window and rescans on eviction. The ring buffer is pre-allocated at prepare() to kDetectorMaxWindowMs samples β€” no allocation on the audio thread.
CompressorDisplay src/CompressorDisplay.h/cpp JUCE Component rendered at 60 Hz. Unified panel with: (1) a transfer curve with full-colour draggable threshold handles, (2) a rolling or beat-synced waveform showing input level and both GR overlays, and (3) a musical time selector. Pulls all data from lock-free FIFOs and BeatSyncBuffers via updateFromFifos().
BeatSyncBuffer lib/audio/BeatSyncBuffer.h Position-indexed overwrite buffer. Each bin maps to a normalised beat position [0, 1). Audio-thread writes via write(normalizedPos, value); UI-thread reads via data(). Single float stores are naturally atomic on x86/x64.
AudioSampleFifo lib/audio/AudioSampleFifo.h Lock-free SPSC FIFO for per-sample audio β†’ UI transport. Used for input level and both gain-reduction streams.
RmsPacketFifo lib/audio/RmsPacketFifo.h Lock-free FIFO carrying batched, PPQ-anchored detector level packets. Allows the display to draw detector curves with musical time alignment.
SyncGlobals lib/events/SyncGlobals.h Holds current BPM, sample rate, and transport PPQ. Written on the audio thread; UI-thread reads ppqEndOfBlock via std::atomic<double>.
PluginProcessor src/PluginProcessor.h/cpp AudioProcessor + APVTS. Owns all DSP, FIFOs, and BeatSyncBuffers. APVTS raw parameter pointers are cached at construction for lock-free audio-thread reads.

DSP Signal Path

processBlock  (audio thread)
  β”œβ”€ Read play-head: extract BPM, PPQ, isPlaying β†’ SyncGlobals
  β”‚
  β”œβ”€ Push raw input β†’ InputFifo  (for UI input level curve)
  β”‚
  β”œβ”€ For each sample (L + R independently):
  β”‚    β”œβ”€ detectorUp.processSample(input)   β†’ up level (dB)
  β”‚    β”œβ”€ upStage.processSample(upLevelDb)  β†’ upBoostGain  (β‰₯ 1.0)
  β”‚    β”œβ”€ intermediate = input Γ— upBoostGain
  β”‚    β”‚
  β”‚    β”œβ”€ detectorDown.processSample(intermediate) β†’ down level (dB)
  β”‚    └─ downStage.processSample(downLevelDb)     β†’ downGain (≀ 1.0)
  β”‚         └─ output = intermediate Γ— downGain
  β”‚
  β”œβ”€ Push downGain  β†’ GainReductionFifo   (for UI down-GR curve)
  β”œβ”€ Push upBoost   β†’ UpGainReductionFifo (for UI up-GR curve)
  β”œβ”€ Accumulate detector levels β†’ RmsPacketFifo (PPQ-anchored, ~4-block batches)
  β”‚
  └─ Write beat-sync buffers (PPQ β†’ normalised pos β†’ bin):
       β”œβ”€ m_inputSyncBuf.write(normPos, inputDb)
       β”œβ”€ m_grSyncBuf.write(normPos, downGrDb)
       └─ m_upGrSyncBuf.write(normPos, upBoostDb)

UI Timer  (60 Hz)
  β”œβ”€ updateFromFifos(): drain all FIFOs β†’ internal ring buffers
  β”œβ”€ If beat-sync mode: snapshot BeatSyncBuffers directly
  β”œβ”€ Repaint transfer curve with current threshold handles
  └─ Repaint waveform with input, down-GR, up-GR, detector overlays

Beat-Synced Detector Window

When Sync is enabled in the Detector group, the detector window length is computed as:

$$W_{ms} = \frac{60,000}{\text{BPM}} \times \text{beatFraction}$$

The window is recomputed on every processBlock call using the current play-head BPM, so it tracks tempo automation without any user interaction. Supported fractions range from 1/32 (fast, β‰ˆ 15 ms at 120 BPM) to 4 beats (slow, β‰ˆ 2000 ms at 120 BPM).

Project Layout

phu-compressor/
β”œβ”€β”€ CMakeLists.txt / CMakePresets.json
β”œβ”€β”€ docs/                            Screenshots and build guide
β”œβ”€β”€ JUCE/                            JUCE 8.0.12 (git submodule)
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ PluginProcessor.h/cpp        processBlock, OTT compressor, FIFOs,
β”‚   β”‚                                BeatSyncBuffers, APVTS
β”‚   β”œβ”€β”€ PluginEditor.h/cpp           UI layout, 60 Hz timer, APVTS attachments
β”‚   β”œβ”€β”€ CompressorDisplay.h/cpp      Transfer curve + rolling/beat-sync waveform
β”‚   β”œβ”€β”€ OttCompressor.h              Upward + downward compressor chain
β”‚   β”œβ”€β”€ CompressorStage.h            One compressor stage (up or down)
β”‚   β”œβ”€β”€ VolumeDetector.h             RMS / PeakMax rolling-window detector
β”‚   β”œβ”€β”€ PluginConstants.h            Beat-division tables, buffer size constants
β”‚   └── CMakeLists.txt
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ audio/
β”‚   β”‚   β”œβ”€β”€ AudioSampleFifo.h        Lock-free SPSC FIFO (audioβ†’UI)
β”‚   β”‚   β”œβ”€β”€ BeatSyncBuffer.h         Position-indexed overwrite buffer
β”‚   β”‚   β”œβ”€β”€ BucketSet.h              Dirty-tracked bucket partitioning
β”‚   β”‚   β”œβ”€β”€ RmsPacketFifo.h          PPQ-anchored detector level packets
β”‚   β”‚   β”œβ”€β”€ PpqRingBuffer.h          PPQ-indexed ring buffer helper
β”‚   β”‚   └── PacketFifo.h             Generic lock-free packet FIFO
β”‚   └── events/
β”‚       β”œβ”€β”€ SyncGlobals.h            BPM / PPQ / transport state
β”‚       β”œβ”€β”€ SyncGlobalsListener.h    Event listener interface
β”‚       └── Event.h / EventSource.h  Typed event infrastructure
β”œβ”€β”€ .github/workflows/               CI build + pluginval + release workflows
└── scripts/
    β”œβ”€β”€ build.bat                    Windows convenience build script
    β”œβ”€β”€ release.bat                  Windows release packaging script
    └── install-linux-deps.sh        Installs JUCE Linux dependencies

Contributing

Contributions are welcome.

  1. Fork and branch from main
  2. Follow existing C++17/JUCE code style
  3. No memory allocation, system calls, or locks on the audio thread
  4. Verify the project builds and passes pluginval before opening a PR

Bug reports β€” please include DAW name/version, OS, and reproduction steps in a GitHub Issue.


License

MIT

About

simple downwards/upwards compressor

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

 
 
 

Contributors