A source-free engineering case study for a commercial JUCE audio plugin. Proprietary C++ source, the licensed audio library, signing credentials, and production secrets are intentionally excluded.
Watch the 2:57 product demo · Visit the product page · Explore the architecture
Loopsic is a drum and guitar loop plugin built to shorten the path from finding an idea to arranging it in a DAW. It combines a searchable loop library, waveform preview, four-variation browsing, host-tempo matching, processing controls, and file drag-and-drop in one native desktop product.
The engineering work spans more than the plugin UI:
- a JUCE/C++20 audio processor and editor;
- VST3, Audio Unit, and standalone targets;
- DAW transport and tempo integration;
- real-time preview and playback;
- tempo-matched rendering for drag-and-drop;
- online license activation with bounded offline use;
- external content-pack discovery and metadata;
- CMake-based macOS and Windows build pipelines.
This repository documents the architecture, constraints, decisions, and trade-offs without publishing the product source.
| Area | Implementation |
|---|---|
| Language | C++20 |
| Framework | JUCE 8 |
| Build system | CMake 3.22+ |
| Plugin formats | VST3, Audio Unit on macOS |
| Application target | Standalone on macOS and Windows |
| Shipping platforms | macOS universal binary and Windows x64 |
| Host integration | BPM, play state, and PPQ position |
| State | AudioProcessorValueTreeState plus application properties |
| Content | External loop packs with indexed metadata |
| Delivery | macOS package and Windows installer pipelines |
- The musician filters the library by type, genre, meter, key, favorites, or text search.
- Loopsic previews the selected variation and displays its waveform and metadata.
- Optional BPM sync renders the loop for the current host tempo.
- The musician drags either the source loop or a processed render into the DAW.
The plugin keeps host-facing audio work, content browsing, offline rendering, licensing, and persistence behind separate responsibilities.
flowchart LR
Host["DAW host"] <--> Processor["LoopsicAudioProcessor"]
Processor --> Transport["Atomic host-transport snapshot"]
Processor --> Engine["LoopEngine"]
Processor <--> State["AudioProcessorValueTreeState"]
Editor["LoopsicAudioProcessorEditor"] --> Browser["LoopBrowserComponent"]
Browser --> Library["LoopLibrary + LoopPackReader"]
Browser --> Preview["Waveform preview"]
Browser --> Render["LoopRenderEngine"]
Render --> Drag["External file drag"]
Drag --> Host
Content[("External loop pack")] --> Library
Editor --> License["LicenseManager"]
License --> Local["Device-bound local cache"]
License --> Backend["Licensing backend"]
The detailed component boundaries and data flows are documented in Architecture.
The processor reads the host playhead during the audio callback and snapshots BPM, transport state, and PPQ position into atomics. The UI and render path can consume that state without making a network or file-system call from the callback.
For a selected loop, TempoSyncEngine combines:
- the source loop BPM;
- its bar count and time-signature metadata;
- the current host BPM;
- pitch-lock and processing choices.
It then derives the target duration and playback ratio used by preview and offline drag rendering.
sequenceDiagram
participant DAW as DAW host
participant P as LoopsicAudioProcessor
participant T as TempoSyncEngine
participant R as LoopRenderEngine
participant U as Musician
DAW->>P: BPM, play state, PPQ
P->>P: Store atomic transport snapshot
U->>T: Select loop + enable BPM Sync
T->>T: Calculate ratio and target duration
U->>R: Drag loop to arrangement
R->>R: Render tempo-matched temporary audio
R-->>DAW: Begin external file drag
The callback path is intentionally narrower than the product as a whole:
- DSP resources are prepared in
prepareToPlay; ScopedNoDenormalsprotects the processor from denormal slowdowns;- host transport values are shared through atomics;
- library indexing, licensing, downloads, and file rendering remain outside the audio callback;
- mono and stereo layouts are handled explicitly;
- state changes flow through JUCE parameter state rather than UI-owned values.
The implementation also retains a compatibility guard that can grow an internal scratch buffer if a host changes the channel or block-size contract. Removing that last callback-time growth path through maximum-size preallocation is a documented improvement, not a hidden limitation.
See Real-time audio notes for the callback boundary and test strategy.
Dragging audio between a plugin and multiple DAWs is not just a UI gesture. The pipeline has to keep metadata, tempo choices, optional processing, temporary file lifetime, and native OS drag behavior consistent.
flowchart LR
Selection["Loop + variation"] --> Decision{"BPM sync or FX?"}
Decision -->|No| Source["Resolve source audio"]
Decision -->|Yes| Render["Offline render to temporary WAV"]
Source --> Payload["LoopDragPayload"]
Render --> Payload
Payload --> Native["JUCE external file drag"]
Native --> DAW["DAW arrangement"]
The drag payload carries the selected loop identity and musical metadata while
LoopRenderEngine owns any tempo-matched or processed render.
The preview surface connects processing controls to immediate musical feedback: an equalizer curve, width/depth controls, a tap-tempo tool, waveform selection, variation switching, favorites, pitch lock, and BPM sync.
LicenseManager coordinates activation, validation, deactivation, device
identity, and a bounded offline grace period. Network calls go through a
project-owned backend rather than exposing commerce-provider credentials in
the plugin.
The local record is device-bound and obfuscated for persistence, but it is not treated as tamper-proof encryption. The server remains the authority. That distinction is important in a client binary that an end user fully controls.
See Licensing and trust boundaries.
One CMake target description produces platform-appropriate artifacts:
| Platform | Targets | Packaging |
|---|---|---|
| macOS | VST3, Audio Unit, Standalone | Universal arm64/x86_64 build and .pkg |
| Windows | VST3, Standalone | x64 build and Inno Setup installer |
The CI definition builds and stages the plugin artifacts independently on
macos-14 and windows-latest. The content pack remains external so installer
size and licensed content delivery can evolve separately from the binary.
Build contracts, artifact paths, and release gates are described in Build and release.
- Native C++ product engineering beyond a DSP prototype
- Real-time and non-real-time boundary design
- DAW host integration and musical-time calculations
- Cross-application drag-and-drop with rendered audio
- Commercial licensing and offline-state trade-offs
- External content delivery and metadata-driven discovery
- Cross-platform build, installer, and artifact automation
- Preallocate the compatibility scratch buffer for the maximum supported host block size.
- Add automated allocation instrumentation around the audio callback.
- Run
pluginvaland a representative DAW matrix in release qualification. - Add signed/notarized artifact gates and installer smoke tests to CI.
- Add deterministic tempo-render tests across compound and odd meters.
- Track render latency and preview underruns with privacy-preserving telemetry.
This portfolio repository contains documentation and product media only. It does not grant a license to the proprietary Loopsic source code, audio content, brand assets, or commercial binaries. See NOTICE.
Designed and engineered by Serkan Seker.


