A futuristic, neon-styled virtual analog synthesizer built with JUCE and C++17, by Razvan Julian Petrescu / AudioDSP.
- 4 Waveforms — Sine, Square, Triangle, Sawtooth
- Low-pass Filter — adjustable cutoff (20 Hz – 20 kHz) with on/off bypass toggle
- 8-voice Polyphony — up to 8 simultaneous notes with voice stealing
- Release tail — smooth ~300 ms exponential fade on note-off
- Post-filter waveform display — live output visualiser in the UI
- Custom neon GUI — background image, cyan/magenta controls
- MIDI — works with any MIDI controller or keyboard
- MIDI Logging — optional real-time logging of received MIDI messages to a file (
--midilog <file>) - DAW automation — all parameters are automatable in VST3 hosts
Pre-built Windows installer: Latest release →
Run NeonSynth-x.y.z-Windows-Setup.exe. The wizard installs the VST3 to
C:\Program Files\Common Files\VST3\ and optionally the standalone app to
C:\Program Files\AudioDSP\Neon Synth\.
| Tool | Version |
|---|---|
| CMake | 3.21+ |
| JUCE | 8.x (download separately) |
| C++ compiler | MSVC 2022 / Clang / GCC with C++17 |
cmake -S . -B build \
-DJUCE_DIR="/path/to/JUCE" \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config ReleaseOn Windows (PowerShell):
cmake -S . -B build `
-DJUCE_DIR="C:/path/to/JUCE" `
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release| Format | Path |
|---|---|
| Standalone | build/BasicSynth_artefacts/Release/Standalone/BasicSynth.exe |
| VST3 | build/BasicSynth_artefacts/Release/VST3/BasicSynth.vst3/ |
Run BasicSynth.exe. Click Options → Audio/MIDI Settings to select your
audio output and MIDI input device, then play from any connected MIDI keyboard.
You can also launch the standalone application with the --midilog <file> parameter to log real-time MIDI input:
BasicSynth.exe --midilog log.txtCopy the BasicSynth.vst3 bundle to your DAW's VST3 folder
(typically C:\Program Files\Common Files\VST3\). The plugin appears under
the AudioDSP manufacturer in your DAW's instrument browser.
| Control | Colour | Description |
|---|---|---|
| Waveform | Cyan | Oscillator shape — Sine / Square / Triangle / Sawtooth |
| Filter ON | Magenta | Toggle button — enables / bypasses the filter (dims the cutoff slider when off) |
| Cutoff | Magenta | Low-pass filter cutoff frequency (20 Hz – 20 kHz) |
| Output | Cyan | Live post-filter waveform display |
The included test_midi.py script loads the VST3 directly from Python — no
MIDI ports, loopMIDI, or DAW required.
pip install pedalboard mido numpy sounddevice soundfilepython test_midi.pyExpected output:
Loading plugin …
Parameters: ['waveform', 'cutoff', 'filter_enabled', 'attack', 'decay', 'sustain', 'release', 'bypass']
PASS Single note RMS=0.2747
PASS Polyphony (4ch) single=0.2747 chord=0.6365
PASS Polyphony (8ch) RMS=0.8984
PASS Voice stealing RMS=0.8943
PASS Release tail tail RMS=0.183607
PASS Filter cutoff HFR@1.5kHz 200Hz=0.0000 18kHz=0.0409
PASS Waveforms Sine=0.0000 Square=0.0331 Triangle=0.0001 Sawtooth=0.0446
PASS Velocity scaling vel20=0.1256 vel120=0.3351
8/8 passed
The script can also render a demo arpeggio and play it through your speakers or save it to a WAV file — no MIDI keyboard or DAW needed.
# play through speakers
python test_midi.py --play
# save to WAV
python test_midi.py --save output.wav
# both at once
python test_midi.py --play --save output.wavThe demo renders a C major arpeggio with a sawtooth oscillator and a 1.1 kHz filter cutoff — warm and analog rather than chiptune.
You can enable logging of all received MIDI messages (with high-resolution timestamps in seconds relative to the start of processing) to a text file by passing the --midilog <file> option:
# Log MIDI events to a file during testing
python test_midi.py --midilog log.txtNote
On Windows, when a relative path like log.txt is passed, the file is created relative to the VST3 plugin binary's location within the build folder (e.g. build/BasicSynth_artefacts/Release/VST3/BasicSynth.vst3/Contents/x86_64-win/log.txt). To write to a specific directory (like the repository root), provide an absolute path:
python test_midi.py --midilog C:\path\to\your\synth\log.txtExample log output:
[0.025s] Note on C3 Velocity 127 Channel 1
[0.028s] All notes off Channel 1
[0.029s] Note on C3 Velocity 120 Channel 1
[0.118s] Note off C3 Velocity 0 Channel 1
| Test | What it verifies |
|---|---|
| Single note | Basic audio output — RMS above noise floor |
| Polyphony (4ch) | 4-voice chord is significantly louder than a single note |
| Polyphony (8ch) | All 8 voices fire simultaneously without dropouts |
| Voice stealing | 9th simultaneous note steals a voice without crashing or silence |
| Release tail | Audio persists ~300 ms after note-off (exponential decay) |
| Filter cutoff | High-frequency energy is lower at 200 Hz cutoff than at 18 kHz |
| Waveforms | Sine, Square, Triangle, Sawtooth produce distinct harmonic content |
| Velocity scaling | Higher velocity produces proportionally louder output |
You can also drive the synth from your own Python scripts:
import pedalboard, mido, numpy as np
VST3 = (
r"build\BasicSynth_artefacts\Release\VST3"
r"\BasicSynth.vst3\Contents\x86_64-win\BasicSynth.vst3"
)
plugin = pedalboard.load_plugin(VST3)
# Set parameters (raw_value is VST3-normalised 0–1)
plugin.parameters["cutoff"].raw_value = (500 - 20) / (20000 - 20) # ~500 Hz
plugin.parameters["waveform"].raw_value = 1 / 3 # Square
# Build MIDI messages (time field = offset in seconds)
msgs = [
mido.Message("note_on", note=60, velocity=100, time=0.0),
mido.Message("note_off", note=60, velocity=0, time=1.0),
]
# Render 2 seconds of audio → numpy array (channels, samples)
audio = plugin.process(msgs, 2.0, SR, reset=True)
print(audio.shape, audio.max())chord = [60, 64, 67, 72] # C major
msgs = []
for note in chord:
msgs.append(mido.Message("note_on", note=note, velocity=90, time=0.0))
for note in chord:
msgs.append(mido.Message("note_off", note=note, velocity=0, time=1.5))
audio = plugin.process(msgs, 2.5, SR, reset=True)import numpy as np
# Low cutoff → dark tone
plugin.parameters["cutoff"].raw_value = (200 - 20) / (20000 - 20)
audio_dark = plugin.process(msgs, 2.5, SR, reset=True)
# High cutoff → bright tone
plugin.parameters["cutoff"].raw_value = (16000 - 20) / (20000 - 20)
audio_bright = plugin.process(msgs, 2.5, SR, reset=True)| File | Purpose |
|---|---|
BasicSynth.h |
Voice and sound classes — oscillator, filter, envelope |
BasicSynthProcessor.h/cpp |
AudioProcessor — parameter management, MIDI, state |
BasicSynthEditor.h |
Neon UI — waveform selector, filter slider, output display |
CMakeLists.txt |
Build config — Standalone + VST3, JUCE path, binary resources |
gui_background.png |
Neon grid background image (embedded as binary data) |
icon.png |
Application / plugin icon |
test_midi.py |
Automated Python test suite |
pyproject.toml |
Ruff lint configuration |
.github/workflows/build.yml |
CI — CMake build on Ubuntu |
.github/workflows/lint.yml |
CI — Python ruff lint |
.github/workflows/release.yml |
CI — builds installer and publishes GitHub Release on tag push |
installer.nsi |
NSIS installer script (VST3 + Standalone) |
LICENSE |
MIT licence |