No description
  • C++ 96.5%
  • CMake 3.4%
  • Makefile 0.1%
Find a file
Chris Cowley 8350c31b11
All checks were successful
CI / Core tests (push) Successful in 22s
CI / LV2 plugin (push) Successful in 25s
Refactored to make space for documentation
2026-11-03 20:46:58 +01:00
.forgejo/workflows Add LV2 MIDI smoke test and CI coverage 2026-11-03 18:22:59 +01:00
.omo/run-continuation first commit 2026-11-02 19:06:55 +01:00
contributors Refactored to make space for documentation 2026-11-03 20:46:58 +01:00
core Phase 1 complete 2026-11-03 20:06:14 +01:00
data/personas Implement P1.8 persona JSON schema, loader, and validator 2026-11-03 17:11:03 +01:00
documentation Refactored to make space for documentation 2026-11-03 20:46:58 +01:00
src Fix LV2 MIDI emission and finalize P1.9 pattern engine 2026-11-03 18:14:01 +01:00
tests Phase 1 complete 2026-11-03 20:06:14 +01:00
.gitignore Refactored to make space for documentation 2026-11-03 20:46:58 +01:00
AGENTS.md Refactored to make space for documentation 2026-11-03 20:46:58 +01:00
CMakeLists.txt Phase 1 complete 2026-11-03 20:06:14 +01:00
LICENSE Clean up 2026-11-02 22:27:42 +01:00
Makefile Refactored to make space for documentation 2026-11-03 20:46:58 +01:00
README.md Refactored to make space for documentation 2026-11-03 20:46:58 +01:00
zensical.toml Refactored to make space for documentation 2026-11-03 20:46:58 +01:00

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:Position and time: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-config for 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.

  1. Create a MIDI track and add a drum sampler such as DrumGizmo to it.
  2. Add Fill-In to the same track's processor chain, above the sampler.
  3. Set MIDI Channel to match the sampler (10 by default).
  4. 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.lv2 is in ~/.lv2 or /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:Position from 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 0 to 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:

  • test builds the core without JACK, GTK, or LV2 and runs CTest.
  • lv2 builds the LV2 bundle in Release mode, validates it with lv2_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 with zensical build --clean (output goes to site/) or preview locally with zensical serve --dev-addr 127.0.0.1:8000. (Binding to localhost can fail on some systems; use the loopback IP.) Hosting is still to be decided.

License

MIT. See LICENSE. The LV2 manifest also declares MIT.