mTroll Hybrid MIDI controller

(or How I Spent Summer 2007)
last update: 2008.05.08

contents

introduction
software overview
download
development notes
part list
process milestones
hardware pictures
software usage / notes
xml data file notes
todo
contact

introduction

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.

software overview

screenshot
(click to enlarge)

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)

download

application software
mTroll (build 2008.05.08) a Win32 application (no installer; just unzip and run - configuration is done via the included XML files). Usage notes here.

To use with a monome board, you must install the FTDI D2XXX drivers (not necessary for standalone operation).
source code
My Win32 C++ implementation of a monome serial protocol handler (updated 2007.11.03). Licensed under the zlib/libpng License.

My altered version of the monome firmware (updated 2007.11.04) (modified ADC port handling, see Expression Pedal Calibration). The original work is licensed under GPL, so my alterations are as well.

The application source code is not currently available.

development notes

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

./Win32
Win32 monome interface implementation
./tinyxml
TinyXML source

part list

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.

monome logic kit
Ground Control Pro chassis
P26A-1D-BL Carling foot switches (clickless, momentary switches)
Jameco T1 3/4 Blue LEDs (these are almost too bright)
Radio Shack panel mount LED holders
FrontX panel mount USB cable
Radio Shack 1/4" jacks (for expression pedals)

process milestones

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: Many thanks to Josh and Harmony at Voodoo Lab!
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.09.08
Started on this page
2007.09.16
Added first meta-patch: Reset patches in active bank
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.11.17
Added support for instant access switches
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)
2008.05.08
Added support for new meta-patches: Backward, Forward and Recall

hardware pictures

After drilling 8 additional holes on top and 4 on the back:


LEDs mounted:


LEDs illuminated:


LED and switch wiring:


LED and switch wiring 2:


Top panel:


Top panel 2:


software usage / notes

User Interface
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
Expression Pedal Calibration
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).

	<adc inputNumber="1" enable="0" minimumAdcVal="10" maximumAdcVal="1015" />
	<adc inputNumber="2" enable="0" minimumAdcVal="10" maximumAdcVal="1015" />
	<adc inputNumber="3" enable="0" minimumAdcVal="10" maximumAdcVal="1015" />
	<adc inputNumber="4" enable="0" minimumAdcVal="10" maximumAdcVal="1015" />
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.

	<globalExpr inputNumber="1" assignmentNumber="1" channel="7" controller="2" 
		min="0" max="127" invert="0" enable="1" />
	<globalExpr inputNumber="1" assignmentNumber="2" channel="8" controller="2" 
		min="0" max="127" invert="1" enable="1" />
	<globalExpr inputNumber="2" assignmentNumber="1" channel="4" controller="2" 
		min="0" max="127" invert="0" enable="1" />
	<globalExpr inputNumber="2" assignmentNumber="2" channel="5" controller="2" 
		min="0" max="127" invert="1" enable="1" />
	<globalExpr inputNumber="3" assignmentNumber="1" channel="11" controller="110" 
		min="0" max="127" invert="0" enable="1" />
	<globalExpr inputNumber="3" assignmentNumber="2" channel="12" controller="110" 
		min="0" max="127" invert="1" enable="1" />
	<globalExpr inputNumber="4" assignmentNumber="1" channel="11" controller="111" 
		min="0" max="127" invert="0" enable="1" />
	<globalExpr inputNumber="4" assignmentNumber="2" channel="12" controller="111" 
		min="0" max="127" invert="1" enable="1" />
And finally, the section closing tag.
</expression>
Instant Access Support
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.
Meta-Patches
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.

LoadBank example:
<engineMetaPatch name="Load bank 1 (meta)" number="51" action="LoadBank" bankNumber="1" />

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.)

Recall example:
<engineMetaPatch name="Bank Recall" number="54" action="BankHistoryRecall" />
Exclusive Switch Groups
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.

xml data file notes

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.

*.ui.xml (example)
ui.xml files define the GUI of the application.

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 (example)
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).

todo

contact

Bugs, comments, questions? Contact me.