Public SiteDocumentation
 

JPatch - Module Descriptions v1.3

Introduction

Module Descriptions is a XML format which can be used to provide a comprehensive documentation of the modules of a synthesizer. The format is designed to make automated generation of modules and their user interface easy.

Tutorial

Introduction

This tutorial describes step by step how to write the module descriptions xml file. For this tutorial we describe the properties of the Waldorf Miniworks 4 Pole Filter. The documentation is available in the archives at http://www.waldorfmusic.de (direkt link).

First Step

We start with a new file containing ModuleDescriptions as the root element where the current version of the format version="1.3" is specified.

<?xml version="1.0" encoding="utf-8"?>
<ModuleDescriptions version="1.3"
  xmlns="http://nmedit.sf.net/ns/ModuleDescriptions"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  ...
</ModuleDescriptions>

ModuleDescriptions - attribute overview

attribute use description
versionrequiredcurrent format version: 1.3

Document Header

Then we declare the header where global properties are defined. The first line contains the vendor of the ModuleDescriptions document. The element optionally allows to specify the vendor's homepage using the url-attribute. The second line contains the device description. Containing the model-name, device-vendor and optionally some version-information.

At the end of the device- or header-element it is optionally possible to specify custom properties.

<?xml version="1.0" encoding="utf-8"?>
<ModuleDescriptions version="1.3"
  xmlns="http://nmedit.sf.net/ns/ModuleDescriptions"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<header>
  <vendor url="http://nmedit.sf.net">NMedit Project</vendor>
  <device>
    <model>miniWORKS 4pole</model>
    <vendor url="http://www.waldorfmusic.de/">Waldorf Music</vendor>
    <version type="os-version">1.0</version>
  </device>
  <property name="author">Christian Schneider</property>
</header>
  ...
</ModuleDescriptions>

vendor - attribute overview

attribute use description
urloptionalvendor url

version - attribute overview

attribute use description
typerequiredversion

property - attribute overview

attribute use description
namerequiredname of the property

Annotations

The annotation section allows to add comments, license / copyright information, a changelog or other useful information. The element comes after the header element. Here is some example:

<?xml version="1.0" encoding="utf-8"?>
<ModuleDescriptions version="1.3"
  xmlns="http://nmedit.sf.net/ns/ModuleDescriptions"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<header> ... </header>
<annotation>
  <section>
    <title>License</title>
    <pre>
      <!-- here should be the license in preformattet text -->
    </pre>
  </section>
  <section>
    <title>Misc</title>
    This is some inline <code>code</code> that should be rendered in an appropriate
    font (monospace).
    A simple <link href="http://nmedit.sf.net">http://nmedit.sf.net</link>.
    A mail adress: <mail mailto="someone@somewhere.net">someone@somewhere.net</mail>.
    A simple list:
    <list>
      <item>one</item>
      <item>two</item>
      <item>three</item>
    </list>
  </section>

</annotation>
</ModuleDescriptions>

Defining custom types

After the header we have the optional defs-element where custom types can be defined. Custom types can be used

  • as type in the attribute-element
  • as formatter of a parameter
<?xml version="1.0" encoding="utf-8"?>
<ModuleDescriptions version="1.3"
  xmlns="http://nmedit.sf.net/ns/ModuleDescriptions"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<header> ... </header>
<defs>
  <def-type name="tmLFOShape" >
    <enumeration key="0" value="sin"/>
    <enumeration key="1" value="tri"/>
    <enumeration key="2" value="saw"/>
    <enumeration key="3" value="pls"/>
    <enumeration key="4" value="S-H"/>
  </def-type>
  <def-type name="tmTriggerSource" >
    <enumeration key="0" value="Audio"/>
    <enumeration key="1" value="MIDI"/>
    <enumeration key="2" value="All"/>
  </def-type>
  <def-type name="tmTriggerMode" >
    <enumeration key="0" value="Multi"/>
    <enumeration key="1" value="Single"/>
  </def-type>
  <def-type name="tmModulationSource" >
    <enumeration key="0" value="off"/>
    <enumeration key="1" value="LFO"/>
    <enumeration key="2" value="LFO * ModW."/>
    <enumeration key="3" value="LFO * Aftertouch"/>
    <enumeration key="4" value="LFO * VCA Env"/>
    <enumeration key="5" value="VCF Env"/>
    <enumeration key="6" value="VCA Env"/>
    <enumeration key="7" value="Signal Env"/>
    <enumeration key="8" value="Vel * VCA Env"/>
    <enumeration key="9" value="Velocity"/>
    <enumeration key="10" value="Keytrack"/>
    <enumeration key="11" value="Pitch Bend"/>
    <enumeration key="12" value="Modwheel"/>
    <enumeration key="13" value="Aftertouch"/>
    <enumeration key="14" value="Breath Ctr."/>
    <enumeration key="15" value="Foot Ctr."/>
  </def-type>
</defs>
  ...
</ModuleDescriptions>

In this example we have defined the three types tmLFOShape, tmTriggerSource and tmTriggerMode. Each type contain several enumeration-elements with a key and a string representation of the key. The use of custom types will be described later.

def-type - attribute overview

attribute use description
namerequiredname of the type

enumeration attribute overview

attribute use description
keyrequireda key that is associated with the value
valuerequiredthe value

Signal types

Depending on the synthesizer some modules may have connectors. Such connectors can have different output signals for example audio, control or logic signals. For such cases it is possible to define custom signal types.

<?xml version="1.0" encoding="utf-8"?>
<ModuleDescriptions version="1.3"
  xmlns="http://nmedit.sf.net/ns/ModuleDescriptions"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<header> ... </header>
<defs>
  <def-signal>
    <signal key="0" type="audio" />
    <signal key="1" type="logic" />
    <signal key="2" type="control" />
    <signal key="3" type="master-slave" />
  </def-signal>
  
  <!-- other defs ... -->
  ...
  </def-type>
</defs>
  ...
</ModuleDescriptions>

The example shows the definition of four signal types: audio, logic, control and master-slave. Each signal type is associated with a number (key-attribute).

signal - attribute overview

attribute use description
keyrequiredinteger value associated with the signal type
typerequiredtype/name of the signal

Modules I

Inside the body-element it is possible to define an arbitrary number of modules. In the example we only have one module representing the miniWorks filter. The module-element contains a comment which describes the module. Comments are also allowed inside parameter- and connector-Elements.

<?xml version="1.0" encoding="utf-8"?>
<ModuleDescriptions version="1.3"
  xmlns="http://nmedit.sf.net/ns/ModuleDescriptions"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<header> ... </header>
<defs> ... </defs>
<body>
  <module name="miniWorks 4pole" >
    <comment>
      This module represents the Waldorf miniWorks 4pole filter.
    </comment>
    ...
  </module>
</body>
</ModuleDescriptions>

module - attribute overview

attribute use description
namerequiredmodule name
display-nameoptionalprovides a short version of the module name. If not specified the name-value is used as display name
idoptionalelement id
keyoptionalIdentifies the current module. No other module can have the same key. If not specified the name-value is used as key
categoryoptional name of the category this module belongs to
category-idoptional id of the category this module belongs to
classoptional optional attribute that can be used to associate a module with a class of modules

Parameters

Before we can define the module we have to take a look at the properties of the module. The column byte means the parameter-offset in midi messages, value is the valid parameter range and description contains the parameter name and optionally the description of a parameter value.

Byte #    Value     Description
6         0-127     VCF Envelope Attack
7         0-127     VCF Envelope Decay
8         0-127     VCF Envelope Sustain
9         0-127     VCF Envelope Release
10        0-127     VCA Envelope Attack
11        0-127     VCA Envelope Decay
12        0-127     VCA Envelope Sustain
13        0-127     VCA Envelope Release
14        0-127     VCF Envlope Cutoff Amount -64...+63
15        0-127     VCA Envlope Volume Amount -64...+63
16        0-127     LFO Speed
17        0-127     LFO Speed Mod. Amount -64...+63
18        0-4       LFO Shape 0:sin 1:tri 2:saw 3:pls 4:S-H
19        0-15      LFO Speed Modulation Source
20        0-127     Cutoff Modulation Amount -64...+63
21        0-127     Resonance Modulation Amount -64...+63
22        0-127     Volume Modulation Amount -64...+63
23        0-127     Panning Modulation Amount -64...+63
24        0-15      Cutoff Modulation Source
25        0-15      Resonance Modulation Source
26        0-15      Volume Modulation Source
27        0-15      Panning Modulation Source
28        0-127     Cutoff
29        0-127     Resonance
30        0-127     Volume
31        0-127     Panning
32        0-127     Gate Time 0.000 to 1.02 s
33        0-2       Trigger Source 0:Audio 1:MIDI 2:All
34        0-1       Trigger Mode 0:Multi 1:Single

Now we add the parameters to the module element. Each parameter has a key assigned to it (optional) as unique identifier of each parameter in the module. Since each parameter has also it's unique offset in the midi message it makes sense to use the offset also as identifier of the parameter.

...
<module name="miniWORKS 4pole" >
  <parameter key="6" name="VCF Envelope Attack" />
  <parameter key="7" name="VCF Envelope Decay" />
  <parameter key="8" name="VCF Envelope Sustain" />
  <parameter key="9" name="VCF Envelope Release" />
  <parameter key="10" name="VCA Envelope Attack" />
  <parameter key="11" name="VCA Envelope Decay" />
  <parameter key="12" name="VCA Envelope Sustain" />
  <parameter key="13" name="VCA Envelope Release" />
  <parameter key="14" name="VCF Envlope Cutoff Amount" formatter="offset(-64)" />
  <parameter key="15" name="VCA Envlope Volume Amount" formatter="offset(-64)" />
  <parameter key="16" name="LFO Speed" />
  <parameter key="17" name="LFO Speed Mod. Amount" formatter="offset(-64)" />
  <parameter key="18" name="LFO Shape" maxValue="4" formatter="type('tmLFOShape')" />
  <parameter key="19" name="LFO Speed Modulation Source" maxValue="15"
    formatter="type('tmModulationSource')"/>
  <parameter key="20" name="Cutoff Modulation Amount" formatter="offset(-64)" />
  <parameter key="21" name="Resonance Modulation Amount" formatter="offset(-64)" />
  <parameter key="22" name="Volume Modulation Amount" formatter="offset(-64)" />
  <parameter key="23" name="Panning Modulation Amount" formatter="offset(-64)" />
  <parameter key="24" name="Cutoff Modulation Source" maxValue="15"
    formatter="type('tmModulationSource')"/>
  <parameter key="25" name="Resonance Modulation Source" maxValue="15"
    formatter="type('tmModulationSource')"/>
  <parameter key="26" name="Volume Modulation Source" maxValue="15"
    formatter="type('tmModulationSource')" />
  <parameter key="27" name="Panning Modulation Source" maxValue="15"
    formatter="type('tmModulationSource')" />
  <parameter key="28" name="Cutoff" />
  <parameter key="29" name="Resonance" />
  <parameter key="30" name="Volume" />
  <parameter key="31" name="Panning" />
  <parameter key="32" name="Gate Time" format-id="GateTimeFormatter" />
  <parameter key="33" name="Trigger Source" maxValue="2" formatter="type('tmTriggerSource')" />
  <parameter key="34" name="Trigger Mode" maxValue="1" formatter="type('tmTriggerMode')" />
</module>
...

parameter - attribute overview

attribute use description
namerequiredparameter name
idoptionalelement id
keyoptional Identifies the current parameter. No other parameter in the module can have the same key. If not specified the name-value is used as key.
classoptional optional attribute that can be used to associate a parameter with a class of parameters
minValueoptional, default: 0minimum value
defaultValueoptional, default: 0default value
maxValueoptional, default: 127maximum value
format-idsee: attribute formatterspecifies the id of an external formatter.
formatter formatter and format-id are
both optional. Only one of
them can be specified.
Uses one of the built in formatters.

Modules II - Restructuring

Now we have defined the properties of the MiniWorks Filter. But in our case it would make sense to provide more information. One problem is that we have all parameters mixed (VCF, VCA, LFO, ...). We also have some kind of internal routing. Modulation sources and targets could be represented by connectors.

...
<module category="External" name="External">
  <connector type="output" key="7" name="Signal Env" />
  <connector type="output" key="9" name="Velocity" />
  <connector type="output" key="10" name="Keytrack" />
  <connector type="output" key="11" name="Pitch Bend" />
  <connector type="output" key="12" name="Modwheel" />
  <connector type="output" key="13" name="Aftertouch" />
  <connector type="output" key="14" name="Breath Ctr." />
  <connector type="output" key="15" name="Foot Ctr." />
</module>

<module category="LFO" name="LFO">
  <parameter key="16" name="Speed" />
  <parameter key="17" name="Speed Mod. Amount" formatter="offset(-64)" />
  <parameter key="18" name="Shape" maxValue="4" formatter="type('tmLFOShape')" />
  <parameter key="19" name="Speed Modulation Source" maxValue="15"
    formatter="type('tmModulationSource')">
    <attribute type="boolean" name="has-replacement" value="yes" />
  </parameter>

  <connector type="input" key="19" name="Speed Modulation Source">
    <comment>Replacement for 'Speed Modulation Source' parameter</comment>
  </connector>

  <connector type="output" key="1" name="LFO" />
  <connector type="output" key="2" name="LFO * ModW." />
  <connector type="output" key="3" name="LFO * Aftertouch" />
  <connector type="output" key="4" name="LFO * VCA Env" />
</module>

<module category="Envelope" name="VCF Envelope">
  <parameter key="6" name="Attack" />
  <parameter key="7" name="Decay" />
  <parameter key="8" name="Sustain" />
  <parameter key="9" name="Release" />
  <parameter key="14" name="VCF Envlope Cutoff Amount" formatter="offset(-64)" />

  <connector type="output" key="5" name="VCF Env" />
</module>

<module category="Envelope" name="VCA Envelope">
  <parameter key="10" name="Attack" />
  <parameter key="11" name="Decay" />	
  <parameter key="12" name="Sustain" />
  <parameter key="13" name="Release" />
  <parameter key="15" name="VCA Envlope Volume Amount" formatter="offset(-64)" />

  <connector type="output" key="6" name="VCA Env" />
  <connector type="output" key="8" name="Vel * VCA Env" />
</module>

<module category="Filter" name="Filter">
  <parameter key="20" name="Cutoff Modulation Amount" formatter="offset(-64)" />
  <parameter key="21" name="Resonance Modulation Amount" formatter="offset(-64)" />
  <parameter key="24" name="Cutoff Modulation Source" maxValue="15"
    formatter="type('tmModulationSource')">
    <attribute type="boolean" name="has-replacement" value="yes" />
  </parameter>
  <parameter key="25" name="Resonance Modulation Source" maxValue="15"
    formatter="type('tmModulationSource')">
    <attribute type="boolean" name="has-replacement" value="yes" />
  </parameter>
  <parameter key="28" name="Cutoff" />
  <parameter key="29" name="Resonance" />

  <connector type="input" key="24" name="Cutoff Modulation Source">
    <comment>Replacement for parameter</comment>
  </connector>
  <connector type="input" key="25" name="Resonance Modulation Source">
    <comment>Replacement for parameter</comment>
  </connector>
</module>

<module category="Audio" name="Amp" >
  <parameter key="22" name="Volume Modulation Amount" formatter="offset(-64)" />
  <parameter key="26" name="Volume Modulation Source" maxValue="15" formatter="type('tmModulationSource')" >
    <attribute type="boolean" name="has-replacement" value="yes" />
  </parameter>
  <parameter key="30" name="Volume" />

  <parameter key="23" name="Panning Modulation Amount" formatter="offset(-64)" />
  <parameter key="27" name="Panning Modulation Source" maxValue="15" formatter="type('tmModulationSource')" >
    <attribute type="boolean" name="has-replacement" value="yes" />
  </parameter>
  <parameter key="31" name="Panning" />

  <connector type="input" key="26" name="Volume Modulation Source">
    <comment>Replacement for parameter</comment>
  </connector>
  <connector type="input" key="27" name="Panning Modulation Source">
    <comment>Replacement for parameter</comment>
  </connector>
</module>

<module category="Settings" name="Settings" >
  <parameter key="32" name="Gate Time" format-id="GateTimeFormatter" />
  <parameter key="33" name="Trigger Source" maxValue="2" formatter="type('tmTriggerSource')" />
  <parameter key="34" name="Trigger Mode" maxValue="1" formatter="type('tmTriggerMode')" />
</module>

...

Here we have moved parameters which belong together into their own module. Additionally we have defined connectors which should later be used to connect modulation sources with modulation inputs.

Connectors

The connector-element is used to define in- and outputs of a module. Since the miniWorks has no connectors we put the example aside. The following example shows a module with one input and one output connector.

...
<module id="someID" name="someName" >
  ...
  <connector name="audio-in" key="c1" signal="input" />
  <connector name="audio-out" key="c2" signal="output" />
</module>
...

connector - attribute overview

attribute use description
namerequiredconnector name
idoptionalelement id
typerequired: "input" or "output"connector type
signaloptional The signal type of the connector. The signal type has to be defined in the defs section. See Signal types.
keyoptional Identifies the current connector. No other connector in the module can have the same key. If not specified the name-value is used as key.
classoptional optional attribute that can be used to associate a connector with a class of connectors

Attributes

In some cases the default attributes for the module-, parameter- and connector-elements are not enough to store all information. Thus we provide the attribute- element which can be used inside these three elements to specify custom properties. Here are some usage example:

...
<body>
<module ...>
  <attribute name="dsp-load" type="double" value="0.23" />
  <attribute name="another-one" value="this is a string" />

  <connector ...>
    <attribute name="hint" type="boolean" value="yes" />
  </connector>
  <parameter ...>
    <attribute name="a-number" type="integer" value="5" />
  </parameter>
</module>
</body>
... 

attribute - attribute overview

attribute use description
namerequiredattribute name
idoptionalattribute id
type built in types:
boolean - value = "yes" or "no"
float
double
integer
string (used as default if attribute
type is not specified)
custom types:
types defined in the defs-section. The value must be one of the enumeration values of the specified type.
type of the specified value
value required a value according to the specified type

Comments

The format allows to add a comment in module-, parameter- and connector elements. The comment has to be the first child element.

...
<body>
<module ...>
  <comment>comment about the module</comment>
  <connector ...>
    <comment>comment about the connector</comment>
  </connector>
  <parameter ...>
    <comment>comment about the parameter</comment>
  </parameter>
</module>
</body>
... 

Formatter

In most cases the parameter value itself is the internal representation which alone has no meaning to the user. Then it is necessary to substitute the value with something more meaningful. As solution we provide built in formatters and the possibility to identify custom formatters.

Built in formatters

Built in formatters are used inside the formatter-attribute of the parameter-element. Like in this example:

 <parameter name="LFO Speed Mod. Amount" formatter="offset(-64)" /> 

Built in formatters overview

formatter arguments description example
offset offset:integer Adds the specified offset to the parameter value offset(-64)
formatted value:
parameter-value + (-64)
scale factor:integer Multiplies the specified offset with the parameter value scale(2)
formatted value:
parameter-value * 2
scaled factor:double (,base:integer)? Multiplies the specified offset with the parameter value. The base value performs the additional operation:
if (base<0):
result=round(
scaledValue *(10^-base)
) *(10^base)
else if (base>0):
result=round(
scaledValue *(10^base)
) *(10^-base);
else:
result=round(scaledValue);
If the base value is not specified then a value of 0 (zero) is used.
scaled(0.1234, -3)
formatted value:
The result of parameter-value*0.1234
is rounded up to the third digit behind the decimal point.
scale min:integer, max:integer specifies a different range for the parameter scale(2, 7)
scale(min, max)
formatted value:
(((pvalue-pmin)/(pmax-pmin)) *(max-min))+min
str string:string displays a constant string. Can only be used as first and/or last formatter in a sequence of formatters. str('+')
formatted value:
+
type name:string Uses the key-value pair of a custom type defined in the defs-section to substitute a value with a string. type('tmLFOShape')
formatted value:
switch (parameter-value)
{
case type.0:
return "sin";
case type.1:
return "tri";
case type.2:
return "saw";
case type.3:
return "pls";
case type.4:
return "S-H";
default:
return string(
parameter-value);
}

The built in formatters can be combined with each other if they are seperated using the comma character:

 <parameter name="p"
    formatter="str('x'),offset(-64),scale(2),str('y')"
  /> 

Custom formatters

Some values require formatters that are more complex than the built in ones. In that case it is possible to add the format-id attribute to a parameter and link the formatter at runtime with the parameter.

 <parameter name="Gate Time"
    format-id="GateTimeFormatter"
  /> 

Result

You can download the example file below.

The example file has a reference to a XSL stylesheet. If the ModuleDescriptions file is opened in a browser it should render as html. (tested only in Mozilla Firefox 2.0)

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xml" href="./ModuleDescriptions2html.xsl"?>
<ModuleDescriptions version="1.3" ... >
...
</ModuleDescriptions>

Todo

  • annotation support
  • annotations that describe the preferred control of a parameter (knob, slider, button(s), ...)
  • annotations that describe envelopes (param1 = attack.time, param2 = decay.time, ...)
  • defining virtual modules - these are child modules of a real module that create logic groups of parameters and connectors which belong together.

by Christian Schneider