Start from the template
The template plugin is a complete plugin you copy and change. It lives in OxiGuitar’s repository as docs/plugins/template/, and adds one of each common thing:
- a format, “Example tab” (
.extab): a tiny text format of eighth notes on a guitar, read and written; - a tab, “Statistics”, in the right area: how many notes each string of a track plays in the open score, as a small chart you can click, with a setting remembered in settings.json;
- a service,
note-names, which other plugins can call to name a pitch.
Each is one file in src/; you delete what you don’t need.
1. Copy it
Copy the folder, hidden .github/ included, to wherever you keep your projects. With git:
git clone --depth 1 --branch v0.9.0 https://github.com/mateusrdp/oxiguitar
cp -R oxiguitar/docs/plugins/template my-plugin
rm -rf oxiguitar
cd my-plugin
The template’s Cargo.toml takes the SDK and the contract suite from OxiGuitar’s repository on GitHub, at the same tag, v0.9.0 (Cargo downloads the repository the first time). The copy in OxiGuitar 0.9.0 still names v0.5.0: change both to v0.9.0, which has interface 1.3 (Interface versions). The template asks for interface 1.1, so what you build loads in OxiGuitar 0.2 and later; ask for 1.2 when you use its tools or the score version 2, and it needs 0.5; ask for 1.3 for a settings window, a section of the track dialog, footer links, the plugin’s folders, instruments, effects or audio devices, and it needs 0.9. To build for a later OxiGuitar, change both tags.
2. Build and test it
cargo test
The first run downloads and builds the SDK and the contract suite, which takes a minute. Then it runs:
- the template’s own unit tests (
src/*.rs); the_manifest_matches_the_plugin, a testexport_plugin!adds: the manifest is valid, its version is the crate’s, and every format, service and tab it declares is implemented, with contents the core can show;tests/contract.rs, which builds the plugin filetarget/oxp/example.oxpand checks it through OxiGuitar’s own plugin host (Testing).
3. Try it in OxiGuitar
Copy the file into your plugin folder (OxiGuitar creates it the first time it starts):
# Linux
cp target/oxp/example.oxp ~/.local/share/oxiguitar/plugins/
# macOS
cp target/oxp/example.oxp ~/Library/Application\ Support/OxiGuitar/plugins/
On Windows, copy target\oxp\example.oxp to %APPDATA%\OxiGuitar\plugins\.
Start OxiGuitar. Tools → Plugins lists “Example”, not loaded yet. Open samples/open-strings.extab (File → Open): the plugin loads and reads it (oxiguitar-cli info samples/open-strings.extab also prints its import log, one warning). The right area has a Statistics tab, known from the manifest before the plugin loaded; open it, and click a bar of its chart.
4. Make it yours
- Rename it. In
Cargo.toml, the package’sname. Inmanifest.json:id(lowercase letters and digits joined by.or-, starting with your own name:jane.chord-finder),name,description,authors,licenseandhomepage. Inbuild-oxp.sh,NAME(your file’s name) andLIBRARY(the package’s name with_for-); in.github/workflows/build.yml,NAMEagain. Intests/contract.rs, the file’s name. - Delete what you don’t need. Each contribution is a module, a line in
src/lib.rs’simpl Plugin, and an entry inmanifest.json. Remove all three;cargo testsays if the manifest and the code disagree. - Write your own. The next pages explain each part: the manifest, the SDK, formats, tabs, events, services, settings.
- Keep the versions equal. The version in
manifest.jsonmust be the crate’s, and you raise both with each release: when two files of the same plugin are installed, OxiGuitar uses the higher version.
What’s in the folder
| File | What it is |
|---|---|
Cargo.toml | The crate: the oxp feature, the SDK, the contract suite for tests, lints, a release profile for small files |
manifest.json | What the plugin adds, read by OxiGuitar without loading the plugin |
src/lib.rs | The plugin type, which hands out each contribution, and export_plugin! |
src/format.rs | The Example tab format |
src/statistics.rs | The Statistics tab |
src/service.rs | The note-names service |
src/notes.rs | Note names and standard tuning, shared by the last two |
tests/contract.rs | Builds the .oxp file and runs the contract suite on it |
samples/open-strings.extab | A file to open |
build-oxp.sh | Builds the release file for this system, dist/NAME-VERSION-SYSTEM-ARCH.oxp (Building for every system) |
.github/workflows/build.yml | Builds and tests the plugin for every system on GitHub, and attaches the files to a release |
Here is src/lib.rs, the whole plugin in one place:
/// The plugin: built once, when OxiGuitar first uses one of its
/// contributions, and kept until the program ends.
pub struct Example {
statistics: statistics::Statistics,
}
impl Plugin for Example {
fn start(host: Host) -> Result<Example, Error> {
Ok(Example {
statistics: statistics::Statistics::new(host),
})
}
// Each method hands out a contribution the manifest declares, by its id.
fn reader(&self, format: &str) -> Option<&dyn FormatReader> {
(format == format::ID).then_some(&format::ExampleTab as &dyn FormatReader)
}
fn writer(&self, format: &str) -> Option<&dyn FormatWriter> {
(format == format::ID).then_some(&format::ExampleTab as &dyn FormatWriter)
}
fn service(&self, interface: &str, id: &str) -> Option<&dyn Service> {
(interface == service::INTERFACE && id == service::ID)
.then_some(&service::NoteNames as &dyn Service)
}
fn tab(&self, id: &str) -> Option<&dyn Tab> {
(id == statistics::ID).then_some(&self.statistics as &dyn Tab)
}
fn event(&self, event: &oxiguitar_plugin::Event) {
self.statistics.event(event);
}
}
// The plugin's entry point and manifest, and a test (under `cargo test`)
// that the manifest matches the code.
oxiguitar_plugin::export_plugin!(Example);