-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Behringer Extension Library
This document explains the Behringer Extension Library which is provided under controllers/behringer-extension-scripts.js
.
Main target audience is the group of mapping creators.
The library is an extension to Components JS. It contains
- A framework to declare a controller mapping in JavaScript
- Additional Components
Initial purpose of this library was to simplify the mapping of two Behringer controllers. Despite its name, it doesn't require anything Behringer-specific. It is a generic library that may be used for any controller mapping.
Note
This library was not developed by the core developer team. It might be that its underlying concepts do not match the long-term direction of controller mapping development. Therefore, it might be required to introduce incompatible changes in future if mainline development requires so. The name Behringer was kept as part of the name to prevent general use by accident. Keep this in mind before using it.
The framework allows to define the input and output bindings of a controller mapping in JavaScript by declaring a GenericMidiController
object.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class GenericMidiController {
+ GenericMidiController(options: object)
+ init(controllerId: string, debug: boolean)
+ shutdown()
+ input(channel, control, value, status, group)
}
A ComponentContainer
representing a generic, configurable MIDI controller. It consists of custom decks, effect units and additional component containers, where custom means:
- Decks may be composed of arbitrary components.
- Effect units have support for sending output values for encoders.
- All components are managed by a
LayerManager
so that they may be put on either the Default or the Shift layer by configuration.
The mapping definitions are defined by a Configuration
object. It is read on controller initialization by calling configurationProvider()
on the constructor options object.
The input()
function dispatches incoming messages to the component with matching MIDI address and layer. It may be referenced from XML, although this is usually not required: since Mixxx 2.6, both input and output bindings are managed via JavaScript. This means that input will work even if the XML element <controls>
is omitted.
This example illustrates how to do declare a minmal GenericMidiController
:
- File
controllers/Demo.midi.xml
:<?xml version="1.0" encoding="UTF-8"?> <MixxxControllerPreset schemaVersion="1" mixxxVersion="2.6+"> <info> <name>Demo</name> <description>Demo Controller</description> </info> <controller id="Demo"> <scriptfiles> <file filename="midi-components-0.0.js"/> <file filename="Behringer-Extension-scripts.js" /> <file filename="Demo-scripts.js" functionprefix="Demo" /> </scriptfiles> </controller> </MixxxControllerPreset>
- File
controllers/Demo-scripts.js
:var Demo = new behringer.extension.GenericMidiController({ configurationProvider: function() { return { init: function() { print("Initializing DemoController"); }, decks: [ /* deck definitions */ ], effectUnits: [ /* effect unit definitions */ ] }; } });
The GenericMidiController
was designed for general purpose controllers (such as AKAI APC, AKAI Fire, AKAI midimix, Behringer BCR2000, Behringer X-Touch, DJ Techtools Midi Fighter, Faderfox, Icon Platform M+, Korg nanoKONTROL, Midiplus SmartPAD, Nektar Aura, Novation Launchpad & Studiologic SL Mixface). For real world examples, see Behringer DDM4000 and BCR2000 mappings.
An object that contains all mapping definitions for a controller.
classDiagram
class Configuration {
decks: DeckDefinition[]
effectUnits: EffectUnitDefinition[]
containers: ComponentContainerDefinition[]
throttleDelay: int
init()
shutdown()
}
All members are optional.
-
decks
: An array of deck definitions. -
effectUnits
: An array of effect unit definitions. -
containers
: An array of component container definitions. -
throttleDelay
: A positive number (in ms) that is used to slow down the initialization of the controller; this option is useful if the hardware is limited to process a certain number of MIDI messages per time. -
init()
: A function that is called when the mapping is loaded, e.g. when Mixxx is started. -
shutdown()
: A function that is called when the mapping is unloaded, e.g. when Mixxx is shutting down.
Example:
{
throttleDelay: 40,
init: function() {
components.Button.prototype.shiftChannel = true;
components.Button.prototype.shiftOffset = 0x20;
},
decks: [{
deckNumbers: [1],
components: [
{type: components.Button, options: {midi: [0x90, 0x01], inKey: "reverseroll"}},
{type: components.Button, shift: true, options: {midi: [0x90, 0x01], inKey: "reverse", type: c.Button.prototype.types.toggle}},
// ... more components
]
}]
}
Mapping definition for a single Component, e.g. a button or an encoder.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class ComponentDefinition {
type()
options: object
shift: boolean
}
-
type
: Component type (constructor function, required). -
options
: Additional options for the component (object, required). -
shift
: Active only when a Shift button is pressed? (boolean, optional).
Examples:
{type: components.Button, options: {midi: [0x90, 0x01], inKey: "reverseroll"}}
{type: components.Button, shift: true, options: {midi: [0x90, 0x01], inKey: "reverse", type: c.Button.prototype.types.toggle}}
Mapping definition for a deck (channel) containing components for e.g. play or loop as well as an equalizer and a quick effect unit.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class DeckDefinition {
deckNumbers: number[]
components: ComponentDefinition[]
equalizerUnit: SimpleEffectUnitDefinition
quickEffectUnit: SimpleEffectUnitDefinition
}
class SimpleEffectUnitDefinition {
midi: object
feedback: boolean
output: object
}
Example:
{
deckNumbers: [1],
components: [
{type: components.Button, options: {midi: [0x90, 0x01], inKey: "reverseroll"}},
{type: components.Button, shift: true, options: {midi: [0x90, 0x01], inKey: "reverse", type: toggle}},
// ... more components
],
equalizerUnit: {
midi: {
parameterKnobs: {1: [0xB0, 0x06], 2: [0xB0, 0x05], 3: [0xB0, 0x04]},
parameterButtons: {1: [0x90, 0x02], 2: [0x90, 0x01], 3: [0x90, 0x00]},
},
output: {
parameterButtons: {1: [0xB0, 0x3D], 2: [0xB0, 0x3B], 3: [0xB0, 0x39]},
},
},
quickEffectUnit: {
midi: {enabled: [0x90, 0x02], super1: [0xB0, 0x06]},
output: { enabled: [0xB0, 0x3D]},
}
}
-
deckNumbers
: As defined bycomponents.Deck
-
components
(optional): An array of component definitions for the deck -
equalizerUnit
(optional): Equalizer unit definition -
quickEffectUnit
(optional): Quick effect unit definition
This definition is used for both equalizer and quick effect unit of a deck.
-
midi
: An object of component definitions for the unit. Each definition is a key-value pair for a component ofEqualizerUnit
orcomponents.QuickEffectUnit
wherekey
is the name of the component andvalue
is the MIDI address. -
feedback
: Enable controller feedback (boolean, optional). When set totrue
, values of the components in this unit are sent to the hardware controller on changes. The address of the MIDI message is taken from themidi
property of the affected component. -
output
: Additional output definitions (optional). The structure of this object is the same as the structure ofmidi
. Every value change of a component contained inoutput
causes a MIDI message to be sent to the hardware controller, using the configured address instead of the component'smidi
property. This option is independent of thefeedback
option.
Mapping definition for an effect unit.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class EffectUnitDefinition {
unitNumbers: number[]
midi: object
feedback: boolean
output: object
sendShiftedFor()
}
-
unitNumbers
: As defined bycomponents.EffectUnit
. -
midi
,feedback
,output
: same as forSimpleEffectUnitDefinition
. -
sendShiftedFor
: Type of components that send shifted MIDI messages (optional). When set, all components of this type within this effect unit are configured to send shifted MIDI messages (sendShifted: true
).
Example:
{
unitNumbers: [1, 2],
midi: {
dryWetKnob: [0xB0, 0x03],
effectFocusButton: [0x90, 0x34],
enableButtons: {1: [0x90, 0x31], 2: [0x90, 0x32], 3: [0x90, 0x33]},
knobs: {1: [0xB0, 0x00], 2: [0xB0, 0x01], 3: [0xB0, 0x02]},
},
sendShiftedFor: components.Button,
}
Mapping definition for a custom component container. May be used to group components that are not part of a deck or effect unit, e.g. for a sampler, crossfader or microphone.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class ComponentContainerDefinition {
components: ComponentDefinition[]
options: object
type()
defaultDefinition: object
init()
}
-
components
: An object of component definitions for the component container. -
options
: (object, optional) Constructor argument for the container -
defaultDefinition
: (object, optional) Default definition for components in the container -
type
: (constructor function, optional); default:components.ComponentContainer
-
init
: (function, optional) A function that is called after component creation and before first use
The defaultDefinition
may be used to avoid repeated declarations of settings for multiple components. It is deep-merged with each component definition before the component is created. If both default and component definition are given, the component definition is preferred.
Example:
{ // Crossfader
defaultDefinition: {options: {group: "[Mixer Profile]"}},
components: [
{type: behringer.extension.CrossfaderCurvePot, options: {midi: [0xB0, 0x14]}},
{type: components.Button, options: {midi: [0x90, 0x29], key: "xFaderReverse", type: toggle, sendShifted: true}},
]
}
This section gives some insight into implementation details of the framework. Despite the rest of this document, it is targeted at developers, not mapping creators.
---
title: Framework Overview
---
classDiagram
direction LR
namespace components {
class Component {
# options: object
+ shift() # optional
+ unshift() # optional
+ input(channel, control, value, status, group)
}
class ComponentContainer {
# options: object
+ shift()
+ unshift()
+ applyLayer(newLayer, reconnectComponents)
}
}
namespace behringer.extension {
class GenericMidiController {
- config: Configuration
- componentContainers: ComponentContainer[]
+ init(controllerId: string, debug: boolean)
+ shutdown()
+ input(channel, control, value, status, group)
}
class LayerManager {
- activeLayer: ComponentContainer
- inputConnections: MidiInputHandlerController[]
- findComponent(status: int, control: int) Component
+ init()
+ destroy(disconnect: boolean)
+ register(component: Component, shift: boolean)
+ unregister(component: Component, shift: boolean)
+ shift()
+ unshift()
+ input(channel, control, value, status, group)
}
class ComponentRegistry {
+ register(component: Component, containerName: string)
+ unregister(component: Component, containerName: string)
}
}
GenericMidiController --|> ComponentContainer
GenericMidiController o-- LayerManager
LayerManager --|> Component
LayerManager o-- ComponentRegistry
LayerManager o-- ComponentContainer: defaultLayer
LayerManager o-- ComponentContainer: shiftLayer
- The root object
GenericMidiController
creates and stores all components and component containers given by the definitions of theConfiguration
object. All components are registered in aLayerManager
on initialization. - The
LayerManager
associates each component to either the Default or the Shift layer.- A
ComponentRegistry
is used internally to manage layers. - Only one layer is active at a time. The
LayerManager
is derived fromComponent
and thus knowsshift()
andunshift()
functions. These are used to switch the active layer. - When a component is registered, connections for both input and output are created; input is bound to
LayerManager.input()
which dispatches MIDI messages to the component matching MIDI address and layer. - If an input-only component is configured for
feedback
, aPublisher
is created providing output for that component.
- A
- The
ComponentRegistry
simplifies association of a component to a named container.
A Component
that helps working with layered components.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class LayerManager {
- activeLayer: ComponentContainer
- inputConnections: MidiInputHandlerController[]
- findComponent(status: int, control: int) Component
+ init()
+ destroy(disconnect: boolean)
+ register(component: Component, shift: boolean)
+ unregister(component: Component, shift: boolean)
+ shift()
+ unshift()
+ input(channel, control, value, status, group)
}
The wiki article MIDI Scripting describes two approaches to implement a shift layer: either working with a condition within a component or switching components in a container. The LayerManager is a generic component that implements the second approach.
Internally, it uses a component registry for the two layers Default and Shift. JS Components may be added to either layer. The shift()
and unshift()
functions toggle between the these layers, whereas the LayerManager
is the only component that knows about shifting. When toggled, all affected components on the corresponding layer are registered / unregistered. Components that are not shiftable stay untouched. Additionally, the LayerManager
offers input()
, a facade to be called from the outside that delegates to the component on the currently active layer.
An object
(no Component
) that manages Components
in named ComponentContainers
. Within a container, components are identified by their MIDI address.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class ComponentRegistry {
containers: ComponentContainer
createContainer(name: string)
getContainer(name: string)
disconnectContainer(container: ComponentContainer)
destroyContainer(name: string, disconnect: boolean)
destroy(disconnect: boolean)
register(component: Component, containerName: string)
unregister(component: Component, containerName: string)
}
A component that sends the values of a source component to a MIDI controller even if the source component uses its outKey
property for other purposes.
Useful if an input-only component in Mixxx (e.g. a Pot
) is bound to an output-aware physical control (e.g. an encoder with LEDs).
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class Publisher {
- source: Component
- sync()
+ bind()
}
-
options.source
Source component whose values are sent to the controller
Most components send output properly out of the box so that no Publisher
is required. It was designed to add functionality to some special components, e.g. effect unit controls, and offers a bind()
function that allows for re-binding to the source component when its internal state changes.
All components are derived from type Component
unless specified otherwise.
A button that toggles a beatloop ending at the current play position, so the beat jump occurs immediately on button press and not after the first loop.
A button that blinks when on
.
-
options.blinkDuration
: Blink duration in ms; optional, default: 500
A pot for the crossfader curve.
-
options.mode
: Crossfader mode; optional, default:0
. (0
: additive,1
: constant)
A button with configurable Mixxx control values for on
and off
.
-
options.onValue
: Value foron
; optional, default:1
-
options.offValue
: Value foroff
; optional, default: opposite ofonValue
An encoder for directions. Turning the encoder to the right means "forwards" and returns 1
; turning it to the left means "backwards" and returns -1
.
-
options.relative
: Enable soft-takeover
This component supports an optional relative mode as an alternative to dealing with soft takeover. To use it, set the relative
property to true
in the options object for the constructor. In this mode, moving the Pot will adjust the Mixxx Control relative to its current value. Holding shift and moving the encoder will not affect the Mixxx Control. This allows the user to continue adjusting the Mixxx Control after the encoder has reached the end of its physical range.
An encoder for enumeration values.
-
options.values
: an array containing the enumeration values -
options.softTakeover
: (optional) Enable soft-takeover; default:true
A button to cycle through the values of an enumeration.
The enumeration values may be defined either explicitly by an array or implicitly by a maxValue
so that the values are [0..maxValue]
.
-
options.values
An array of enumeration values -
options.maxValue
A positive integer defining the maximum enumeration value
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class LongPressButton {
onShortPress(value)
onLongPress(value)
onRelease(value)
}
A button that supports different actions on short and long press.
An EnumEncoder for a loop control that uses beat sizes as enumeration.
Example use: Encoder for beatloop_size
and beatjump_size
An encoder that moves the current loop. Turning the encoder to the right will move the loop forwards; turning it to the left will move it backwards. The amount of movement may be given by either size
or sizeControl
, sizeControl
being preferred.
-
options.size
(optional) Size given in number of beats; default: 0.5 -
options.sizeControl
(optional) Name of a control that containssize
A component that uses the parameter instead of the value as output.
An encoder for a value range of [-bound..0..+bound].
-
options.bound
: A positive integer defining the range bound
Example use: encoder to change key (pitch
)
An pot for a value range of [-bound..0..+bound].
-
options.bound
: A positive integer defining the range bound
A button that triggers shift()
and unshift()
on a target
component.
-
options.target
: Target component
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class Timer {
+ Timer(options: TimerOptions)
+ start(action: function)
+ reset()
+ setState(active: boolean)
}
class TimerOptions {
timeout: number
oneShot: boolean
action()
owner: object
}
An object
(no Component
) that simplifies using a timer safely. See Script Timers for details.
-
timeout
: Duration between start and action (in ms) -
oneShot
: Iftrue
, the action is run once; otherwise, it is run periodically until the timer is reset. -
action
Function that is executed whenever the timer expires -
owner
Owner object of theaction
function (assigned tothis
)
A component that is triggered on every input, regardless of the value.
Example use: Button to reset key (pitch_set_zero
).
The DDM4000 mapping contains a few more Components
internally; they are specific to the device but might be interesting anyway:
Blinker
OnTrackLoadButton
KeyButton
EffectAssignmentToggleButton
EffectAssignmentLongPressButton
EchoOutButton
-
CrossfaderUnit
(withCrossfader
,CrossfaderToggleButton
) CrossfaderReverseTapButton
CrossfaderAssignLED
-
SamplerBank
(withPlayButton
,PlayIndicatorLED
,ReverseMode
,LoopMode
,ModeButton
)
Mixxx is a free and open-source DJ software.
Manual
Hardware Compatibility
Reporting Bugs
Getting Involved
Contribution Guidelines
Coding Guidelines
Using Git
Developer Guide
Creating Skins
Contributing Mappings
Mixxx Controls
MIDI Scripting
Components JS
HID Scripting