More and more musicians are using laptops in their rigs. Wouldn't it be great to
harness the power of those laptops to offer greater control capabilities than are
usually available in MIDI foot controllers?
Well, yeah, if you're already using a laptop - but you wouldn't want to lose the
flexibility that foot control offers either.
Enter a hybrid hardware/software MIDI foot controller. A hybrid controller separates
the physical interface from the control logic so that the logic can run on a computer.
Inspired by exhibits at Maker Faire,
I've written some control logic and have an application available for download. And
I've assembled a physical interface that is based on the
monome logic kit.
Neither the hardware nor the software requires a MIDI channel.
The hardware is powered by the computer via USB, supports 64 phsyical switches, 64
LEDs, and 4 expression pedals.
Patches
Unlimited patch count
Patches can send multiple MIDI messages of any length
Patches support Normal, Toggle, Momentary or Sequence modes
Normal mode sends one of set MIDI messages when switch pressed, and a different set when another Normal mode patch is selected
Toggle mode sends one set of MIDI messages when switch pressed, and a different set when pressed again
Momentary mode sends one set of MIDI messages when switch pressed, and a different set when released
Sequence mode increments through a list of messages to send when a switch is pressed repeatedly (a toggle with more than 2 sets/states) (Dr. Z )
Patch state is retained across bank loads Meta-patch for resetting all patches loaded in a bank Meta-patch for loading another bank
Banks
Unlimited bank count
Banks are simply mappings of patches to switches (physical or virtual)
Banks can address more switches than are physically present on input hardware
A single patch can be used in multiple banks or on multiple switches in the same bank
Multiple patches can be assigned to a single switch in any given bank (the first patch assigned to a switch in a bank owns the switch label and indicator)
Banks can be configured with load and unload patch states (optionally activate or deactivate particular patches at bank load or unload)
Support for default mappings (for instant access switch creation)
Support for defining exclusive switch groups per bank (for radio button functionality)
Expression Pedals
Support for four expression pedals
Each pedal can be configured globally and/or locally per patch
Supports minimum and maximum transmission values, value inversion and manual pedal calibration
Each pedal can be configured for one or two controllers (per pedal, up to 2 globals and 2 per patch locals)
Only one patch at a time has control of the pedals (in addition to the global control which can be disabled per patch)
Support for manual calibration
Configuration
Patch and bank settings are stored in plain text XML file (example)
UI is configured via an independent plain text XML file (example)
Same data file can be used with different UI files for display on different resolutions or form factors
Other
Can be used without input hardware (using computer keyboard, and on-screen buttons and indicators)
Support for multiple MIDI out devices (one MIDI out assignment per patch)
Supports independent labels for each switch/button (Dr. Z )
User-definable LED brightness
LED state can be inverted (lit when off, unlit when on)
The core of the app is written in cross-platform C++ with liberal STL use. The GUI and
hardware device support (midi out and monome) is currently implemented for Win32.
The GUI and hardware are accessed from the core through core defined interfaces,
so a Mac or Linux developer will be able to "fill in the blanks" using whatever
native OS APIs are available without having to modify the core and without having to
learn an arbitrary cross-platform GUI toolkit/framework.
The application uses TinyXML for parsing
of the XML data files (licensed under the
zlib/libpng License).
The monome uses a simple
serial protocol over USB
(by way of an FTDI serial to USB module). The application communicates with the
monome using the FTDI D2XXX API.
Source directory hierarchy:
./Engine
Cross-platform interfaces, engine control logic, data file loaders, and patch and bank implementations
./mControlUI
Win32 application and interface implementations
./Monome40h
FTDI and monome interfaces, and monome serial protocol helpers
These are the main parts I used in my foot controller. Since you build your own
hardware, it is totally customizable. LED color, switch configuration,
switches with audible positive feedback, silent switches, table top operation, etc.
Since this is built around the monome logic kit, you don't need to code the firmware or
flash anything.
2007.05.19
Exhibits at Maker Faire provide inspiration and get the gears turning
2007.05.26
Completed first pass at engine control logic
2007.05.30
Completed initial UI and got it all to compile
2007.06.16
Initial pass at midi out device, UI and file loader implementation
2007.07.06
Read about and ordered the monome logic kit after having looked at other USB
bridge boards (like Phidgets,
EleXol and
CREATE USB Interface)
2007.07.08
Initial pass at FTDI and monome software support
2007.07.12
Met Josh Fiden at Voodoo Lab in Santa Rosa and scored a
Ground Control Pro chassis - this was huge! Not being too handy with metal, without this the
alternatives would have been:
2007.08.05
Finally got around to assembling the logic board
Fixed some bugs in the monome software framework, app now works with the board
My neighbor, Brian Barron, drilled extra holes in the Ground Control Pro case
2007.08.11
Wired up the LEDs
2007.08.19
Wired up the switches
Initial pass at expression pedal support
2007.09.01
Worked out expression pedal calibration and load of pedal settings from file
2007.10.21
Automatically loads last opened files at startup
Fixed flicker in the GUI
Made LED intensity user definable
Cleaned up GUI (removed unused default elements)
Added Raw ADC Value engine mode
2007.11.03
After biting the bullet and ordering a firmware programmer, it arrived this week;
tweaked the adc filtering/smoothing. Happy now.
2007.12.23
Added support for exclusive switch groups (for radio button functionality)
Added support for a new meta-patch: Load Bank
Added support for inverse LED display (specified in the hardware node of the .ui.xml file)
2008.02.11
Automatically disable screensaver and system sleep/shutdown timers while
application is running if monome is found at startup (previous settings restored at exit)
Main Display Window displays text that is dependent upon the active mode
and the function of the switch pressed Trace Window displays diagnostic messages Switch Labels display patch names or button descriptions Switch Indicators show whether a patch is active or inactive (which
is patch mode dependent) Switch Buttons are used in addition to or in place of dedicated
hardware switches. Underlined letters in button text show what keyboard letters can be used to
activate the button. Pressing a letter causes a button down in addition to a button release. For
best results with momentary patches, place focus on the button and use the spacebar (button release
does not happen until the spacebar is released).
Engine Control Modes
The app supports up to a total of 64 switches split between preset switches and up to three operating
switches. The function of each operating switch is dependent upon the control mode that is active.
Bank mode (default)
Preset switches activate patches
Next and Previous display the next or previous bank and changes to Bank Navigation mode
Mode switch label displays the active bank number and name
Mode switch changes to Mode Select mode
Main Display Window displays patch name and number when a preset switch is pressed
Bank Navigation mode
Any preset switch will load the currently displayed bank (commits the bank) and changes mode back to the default
Next and Previous increment and decrement the displayed bank
Mode switch escapes back to the default mode (with the bank that was previously active)
Main Display Window displays bank information when the displayed bank changes
Bank Description mode
Preset switches display patch state information in the main display window
Next and Previous increment and decrement the displayed bank
Mode switch escapes back to the default mode (with the bank that was previously active)
Bank Direct mode
First 10 preset switches allow a bank number to be typed in
Next switch functions as commit/enter: loads the bank number entered and switches the mode back to the default
Previous switch functions as backspace
Mode switch escapes back to the default mode (with the bank that was previously active)
Main Display Window displays bank number (and name if applicable) of number typed
Mode Select mode
Preset switches select one of the previously described modes
Next and Previous have no function
Mode switch escapes back to the default mode (with the bank that was previously active)
Main Display Window displays mode name
Raw ADC Value mode
Preset switches select ADC port to track
Next and Previous have no function
Mode switch escapes back to the default mode (with the bank that was previously active)
Main Display Window displays ADC value of selected port
Calibrating the expression pedal ports is basically a manual task. Use the Raw ADC Value engine mode
to display the range of values that your pedal produces. Do full sweeps on the selected
pedal while watching the main display. Note the minimum and maximum values that are displayed (make
sure to release foot from pedal since you want to see the minimum and maximum values that occur
without requiring your foot to always be on the pedal).
In your .config.xml file, record the noted values in the minimumAdcVal and
maximumAdcVal attributes of the adc record for the port (specified by the inputNumber attribute)
on which you did the full sweeps. You may want to give youself a cushion and record a slightly
higher number than the minimum for the minimum; and a slightly lower number than the maximum
for the maximum.
Disable any ADC port that is not in use by setting the enable attribute to "0".
The selected ADC port must be enabled for the Raw ADC Value mode to be of value. If a port is
not enabled, the main display window will not display values after that port has been selected.
For best results, flash the board with
this modified monome firmware
(AVR-JTAG programmer required). [The original adc smoothing/filtering is based on the averaging of 16 reads. Even with the 16
buckets I still had to do some filtering in the app. The modification reduces the number of buckets
in the firmware to 4. The firmware does smoothing (based on averaging the contents of the buckets)
while the app does jitter filtering (based on hard compares to the last 3 values received).
It's a night and day difference in response when doing fast full-off to full-on pedal swings
(wah-wah style). Above a certain pedal speed, the 16 buckets meant that the extremes disappeared -
the extremes were averaged away. No more.]
Here is an annotated example of the complete expression block.
The opening tag. Here, port refers to the MIDI out port that the expression pedals
will transmit to.
<expression port="1">
Next are the adc nodes where the four monome adcs are enabled and where the
pedals attached to each adc port are calibrated (each port is calibrated independently).
Next are the global MIDI assignments for each pedal (inputNumber 1 - 4).
Each pedal can have up to two global assignments (assignmentNumber 1 - 2).
Use two volume assignments where one is inverted to do a cross-fade.
Instant access switches are a common feature on sophisticated foot controllers. While it is
possible to emulate them by hardcoding patch assignments in every bank that you define, that can
be tedious, prone to error and a pain to maintain. But there is an an alternative.
Bank number 0 is a special bank that can be used to define default patch maps. When you add
a mapping to a switch in bank 0, that mapping will be used in every other bank that does not
already have a mapping for the switch. The instant access switches are only defined
in one bank but are available in all banks. These default mappings are easily overridden in
a bank by making a mapping for the same switch in the bank. This way you can have default
behavior in all or only some of your banks.
A meta-patch is a patch that affects the control engine state itself rather than affecting
an external MIDI device. Like regular patches, meta-patches are defined in the
patches list using the engineMetaPatch tag.
The ResetBankPatches meta-patch resets all of the patches in a bank that are
currently considered active (without sending any MIDI data out the MIDI port) and clears
the switch LEDs (turns off any lit LEDs). This is useful for clearing patches like toggle
or sequence mode patches, but would not have any effect on momentary mode patches.
ResetBankPatches example: <engineMetaPatch name="Reset bank (meta)" number="50" action="ResetBankPatches" />
The LoadBank meta-patch is a patch that can be assigned to any switch in a bank
that causes the controller to load another bank. This could be used to create a sequence
of banks and provides for immediate one switch access to any bank from any bank.
The Backward and Forward meta-patches are patches that can be assigned
to any switch in a bank that operate like the history function of a web browser. They
are unlike Next and Previous in that they remember the order of banks that you have
actually loaded. The Backward and Forward meta-patches operate
instantly (no confirmation switch press is required).
Backward and Forward examples: <engineMetaPatch name="Last Bank Visited" number="52" action="BankHistoryBackward" /> <engineMetaPatch name="Forward" number="53" action="BankHistoryForward" />
The Recall meta-patch is a patch that can be assigned to any switch in a bank
that causes the controller to switch back and forth between the currently loaded bank and the
previously loaded one; it is similar to 'channel recall' on TV remote controls. It can be
invoked repeatedly to switch back and forth between two banks (map it to the same switch
in multiple banks). (This is basically an automatic application of Backward and
Forward.)
An ExclusiveSwitchGroup is a list of switches (defined per bank) in which only one
switch is allowed to be active (analogous to radio buttons since only one radio station
can be selected at a time). When you press a new switch in an exclusive group, the
previously selected patch in the group is released. The group does not need to be contiguous;
a group could be composed of a column, a row, a block, every other switch, etc. It makes
most sense with groups of toggle patches.
ExclusiveSwitchGroup example (defined in a bank along with its
PatchMaps): <ExclusiveSwitchGroup>2 7 12</ExclusiveSwitchGroup>
Misc
The application is not an editor (it only reads the XML files, does not generate them). Version 2.5.1 of
Raymond, the PMC10 editor, can be used to
generate config.xml files using the Export to XML command.
The current version loads a pair of files, testdata.config.xml (the patches and banks) and testdata.ui.xml
(the GUI definitions) automatically at startup (it looks in the startup directory).
Press Ctrl+O to load a different set (the last opened set of files is remembered across restarts).
Press F5 to reload the both the UI and config files.
I think these files are pretty straightforward. If anything can't be deduced from the example files
and aren't explained below,
let me know and I'll add an
explanation.
Item position can be specified via top and left coordinates. You can also
specify relative positions which makes it easier to position a lot of elements.
incrementalVpos adjusts the top coordinate relative to the previous sibling
element. incrementalHpos adjusts the left coordinate relative to the
previous element. Do not specify both relative and absolute positions for the same
dimension (incrementalVpos and top, or incrementalHpos
and left); mixing relative and absolute is fine for different dimensions
(incrementalVpos and left, or incrementalHpos
and top).
The SwitchMappings section is required if using the software with monome-based hardware.
It is used to associate on screen switch displays with monome switches (the software uses ordinal
based switch identifiers that can be mapped to any monome switch identified by a row and
column coordinate).
The switchAssemblyConfig section is required for the switchAssemblies
section to load; it defines global properties for the switchAssemblies.
A switchAssembly may contain a switch, switchTextDisplay
and switchLED; none of these items are required.
config.xml files define the banks, patches and expression pedal settings.
The SystemConfig|switches section should contain a switch
entry for each of the three operating switches. The id attributes in these entries
correlate to the number attributes of switchAssemblies in the ui.xml file.
The SystemConfig|midiDevices section contains a midiDevice
entry for each MIDI output device (MIDI out port on the computer) referenced by the rest of the
file. If your computer only has one MIDI out, then there will only be one midiDevice
entry. The port attribute is required and can be any number; it is used as an alias
in the patch definitions. The outIdx attribute is required and corresponds to the
zero-based system assigned device index (0 is typically the default MIDI out mapper on Windows).
The activityIndicatorId is optional and specifies the number of a
switchAssemby which contains an LED that should be used to indicate MIDI out
activity (the corresponding switch assemby need only contain a switchLED). If you move
the config file to another computer that has different MIDI out capabilities, you only
need to edit the midiDevice entry to map the port to the new device index (you don't need
to edit every single patch; they are isolated from the actual index by way of the port alias).
The SystemConfig|expression section contains up to 4 adc
entries and up to 8 globalExpr entries.
The adc entries map to each of the monome adc outputs. The adcs can be explicitly
enabled or disabled. If an adc port is not explicitly disabled, it will be automatically enabled
if there are any globalExpr or localExpr settings for that port defined
in the file. The monome adcs have a range of 0 - 1023. Calibrate your pedals by specifying
different minimums and maximums.
The globalExpr entries define the MIDI data that is sent in response to adc
changes on a global basis. There can be a maximum of two settings (assignmentNumber
1 or 2) per adc port. Select the MIDI port used for the global expression definitions by using
the port attribute on the expression node.
Patches can have up to 8 localExpr entries (up to 2 assignments for each of the 4 ports)
and are configured identically to the globalExpr entries in the
SystemConfig|expression section.
By default, the global globalExpr settings are active for all patches. The
globalExpr settings can be defeated on a patch by patch basis through the use
of a truncated globalExpr entry within the patch definition (and
setting enable="0"). The globalExpr settings are defeated by adc port;
it is not possible to defeat only one of two assignments made to an adc port. A
patch can have a maximum of 4 globalExpr defeating entries
(one for each adc port).