MIDI Binding Maps for Ardour 3.0 and later versions

Ardour 2.X supported MIDI learning for more or less any control. This was a nice feature that quite a few other DAWs have now provided, but it didn’t allow Ardour to work “out of the box” with sensible defaults for existing commercial MIDI controllers. In Ardour 3 and later versions, we have augmented the MIDI learn feature with the ability to load a MIDI binding map for a given controller, which can set up an arbitrary number of physical controls with anything inside Ardour that can be controlled. At this time, these binding maps need to be created with a text editor, but we currently have presets for

  • Behringer BCF 2000
  • Korg_nanoKONTROL
  • M-Audio Oxygen 8 v2
  • Roland SI-24
  • Behringer DDX3216
  • M-Audio Axiom 25

MIDI binding maps are accessible by double clicking on the "Generic MIDI" line in the Control Surfaces tab of the Ardour preferences dialog. Ardour will retain your chosen map after you choose one.

The information below describes in great detail how to create a new MIDI binding map.

The Basic Concept

Since the beginning of time (well, sometime early in the 2.X series), Ardour has had the concept of identifying each track and bus with a remote control ID. This ID uniquely identifies a track or bus so that when messages arrive from elsewhere (MIDI or OSC), we can determine which track or bus they are intended to control. Ardour has a number of ways of assigning remote control IDs, but they don't really matter very much when creating MIDI binding maps, so we won't discuss that here. You just need to know that there is a "first track" and its remote control ID is 1, and so on.

Getting Started

MIDI bindings are stored in files with the suffix ".map" attached to their name. The minimal content looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<ArdourMIDIBindings version="1.0.0" name="The name of this set of bindings">
So, to start, create a file with that as the initial contents.

Finding out what your MIDI control surface sends

This is the most complex part of the job, but its still not very hard. You need to connect the control surface to an application that will show you the information that the device sends each time you modify a knob, slider, button etc. There are a variety of such applications (notably gmidimon and kmidimon, but you can actually use Ardour for this if you want. Start Ardour in a terminal window, connect MIDI ports up, and in the Preferences window, enable "Trace Input" on the relevant MIDI port. A full trace of the MIDI data received will show up in the terminal window. (Note: in Ardour3, you get a dedicated, custom dialog for this kind of tracing)

Types of Bindings

There are two basic kinds of bindings you can make between a MIDI message and something inside Ardour. The first is a binding to a specific parameter of a track or bus. The second is a binding to a function that will change Ardour's state in some way.

Binding to Track/Bus controls

A track/bus binding has one of two basic structures

  <Binding msg specification  uri="... control address ..."/>
  <Binding msg specification  function="... function name ..."/>

Message specifications

You can create a binding for either 3 types of channel messages, or for a system exclusive ("sysex") message. A channel message specification looks like this:

   <Binding channel="1" ctl="13" ....
This defines a binding for a MIDI Continuous Controller message involving controller 13, arriving on channel 1. There are 16 MIDI channels, numbered 1 to 16. Where the example above says ctl, you can alternatively use note (to create binding for a Note On message) or pgm (to create a binding for a Program Change message).

You can also bind sysex messages:

  <Binding sysex="f0 0 0 e 9 0 5b f7" ....
  <Binding sysex="f0 7f 0 6 7 f7" ....
The string after the sysex= part is the sequence of MIDI bytes, as hexadecimal values, that make up the sysex message.

Finally, you can bind a totally arbitrary MIDI message:

  <Binding msg="f0 0 0 e 9 0 5b f7" ....
  <Binding msg="80 60 40" ....
The string after the msg= part is the sequence of MIDI bytes, as hexadecimal values, that make up the message you want to bind. Using this is slightly less efficient than the other variants shown above, but is useful for some oddly designed control devices.

Control address

A control address defines what the binding will actually control. There are quite a few different things that can be specified here:

Each of the specifications needs an address, which takes various forms too. For track-level controls (solo/gain/mute/recenable), the address is one the following:
a number, eg. "1"
identifies a track or bus by its remote control ID
B, followed by a number
identifies a track or bus by its remote control ID within the current bank (see below for more on banks)
one or more words
identifies a track or bus by its name
For send/insert/plugin controls, the address consists of a track/bus address (as just described) followed by a number identifying the plugin/send (starting from 1). For plugin parameters, there is an additional third component: a number identifying the plugin parameter number (starting from 1).

One additional feature: for solo and mute bindings, you can also add momentary="yes" after the control address. This is useful primarily for NoteOn bindings - when Ardour gets the NoteOn it will solo or mute the targetted track or bus, but then when a NoteOff arrives, it will un-solo or un-mute it.

Bindings to Ardour "functions"

Rather than binding to a specific track/bus control, it may be useful to have a MIDI controller able to alter some part of Ardour's state. A binding definition that does this looks like this:

  <Binding channel="1" note="13" function="transport-roll"/>
In this case, a NoteOn message for note number 13 (on channel 1) will start the transport rolling. The following function names are available:
stop the transport
start the transport "rolling"
move the playhead to the zero position
move the playhead to the start marker
move the playhead to the end marker
turn on loop playback
enable the global record button
disable the global record button
Move track/bus mapping to the next bank (see Banks below)
Move track/bus mapping to the previous bank (see Banks below)

Binding to Ardour "actions"

You can also bind a sysex or arbitrary message to any of the items that occur in Ardour's main menu (and its submenus). The best place to look for the (long) list of how to address each item is in your keybindings file, which will contain lines that look like this:

(gtk_accel_path "<Actions>/Editor/temporal-zoom-in" "equal")
To create a binding between an arbitrary MIDI message (we'll use a note-off on channel 1 of MIDI note 60 (hex) with release velocity 40 (hex)), the binding file would contain:
   <Binding msg="80 60 40" action="Editor/temporal-zoom-in"/>
The general rule, when taken an item from the keybindings file and using it in a MIDI binding is to simply strip the <Action> prefix of the second field in the keybinding definition.

Banks and Banking

Because many modern control surfaces offer per-track/bus controls for far fewer tracks & busses than many users want to control, Ardour offers the relatively common place concept of "banks". Banks to allow you to relatively easily control any number of tracks and/or busses regardless of how many faders/knobs etc. your control surface has. To use banking, the control addresses must be specified using the bank relative format mentioned above ("B1" to identify the first track of a bank of tracks, rather than "1" to identify the first track).

One very important extra piece of information is required to use banking: an extra line near the start of the list of bindings that specifies how many tracks/busses to use per bank. If the device has 8 faders, then 8 would be a sensible value to use for this. The line looks like this:

   <DeviceInfo bank-size="8"/>
In addition, you probably want to ensure that you bind something on the control surface to the next-bank and prev-bank functions, otherwise you and other users will have to use the mouse and the GUI to change banks, which rather defeats the purpose of the bindings.

A Complete (though muddled) Example

<?xml version="1.0" encoding="UTF-8"?>
<ArdourMIDIBindings version="1.0.0" name="pc1600x transport controls">
  <DeviceInfo bank-size="16"/>
  <Binding channel="1" ctl="1"   uri="/route/gain B1"/>
  <Binding channel="1" ctl="2"   uri="/route/gain B2"/>
  <Binding channel="1" ctl="3"   uri="/route/send/gain B1 1"/>
  <Binding channel="1" ctl="4"   uri="/route/plugin/parameter B1 1 1"/>
  <Binding channel="1" ctl="6"   uri="/bus/gain master"/>

<Binding channel=“1” note=“1” uri="/route/solo B1"/>
<Binding channel=“1” note=“2” uri="/route/solo B2" momentary=“yes”/>

<Binding channel=“1” note=“15” uri="/route/mute B1" momentary=“yes”/>
<Binding channel=“1” note=“16” uri="/route/mute B2" momentary=“yes”/>

<Binding sysex=“f0 0 0 e 9 0 5b f7” function=“transport-start”/>
<Binding sysex=“f0 7f 0 6 7 f7” function=“rec-disable”/>
<Binding sysex=“f0 7f 0 6 6 f7” function=“rec-enable”/>
<Binding sysex=“f0 0 0 e 9 0 53 0 0 f7” function=“loop-toggle”/>

<Binding channel=“1” note=“13” function=“transport-roll”/>
<Binding channel=“1” note=“14” function=“transport-stop”/>
<Binding channel=“1” note=“12” function=“transport-start”/>
<Binding channel=“1” note=“11” function=“transport-zero”/>
<Binding channel=“1” note=“10” function=“transport-end”/>

Please note that channel, controller and note numbers are specified as decimal numbers in the ranges 1-16, 0-127 and 0-127 respectively (the channel range may change at some point)

@paul I’ve not had much time to look into this, but just popping it in ~/.config/ardour3/midi_maps didn’t work.

I think gmidimon site is down.


Will use Ardour to figure out for M-Audio Oxygen8 V2…

Wow, this is amazing. This is exactly the kind of feature / information I’m looking for. I’ll make my own bindings file today :slight_smile:

One small issue though: would it be possible to expose the functionality of “move play-head to edit point”? I find that button extremely useful when recording punches, and would love to be able to bind that to midi button. I guess on a more general note, are there more functions than ones listed above. In an ideal world, a user would be able to bind any non-toggle midi button to any functionality currently provided by a key on the keyboard. Just an idea, I don’t want to seem ungrateful. I’m thrilled that you guys are working on midi surface stuff at all.

Thanks for this! I’ll be posting a my bindings soon.

– Alex the Feature Creep

I think that’s a good idea. I’m going to try my hand with my BCF2000 later today.

Ok, I hope I got it right.

M-Audio Oxygen8 V2

This is a really simple and basic device it only has 8 physical dials (Virtually 128 with the channels). But mostly all I’m providing here is just the transport button functions (Stop, Play, Loop, etc).


Let me know if you need something else.

Thanks for making such a nice MIDI DBase!

@joegiampoli: that looks good, but lets get the dials in as well, controlling volume on banked tracks, perhaps.

hi there,

i would like to help too with an m-audio axiom 25.
i have a stupid question, but is the binding working ?
i am trying to get the transport buttons working, with no success.
how can i “activate” the bindings ?
i am using A3 to find out what the MIDI control surface sends.

just to let people know,
they can also use qtractor to get the midi commands, if like me they don’t have kmidimin or gmidimin.
the commands are displayed in the messages windows

@ Dazgard:

In my case with M-Audio Oxygen8V2, I had to program it with the values provided in the manual and save it to a preset in the Oxygen. Then I connect it via qjackctl to 0:control in ardour , then in ardour go to options>control surfaces>generic midi

Then you should be able to monitor the bindings in the terminal window the way Paul explained.

@ joegiampaoli
i can monitor the commands in ardour 3 terminal window the way Paul explained. i can see sysex messages when using the transport buttons on my axiom25 and i can see controller messages. here’s an output from the MIDI trace control window :

22:45:00.283565 Sysex (6) = [f0 7f 7f 06 03 f7]
22:45:04.352509 Controller chn 1 62 21
22:45:04.352551 Controller chn 1 63 01
22:45:04.352562 Controller chn 1 06 0c
22:45:04.395126 Controller chn 1 06 0a
22:45:04.437817 Controller chn 1 62 21
22:45:04.437854 Controller chn 1 63 01
22:45:04.437865 Controller chn 1 06 00

The sysex is from a transport button
The controller ones from a dial button

bye the way, my axiom is configured to send MMC messages when using the transport buttons and those are working just fine. maybe i should change the axiom preset to something else. just the way i did to have MMC working

@ Dazgard:

My wrong, I made a mistake about saying to create a patch in your Axiom and then go to Ardour to see the MIDI messages.

If you do that you’ll get a “System Exclusive” message in terminal from Ardour, which means that the specific control is already taken or programmed in better words. What we want to do is start with a completely blank patch in our device, (that means no personal preferences), that way, when a person hooks their MIDI device directly to Ardour they don’t have to re-program it the way we have it, if we provide the info from a blank patch it will act as a plug n’ play with default settings making better compatibility between the device and the settings in Ardour Database.

So don’t program or assign anything. Just hook up with a clean or reseted patch from the device and monitor what the controls say in the terminal.

So the info you provided (if it is without assigning anything) should do. Just build your xml file with those values, because from them I can’t tell what each one belongs to what control.

@ Paul:

Ok, I will add the 8 dials also but just a few questions:

1.- Do I create for all 128 “virtual” dials (16 ch), Or do you only need the first channel? Because looking at output in console, the number for controller doesn’t change…

2.- When I turn a dial I get a message like this:

control input: Channel 1 Controller 91 Value 31

with value being the variable from 0 to 127, how do I treat that? or not needed?

3.- So if a dial will control a track level, with the code in question 2, how would I assing to control a track level?


@joegiampoli: not sure what to do about programming all of them … do at least 2 channels’ worth. To bind to route gain:

<Binding channel=“1” ctl=“91” uri="/route/gain B1"/>

What about pan control for each (possibly banked) channel?

I’ve created a map file for one of the BCF2000 factory presets (number 2), but using a (possibly incorrect) assumed routing for pan control.

I don’t have Ardour-3 installed, though, so I can’t test to see if what I’ve done works. It should, though (except for perhaps the pan config I’ve done).

Any chance someone could comment on whether panning is controllable via the mapping described on this page? If so, I can adjust what I’ve done to suit, and contribute it as a BCF2000 mapping.

Ok, updated my xml.


Did it for 4 channels, total of 32 dials, ctl numbers do not change, only channel value. The only way the ctl value changes is when changing a preset on the device. So in other words should work if the user is using the factory default 01 preset of the device.

I forgot to place the rec-disable function. So I assigned it to the same one for rec-enable button, not sure if that can be done, if not then take out the rec-disable line for that same one (ctl 25).

About panning like therockgarden says, can panning be assigned? If so how? and if possible I’ll be glad to update my file to do that in such a way that the 4 bottom controls will adjust levels ant the top 4 will pan, and then add an extra 4 channels. But then, I guess it would only be possible for mono tracks only…otherwise, how to control 2 pans with one control?

I start working on M-Audio Keystation Pro-88, thanks for the midi implementation Paul.
Waiting for Ardour 3

panning isn’t possible yet. still trying to decide on the right design for this. we probably need a “balance”-style control to supplement the per-channel panners, so that you can do “single knob” control.

Ok, then I’ve used what I believe are standard XML comments to comment out the panning lines in my map file. I’ve also attempted to comment the file clearly so that people reading it should be able to easily tell what the intended controls are.

The result is at

I hope this will be useful … Please let me know if any of what I’ve done will cause difficulty. I’m certainly willing to make adjustments. As noted earlier, I don’t have Ardour-3 installed on my system, so this is untested.

@therockgarden: great stuff. there was one minor error - the syntax is uri= not uri-, but i fixed that. Your map and joegiampoli’s one for the oxygen 8 are now in svn, and hopefully there will be more to come. I will add some for the presets on my pc1600x, which actually correspond to other devices.