DROID patch generator
Project description
Introduction
The DROID metapatch library aims to create an easier and more pythonic way of creating DROID patch generators. DROID patch generators are a recent feature added to the the DROID Forge application that allows you to define python scripts that generate full-fledges patches.
DROID is a series of eurorack controllers and CV processors made by Der Mann mit der Maschine.
Getting started
A few examples have been distributed with the source code of this library that you may look at to see full implementations.
Before you can write a patch generator, please install this library to your system python environment. Forge does not support virtual environments, so you must install this library somewhere where your default python library can find it.
Defining your patch generator class
To start, you must define a python class
that is a subclass of metapatch.PatchGenerator
.
Inside this class, you must at least define a title
and a description
, as well as a method called generate
.
This method should contain all the logic needed to create your patch. To actually define the patch, the class instance provides functions that allow you to add circuits, controllers and even sections.
import metapatch
class MyPatchGenerator(metapatch.PatchGenerator):
"""My Patch Generator."""
title = "My patch generator."
description = "This is an example patch generator."
def generate(self) -> None:
"""Generate a patch."""
Capturing parameters
The patch generator framework supports three types of options:
- Boolean (on/off)
- A range of numbers
- Choosing string values from a drop-down list (enumerations)
These are quite essential to make your patch generator dynamic, and can be defined in the class with the option
function.
class MyPatchGenerator(metapatch.PatchGenerator):
"""My Patch Generator."""
title = "My patch generator."
description = "This is an example patch generator."
# Parameters
midi_channel: int = metapatch.option("MIDI channel to use", minimum=1, maximum=16)
clock_source: str = metapatch.option(
"Clock source, internal or external",
choices=[
("internal", "Generate a clock with a DROID circuit"),
("external", "Receive clock via CV"),
("midi", "Receive a clock via MIDI")
]
)
copy_clock: bool = metapatch.option("Copy clock to an output")
def generate(self) -> None:
"""Generate a patch."""
if self.clock_source == "external":
# Do something here
In this example, we see all three option types. To make your text editor be able to help you, we use type hints
on our parameters.
We can now access those parameters from inside our generate()
function like shown before.
You can also specify default values for parameters, by adding default=defaultvalue
. If no default value is specified, we use the following logic:
- The first provided choice for enumerations
- True for booleans
- The lowest number for number ranges.
Capturing presets
I’ve defined presets as a defined set of parameters with a title.
You define your presets in much the same way as your parameters:
class MyPatchGenerator(metapatch.PatchGenerator):
"""My Patch Generator."""
title = "My patch generator."
description = "This is an example patch generator."
# Parameters
midi_channel: int = metapatch.option("MIDI channel to use", minimum=1, maximum=16)
clock_source: str = metapatch.option(
"Clock source, internal or external",
choices=[
("internal", "Generate a clock with a DROID circuit"),
("external", "Receive clock via CV"),
("midi", "Receive a clock via MIDI")
]
)
copy_clock: bool = metapatch.option("Copy clock to an output")
default = metapatch.preset(
"The default preset",
{
"midi_channel": 11,
"clock_source": "internal",
"copy_clock": False,
}
)
Any value provided to the preset()
function dictionary will be passed to the patch generator if that particular preset is loaded.
Writing patches
The following actions are currently supported:
- Adding controllers
- Adding circuits
- Adding sections
Adding controllers
def add_controller(self, type: str, position: int) -> None:
"""Add a controller at a given position.
Args:
type: Type of controller, e.g. B32
position: controller position, e.g. 1
"""
Controllers must be added with a position parameter. So if you want to add a P2B8
module in the first position, you can can write as follows:
self.add_controller("P2B8", 1)
Adding circuits
def add_circuit(
self,
name: str,
params: Mapping[str, str],
comment: Optional[str] = None,
) -> None:
"""Add a circuit.
Args:
name: Circuit name, e.g. copy for a [copy] circuit
params: Dictionary of circuit parameters.
comment: Optional comment for the circuit.
"""
To add a simple copy
circuit:
self.add_circuit("copy", {"input": "I1", "output": "O1"}, "This circuit copies from I1 to O1")
The comment is optional.
Splitting your patch into sections
Sections are a great way to split up a patch into smaller chunks that may be easier to read.
You can define sections as you write your patch:
class MyPatch(metapatch.PatchGenerator):
title = "Example"
description = "Example"
voices: int = metapatch.option("Number of voices", minimum=1, maximum=4)
def generate(self) -> None:
"""Generate patch."""
# Section with a comment
self.add_section("Master Clock", "This section contains the master clock configuration.")
# Add your circuits to this section.
self.add_circuit("lfo", {...})
# Iterate over voices that were defined
for voicenum in range(1, self.voices + 1):
self.add_section(f"Voice {voicenum}")
# Add circuits here.
Finishing your patch
Once you are done writing your code, you just need to make sure that the patch generator is loaded when executing the module.
This can be done by simply calling run()
on your class.
class MyPatch(metapatch.PatchGenerator):
title = "Example"
description = "Example"
def generate(self) -> None:
"""Patch generator function."""
# Patch generator logic here
MyPatch.run()
This automatically sets up processing of command line arguments, correct argument passing and so on.
Missing features
Currently there is no way to define the labels of jacks and pots. This will come soon.
Getting help
If you have any problems with the library, let me know on the DROID discord server. I’m known as eising on that server.
Please note that any support will be on a best effort basis, if I have the time an energy.
If you have found a bug, please create an issue here on github.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for droid_metapatch-0.9.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | be0532659e63455164ef8ce399fdf1f33777eb27012146ab86dae33b5c2ad66d |
|
MD5 | 7662409f69f75b1d43a649594728a659 |
|
BLAKE2b-256 | fa7d009cc43ffbf7fdcb4b4511e50560bd6c053c54d224c98a2cdef64c64222e |