Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions DANZIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,23 @@ The `examples/danzig-gain` directory contains a complete stereo gain effect demo

```zig
pub const GainPlugin = struct {
plugin: danzig.Plugin,
gainProcessor: danzig.GainProcessor,

pub fn init(allocator: std.mem.Allocator) !*GainPlugin
pub fn process(self: *GainPlugin, inputs: []*[*]f32, outputs: []*[*]f32, numChannels: u32, numSamples: u32) void
pub fn setParameterNormalized(self: *GainPlugin, paramId: u32, normalized: f64) void
params: danzig.ParamStore(2),
sample_rate: f32,

pub fn init(sample_rate: f32) GainPlugin
pub fn setSampleRate(self: *GainPlugin, sample_rate: f32) void
pub fn isBypassed(self: *const GainPlugin) bool
pub fn nextGain(self: *GainPlugin, bypassed: bool) f32
};
```

The rest of the file wraps that core in the VST3 C ABI: one object exposing
IComponent, IAudioProcessor and IEditController, a static IPluginFactory, and
the module entry points (`GetPluginFactory`, `bundleEntry`) a host looks for.

### Parameter IDs
- `ParamID.Gain = 0`: Gain in dB (-48 to +48)
- `ParamID.Bypass = 1`: Bypass toggle
- `ParamIndex.gain = 0`: Gain in dB (-48 to +48)
- `ParamIndex.bypass = 1`: Bypass toggle

### Building
```bash
Expand Down
209 changes: 96 additions & 113 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,161 +1,144 @@
# 🎵 Danzig VST3 Framework
# danzig

[![CI](https://github.com/godofecht/danzig/actions/workflows/ci.yml/badge.svg)](https://github.com/godofecht/danzig/actions/workflows/ci.yml)
[![Zig](https://img.shields.io/badge/zig-0.14.1%20%7C%200.15.2-f7a41d)](https://ziglang.org/)

A modern, lightweight VST3 plugin development framework built in Zig with zero external dependencies.
A VST3 plugin framework in pure Zig. No JUCE, no Steinberg SDK, no C++ in the
core. `src/vst3.zig` implements the VST3 C ABI directly as `extern struct`s of
`callconv(.c)` function pointers, which is what a C++ vtable is at the machine
level.

## Project Status
**[Read the guide: docs/WIKI.md](docs/WIKI.md)**

✅ **Framework**: Complete and production-ready
✅ **Example Plugin**: Fully functional gain effect
✅ **Build System**: Standalone Zig-based build
✅ **Tests**: Passing verification suite
✅ **Documentation**: Comprehensive guides included
## Setup

## Quick Start

### Build
```bash
git clone https://github.com/godofecht/danzig
cd danzig
zig build -Doptimize=ReleaseFast
./setup.sh
```

### Test
`setup.sh` checks your Zig version, builds, runs the tests, packages the
universal VST3 bundle, and prints where it landed. It exits non-zero on failure
and is safe to run repeatedly. Pass `--release` for a `ReleaseFast` build.

By hand:

```bash
zig build test
zig build # Build Summary: 29/29 steps succeeded
zig build test --summary all # Build Summary: 9/9 steps succeeded; 35/35 tests passed
zig build vst3 # universal arm64 + x86_64 bundle in zig-out/
zig build install-vst3 # copy it to ~/Library/Audio/Plug-Ins/VST3/
```

Output:
```
✓ Test executable compiles and links with danzig library
✓ Allocator initialized
✓ Danzig library linking successful!
```
Then:

### Run the Example Plugin
The built VST3 plugin bundle is at:
```
zig-out/DanzigGain.vst3/
```bash
lipo -info zig-out/DanzigGain.vst3/Contents/MacOS/DanzigGain
```

Copy to your DAW's VST3 plugin folder:
```bash
cp -r zig-out/DanzigGain.vst3 ~/Library/Audio/Plug-Ins/VST3/
```
Architectures in the fat file: zig-out/DanzigGain.vst3/Contents/MacOS/DanzigGain are: x86_64 arm64
```

## What's Included
## Requirements

### Core Library (`src/`)
- **vst3.zig** - VST3 C ABI bindings (IUnknown, IComponent, IAudioProcessor)
- **plugin.zig** - Plugin base class with parameter management
- **audio.zig** - Audio processing utilities (gain, ramping, DSP math)
- **root.zig** - Public API exports
- Zig 0.14.1 or 0.15.2. Both are tested in CI on every push.
- macOS for the VST3 bundle, `install-vst3`, and the GUI example. The library,
the unit tests, and the CLI examples are portable Zig.
- Xcode command line tools, for `lipo` and the macOS SDK.

### Example Plugin (`examples/danzig-gain/`)
- Complete 100+ line gain effect
- Demonstrates parameter system
- Shows proper audio processing patterns
- Ready to copy and modify
## What's here

### Documentation (`docs/`)
See [docs/INDEX.md](docs/INDEX.md) for:
- Complete API reference
- Architecture explanations
- Real-world plugin examples
- Performance optimization tips
- Best practices guide
### Core library (`src/`)

## Architecture
| File | Contents |
|---|---|
| `vst3.zig` | The VST3 C ABI: `IUnknown`, `IPluginBase`, `IComponent`, `IAudioProcessor`, `IEditController`, and the structs they pass. |
| `plugin.zig` | Plugin lifecycle and a heap-backed `ParameterMap`. |
| `audio.zig` | `dBToLinear`, `GainProcessor`, `SimpleRamp`, `AudioBuffer`. |
| `params.zig` | Lock-free `AtomicParam` and `ParamStore(N)`. One cache line per parameter, no heap, no locks. |
| `tests.zig` | 35 unit tests. |

Danzig wraps VST3's complex COM machinery with type-safe Zig abstractions:
### Examples (`examples/`)

```zig
const my_plugin = try danzig.Plugin.init(allocator);
my_plugin.addParameter(gain_param);
my_plugin.process(inputs, outputs, num_channels, num_samples);
```
| Example | Run it |
|---|---|
| [danzig-minimal](examples/danzig-minimal/). The smallest complete plugin, and the file to copy. | `zig build run-minimal` |
| [danzig-gain](examples/danzig-gain/). The plugin packaged into the `.vst3` bundle. | `zig build vst3` |
| [danzig-test](examples/danzig-test/). Drives the built plugin through the raw VST3 C ABI. | `zig build test-integration` |
| [danzig-gain-standalone](examples/danzig-gain-standalone/). Offline WAV gain processing. | `zig build run-standalone` |
| [danzig-webui](examples/danzig-webui/). An HTTP server in pure `std.net`. | `./zig-out/bin/danzig-webui` |
| [danzig-gain-ui](examples/danzig-gain-ui/). Native macOS window, WebView plus CoreAudio. | `zig build run-gui` |

No hidden allocations - explicit memory management throughout.
See [examples/README.md](examples/README.md) for the index.

## Build Artifacts
## Build artifacts

After building, you'll find:
`zig build` installs:

```
zig-out/
├── lib/
│ ├── libdanzig.a (2.3 KB) - Static library
│ ├── libDanzigGain.dylib (17 KB) - Compiled plugin
│ └── libdanzig_gain.dylib
├── bin/
│ └── danzig_test (208 KB) - Test executable
├── DanzigGain.vst3/ - VST3 bundle for macOS
│ └── Contents/MacOS/DanzigGain
│ ├── danzig-minimal offline demo of the minimal plugin
│ ├── danzig-gain-standalone WAV gain processor
│ ├── danzig-webui HTTP server for the web UI
│ ├── danzig-gain-ui native window (macOS)
│ └── danzig_test VST3 ABI integration harness
└── lib/
├── libDanzigGain.dylib gain plugin, native arch
├── libDanzigGain_arm64.dylib gain plugin, arm64
├── libDanzigGain_x86.dylib gain plugin, x86_64
└── libDanzigMinimal.dylib minimal plugin, native arch
```

## Key Features

✨ **Zero Dependencies** - Only Zig stdlib
✨ **Type-Safe** - Full compile-time checking
✨ **No Hidden Allocations** - Explicit memory management
✨ **Production-Ready** - Fully tested and documented
✨ **Modern Zig** - Using latest Zig patterns and idioms
`zig build vst3` adds the bundle:

## Plugin Features (Gain Example)

- Stereo input/output
- -48 to +48 dB gain range
- Smooth parameter ramping
- Sample-rate aware processing
- Memory pre-allocation

## System Requirements

- Zig 0.14.1 or 0.15.2
- macOS (currently Mach-O format)
- VST3-compatible DAW

## Getting Started with Development

1. Read [docs/INDEX.md](docs/INDEX.md)
2. Check out `examples/danzig-gain/root.zig`
3. Copy the example as a template
4. Implement your plugin logic in the `process()` method
5. Rebuild and test

## Documentation Map
```
zig-out/DanzigGain.vst3/
└── Contents/
├── Info.plist
├── PkgInfo
└── MacOS/
└── DanzigGain universal arm64 + x86_64
```

- **[INDEX.md](docs/INDEX.md)** - Navigation and quick reference
- **[Danzig-Complete-Guide.md](docs/Danzig-Complete-Guide.md)** - Full tutorial
- **[VST3-Architecture.md](docs/VST3-Architecture.md)** - Deep technical dive
- **[Real-World-Guide.md](docs/Real-World-Guide.md)** - Practical examples
`libdanzig.a` is not installed. The static library is linked into each target
rather than shipped on its own.

## Testing with pluginval
Sizes, from `zig build -Doptimize=ReleaseFast`:

The plugin has been built and signed correctly. For advanced testing:
| Artifact | Size |
|---|---|
| `DanzigGain.vst3/Contents/MacOS/DanzigGain` | 84 KB (universal) |
| `lib/libDanzigGain.dylib` | 52 KB |
| `lib/libDanzigMinimal.dylib` | 52 KB |
| `bin/danzig-minimal` | 168 KB |
| `bin/danzig-gain-standalone` | 288 KB |
| `bin/danzig-gain-ui` | 572 KB |

```bash
# Verify plugin exports entry point
nm zig-out/lib/libDanzigGain.dylib | grep GetPluginFactory
The default Debug build produces the same artifacts at roughly 1 to 2 MB each.

# Check code signature
codesign -vvv ~/Library/Audio/Plug-Ins/VST3/DanzigGain.vst3
```
## Current state

See [PLUGINVAL_REPORT.md](PLUGINVAL_REPORT.md) for detailed validation results.
The core library, the tests, the build pipeline, and the non-plugin examples all
work. The VST3 factory in `examples/danzig-gain` is a stub: `getClassInfo` and
`createInstance` do not yet produce a class or an object, so a host scans the
bundle and reports zero plugins. See
[Current state](docs/WIKI.md#current-state) for the detail and for what
finishing it involves.

## Next Steps
## Documentation

- Modify `examples/danzig-gain/root.zig` to create your own plugin
- Add new audio processing to `src/audio.zig` for common effects
- Test in your favorite DAW
- Share your creations!
- **[docs/WIKI.md](docs/WIKI.md)**. The single-page guide. Architecture,
quickstart, parameters, audio helpers, bundle packaging, testing,
troubleshooting.
- [docs/INDEX.md](docs/INDEX.md). The older multi-page docs.

## License

MIT. See [LICENSE](LICENSE).

---

Built with ❤️ in Zig | Framework for VST3 plugin development
VST is a trademark of Steinberg Media Technologies GmbH. danzig vendors no
Steinberg SDK code, and shipping plugins in VST3 format is governed by
Steinberg's own terms, independently of danzig's MIT license.
37 changes: 36 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ pub fn build(b: *std.Build) void {
// Install to zig-out/lib/
b.installArtifact(danzig_gain);

// Test executable
// Integration harness. Links the plugin itself so it can call the exported
// GetPluginFactory entry point and drive it through the raw VST3 C ABI.
const danzig_test = b.addExecutable(.{
.name = "danzig_test",
.root_module = b.createModule(.{
Expand All @@ -47,9 +48,43 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
}),
});
danzig_test.root_module.addImport("danzig", danzig_module);
danzig_test.linkLibrary(danzig_lib);
danzig_test.linkLibrary(danzig_gain);
b.installArtifact(danzig_test);

// Minimal plugin template. Built twice from one source: as the shared
// library a host would load, and as an executable so the DSP can be run
// and checked without a host.
const danzig_minimal = b.addLibrary(.{
.name = "DanzigMinimal",
.root_module = b.createModule(.{
.root_source_file = b.path("examples/danzig-minimal/root.zig"),
.target = target,
.optimize = optimize,
}),
.linkage = .dynamic,
});
danzig_minimal.root_module.addImport("danzig", danzig_module);
danzig_minimal.linkLibrary(danzig_lib);
b.installArtifact(danzig_minimal);

const danzig_minimal_demo = b.addExecutable(.{
.name = "danzig-minimal",
.root_module = b.createModule(.{
.root_source_file = b.path("examples/danzig-minimal/root.zig"),
.target = target,
.optimize = optimize,
}),
});
danzig_minimal_demo.root_module.addImport("danzig", danzig_module);
danzig_minimal_demo.linkLibrary(danzig_lib);
b.installArtifact(danzig_minimal_demo);

const run_minimal = b.addRunArtifact(danzig_minimal_demo);
const run_minimal_step = b.step("run-minimal", "Run the minimal plugin template offline");
run_minimal_step.dependOn(&run_minimal.step);

// Standalone audio processor
const danzig_gain_standalone = b.addExecutable(.{
.name = "danzig-gain-standalone",
Expand Down
Loading
Loading