- C++ 96.5%
- CMake 3.4%
- Makefile 0.1%
| .forgejo/workflows | ||
| .omo/run-continuation | ||
| contributors | ||
| core | ||
| data/personas | ||
| documentation | ||
| src | ||
| tests | ||
| .gitignore | ||
| AGENTS.md | ||
| CMakeLists.txt | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| zensical.toml | ||
Fill-In
Fill-In is a MIDI drum-groove generator for Linux. It runs as an LV2 plugin in Ardour (or another LV2 host) and writes a drum pattern into a MIDI track. It does not make sound: put a drum sampler such as DrumGizmo after it.
It is a placeholder drummer for songwriting and practice. It gives you a part to play or write against until a real drummer takes over.
Status: early prototype. The plugin currently generates a fixed 4/4 placeholder groove with a few controls. Swing is implemented in the core; personas, genres, fills, and humanisation are not implemented yet. See contributors/PLAN.md for the live status.
What it does
- Generates MIDI in sync with the host transport and tempo.
- Emits kick, snare, closed hi-hat, and crash notes on MIDI channel 10 by default.
- Follows the host's
time:Positionandtime:beatsPerMinute; it does not free-run in LV2 mode. - Lets you change MIDI channel, output velocity, complexity, and intensity from the host.
- Sends note-offs for queued notes when the transport stops, so notes do not hang.
- Ships with an optional standalone JACK client for quick routing tests and MIDI capture.
It is not a drum sampler, a mixing tool, or a finished performance. It only writes MIDI.
Requirements
- Linux
- CMake 3.20 or newer
- Ninja, or another CMake generator
- A C++20 compiler (GCC or Clang)
- LV2 development headers and
pkg-configfor the plugin - Optional: JACK and GTK3 development headers for the standalone client
- An LV2 host (Ardour is the main target) and a drum sampler to hear the result
On Debian/Ubuntu, the plugin dependencies are:
sudo apt install build-essential cmake ninja-build pkg-config lv2-dev
On Fedora:
sudo dnf install gcc-c++ cmake ninja-build pkgconf-pkg-config lv2-devel
For the optional JACK client, install your distribution's JACK and GTK3 development packages. The pkg-config names are jack and gtk+-3.0.
Build
Build just the LV2 plugin, with no JACK or GTK dependency:
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DFILLIN_BUILD_JACK=OFF
cmake --build build
Run the unit tests:
ctest --test-dir build --output-on-failure
Build everything, including the optional JACK client:
cmake -S . -B build-jack -G Ninja -DCMAKE_BUILD_TYPE=Release -DFILLIN_BUILD_JACK=ON
cmake --build build-jack
For a core-only test build with no LV2, JACK, or GTK dependencies:
cmake -S . -B build-core -G Ninja -DCMAKE_BUILD_TYPE=Debug \
-DFILLIN_BUILD_JACK=OFF -DFILLIN_BUILD_LV2=OFF
cmake --build build-core
ctest --test-dir build-core --output-on-failure
Install the plugin
System-wide, which installs the bundle to /usr/local/lib/lv2/fillin.lv2:
sudo cmake --install build
If you built into build-jack, replace build with build-jack in the install commands. For the current user only:
mkdir -p ~/.lv2
cp -r build/fillin.lv2 ~/.lv2/
Restart Ardour, or rescan plugins, and search for Fill-In.
Use in Ardour
Fill-In is a MIDI generator, so it needs an instrument after it.
- Create a MIDI track and add a drum sampler such as DrumGizmo to it.
- Add Fill-In to the same track's processor chain, above the sampler.
- Set MIDI Channel to match the sampler (10 by default).
- Start the transport. Fill-In follows Ardour's transport and tempo.
If you prefer to keep the generator on its own track, put Fill-In on a MIDI track and route that track's MIDI output to the sampler track's MIDI input.
Fill-In does not free-run in LV2 mode. If the transport is stopped, it generates nothing. When you stop the transport, it sends note-offs for notes that were still sounding.
Controls
| Control | Range | Default | What it does |
|---|---|---|---|
| Enable | 0–1 | 1 | Turns generation on or off. |
| Host BPM | 20–300 | 120 | Tempo reported by the host through time:beatsPerMinute. You usually do not need to touch this. |
| BPM Override | 0–300 | 0 | 0 follows the host. A non-zero value fixes the tempo; values below 20 are clamped to 20. |
| MIDI Channel | 1–16 | 10 | MIDI channel for the generated notes. |
| Velocity | 0–1 | 1.0 | Scales the generated velocities. MIDI velocity is clamped to 1–127. |
| Complexity | 0–100 | 50 | Increases hi-hat subdivision and adds extra kicks and ghost snares. |
| Intensity | 0–100 | 50 | Raises or lowers the velocity of the generated notes. |
| Swing | 0–100 | 50 | Shifts the second note in each swing pair. 50 is straight, 66.7 is triplet feel, 100 is extreme. |
| Humanise | 0–100 | 0 | Adds deterministic timing and velocity variation. 0 is mechanical. |
| Feel | 0–100 | 50 | Push/pull leaning. Values below 50 push; values above 50 lay back. |
The plugin also has events_in and events_out atom ports. The host writes time:Position to events_in; events_out carries midi:MidiEvent messages to the instrument.
The current placeholder pattern
The generator currently uses a fixed 4/4 pattern at 960 PPQ with 32 steps per bar:
- Kick on beats 1 and 3.
- Snare on beats 2 and 4.
- Closed hi-hat, with density controlled by Complexity.
- Extra kicks and ghost snares at higher complexity.
- A crash on the first beat of every fourth bar.
This is deliberately simple. It is the scaffolding for the persona and feel work in the plan, not the final musical content.
Troubleshooting
- Fill-In is not in Ardour's plugin list. Check that
fillin.lv2is in~/.lv2or/usr/local/lib/lv2, then rescan plugins or restart Ardour. - The transport runs but there is no sound. Fill-In emits MIDI only. Put a drum sampler after it and make sure MIDI Channel matches the sampler.
- The track is silent while stopped. That is expected in LV2 mode. Start the transport.
- There are no notes at all. The plugin follows
time:Positionfrom the host and does not free-run. Check that the host is sending transport position, not just tempo. - The tempo is wrong. Set BPM Override to
0to follow the host, or set it to the tempo you want. - Notes hang after stopping. Stop and restart the transport. The plugin queues note-offs for notes that were sounding when the transport stopped.
Capturing the generated MIDI
Ardour records track inputs, not the output of a generator plugin. To capture the pattern as a MIDI file, build the JACK client and use PipeWire's recorder:
cmake -S . -B build-jack -G Ninja -DCMAKE_BUILD_TYPE=Release -DFILLIN_BUILD_JACK=ON
cmake --build build-jack
pw-midirecord --media-type midi -M midi --target 0 /tmp/fill-in.mid &
./build-jack/fillin_jack
pw-link "fill-in:midi_out" "pw-midirecord:input_1"
Stop the recorder with Ctrl+C to flush the .mid file. Port names vary; list them with pw-link -i.
Optional JACK client
fillin_jack is a thin JACK/GTK3 client over the same generator core. It registers the port fill-in:midi_out and provides a small window with a complexity/intensity pad, a BPM slider, and a Generate button.
cmake -S . -B build-jack -G Ninja -DCMAKE_BUILD_TYPE=Release -DFILLIN_BUILD_JACK=ON
cmake --build build-jack
./build-jack/fillin_jack
It requires the JACK and GTK3 development packages. Unlike the LV2 plugin, the JACK client can free-run when no JACK transport is available, which makes it useful for quick tests.
Testing and CI
The core has CTest coverage for the timebase, RNG, event model, pattern cursor, renderer, persona loader, property invariants, and golden-hash determinism:
ctest --test-dir build --output-on-failure
Forgejo Actions runs on every push and pull request through .forgejo/workflows/ci.yml:
testbuilds the core without JACK, GTK, or LV2 and runs CTest.lv2builds the LV2 bundle in Release mode, validates it withlv2_validate, and uploads the bundle as an artifact.
Project layout
| Path | What it is |
|---|---|
core/ |
Host-independent C++20 generator: timebase, RNG, events, and pattern cursor. |
src/lv2_plugin.cpp |
LV2 adapter: atom ports, host sync, and MIDI output. |
src/jack_client.cpp |
Optional JACK/GTK3 adapter. |
tests/ |
CTest unit tests for the core. |
contributors/ |
Design notes, task plan, research, and ADRs. |
Documentation
- contributors/PLAN.md — current status, milestones, and next work.
- contributors/RESEARCH.md — prior art and technical notes.
- contributors/adr/ — architecture decisions, including the LV2 delivery decision.
- User-facing site is scaffolded with Zensical under the top-level
documentation/directory. Build it withzensical build --clean(output goes tosite/) or preview locally withzensical serve --dev-addr 127.0.0.1:8000. (Binding tolocalhostcan fail on some systems; use the loopback IP.) Hosting is still to be decided.
License
MIT. See LICENSE. The LV2 manifest also declares MIT.