Skip to content

Writing a GeneratePlugin¤

We'll go step by step through all the files you need to create and what they'll do.

Remember the directory structure from before:

├── README.md
├── pyproject.toml
├── setup.py
├── src
│   └── nendo_plugin_destroy
│       ├── __init__.py
│       ├── config.py
│       └── plugin.py

Let's look into the different files now.

Files¤

plugin.py¤

The most important thing in your plugin. Here is where all your magic happens. I took the liberty to implement a simple plugin that generates a new track or extends an existing track based on some crazy shit. We'll go through it step by step.

from logging import Logger
from typing import Optional

import numpy as np
from nendo import Nendo, NendoConfig, NendoGeneratePlugin, NendoTrack


class DestructionGenerator(NendoGeneratePlugin):
    """Generate some weird sounds."""
    nendo_instance: Nendo = None
    config: NendoConfig = None
    logger: Logger = None

    @NendoGeneratePlugin.run_signal
    def generate_fuck_up(self, track: Optional[NendoTrack] = None, destruction_factor: float = 0.2) -> NendoTrack:
        sr = 44100
        if track is None:
            # "generate" audio from scratch
            signal = np.random.rand(2, sr * 30) * destruction_factor
            return self.nendo_instance.library.add_track_from_signal(signal=signal, sr=sr)

        # do some "outpainting"
        new_signal = np.vstack((track.signal, np.random.rand(2, sr * 30)))
        return self.nendo_instance.library.add_related_track_from_signal(
            signal=new_signal,
            sr=sr,
            related_track_id=track.id
        )

If you used the plugin generation script you should already have most of this setup.

If not (why????) then the basics are very simple: Make sure to extend NendoGeneratePlugin, and make sure that your extended class has at least one method that is decorated with @NendoGeneratePlugin.run_signal, @NendoGeneratePlugin.run_track or @NendoGeneratePlugin.run_collection.

Note

For NendoGeneratePlugin's we recommend using @run_track because you might want to save some metadata to the track as well.

config.py¤

from nendo import NendoConfig
class DestructionConfig(NendoConfig):
    """Configuration defaults for the destroy plugin."""
    model_config = ConfigDict(arbitrary_types_allowed=True)

    my_default_param: bool = False

This class extends the base NendoConfig and allows you to define some default and overridable parameters for your plugin. It behaves just like NendoConfig, read up more on basic nendo configuration here.

setup.py¤

from distutils.core import setup
if __name__ == "__main__":
    setup(
        name="nendo-plugin-destruct",
        version="0.1.0",
        description="Nendo destroyer plugin",
        author="Aaron Abebe <aaron@okio.ai>",
    )

This is a standard setup.py file. You can read up more on how to configure it here. You just need to define some basics like the name of your plugin, a version number and a description. Make sure to also enter the same version in src/nendo_plugin_destroy/__init__.py.

pyproject.toml¤

All nendo plugins use pyproject.toml to manage their dependencies. You can read up more on how to configure it here.

Finished plugin¤

That's it!

Success

When you're finished go back to the plugin development overview and learn how to test and publish your plugin.