Developing Software for SLIDER

Click the buttons below to go to the demos. In order for them to work, your browser needs to implement the MIDI API. Currently it works in Chrome/Chromium and nightly releases of Firefox. The code can be found in this repository. The rest of this page details the simple example, and points out some further features used by a more complex example.

Simple example

This example has the following directory structure:

simple-example
├── midi.js
├── midi.json
├── index.html.template
├── site_gen.py
└── index.html

midi.js is a file containing a javascript class called midiHandler and other helper functions. midi.json specifies the midi-aware inputs of the page, with the following example content:

[
    {
        "name": "Input 1",
        "shortName": "input1",
        "symbol": "1"
    },
    {
        "name": "Input 2",
        "shortName": "input2",
        "symbol": "2"
    },
    {
        "name": "Input 3",
        "shortName": "input3",
        "symbol": "3"
    }
]

This defines three inputs. For each, “name” is a human readable name, “shortName” is a machine-readable name, and “symbol” is the symbol associated with the input which will be displayed on SLIDER’s 8x8 display.

index.html.template is a template for the main html index file of the page, and has the following in its body:

{MIDI_BOILERPLATE}

<input type="range" min="0" max="127" name="input1" id="input1" value="0">
<input type="range" min="0" max="127" name="input2" id="input2" value="0">
<input type="radio" name="input3" id="input3" value="0">

{MIDI_BOILERPLATE} is a location where a midi settings dialog will be added, this is why the file is called index.html.template – running site_gen.py will insert this boilerplate and output the complete index file to index.html. The MIDI settings dialog can be seen on this page, where the demo runs.

index.html.template also contains the following javascript, which sets up MIDI handling:

// create the midiHandler object
let mHandler = new midiHandler(document.getElementById("midi"), "mHandler")

// populate it with event handlers for each input defined in midi.json
mHandler.midi_event_handlers["input1"] = function(value) {
    document.getElementById("input1").value = value;
}

mHandler.midi_event_handlers["input2"] = function(value) {
    document.getElementById("input2").value = value;
}

mHandler.midi_event_handlers["input3"] = function(value) {
    if (value === 127) { // button down
        var inp = document.getElementById("input3");
        inp.checked = !inp.checked;
    }
}

// start the event loop, which will run forever
mHandler.eventLoop();

The first line simply creates the midiHandler object, passing in the element of the MIDI settings dialog (part of {MIDI_BOILERPLATE}), and its own name, which it requires because it inserts HTML input buttons into the page which call functions of midiHandler. Next, midiHandler.midi_event_handlers is populated with an event handler function for each input which was defined in midi.json.

These event handler functions will be run as quickly as the webpage can handle, each time updating the page as desired by the website designer. In this simple example, the first two inputs, connected by the user to SLIDER’s slider and rotary encoder, will simply move a HTML input slider. The third, connected to SLIDER’s button, will toggle a checkbox.

The last line of the example javascript will start the event loop.

A more complex example

midiHandler has a number of further features which are used in the demos in . Here is the event handler for SLIDER’s switch used in histograms demo in , simplified for explanatory purposes:

mHandler.midi_event_handlers["switch"] = async function(value) {
    if (value != 127) return;

    if (Object.values(mHandler.control_change_dict).includes("r_center")) {
        // if the reaction norm is currently being controlled,
        // start controlling the efficiency norm
        mHandler.assignMIDI("e_center", 0, 0);
        mHandler.assignMIDI("e_extent", 0, 1);
    } else {
        mHandler.assignMIDI("r_center", 0, 0);
        mHandler.assignMIDI("r_extent", 0, 1);
    }
}

This code checks which histogram is currently being controlled, and swaps over to controlling the other one. This is combined with another feature called assignment callbacks. Assignment callbacks are set each time an input is assigned to. Here is one example:

mHandler.assignment_callbacks["r_center"] = function(value) {
    // put SLIDER into Go To mode, and move it to the new position
    // `midi_hist_shapes` contains the current shapes of the histograms
    mHandler.goTo(0, midi_hist_shapes["reaction_norm"].center);
    // `plot_indices` maps the names to indices in the html
    for (const [val, idx] of Object.entries(plot_indices)) {
        if (idx === plot_indices["r_center"]) {
            // set the border of the new plot to red
            document.getElementsByClassName("plot")[idx].style.border
                = "5px solid red";
        } else {
            // set the border of all other plots to white
            document.getElementsByClassName("plot")[idx].style.border
                = "5px solid white";
        }
    }
}

This assignment callback is set up such that when the SLIDER switches to controlling the reaction norm histogram, it will physically move its motorised slider to the new position, and the page will update so that the newly controlled plot is highlighted in red. goTo(channel, position) is a convenience function to abstract away the interface specified here, there is also setPassive(channel), rumble(channel, value), setNotch(channel, position), setBump(channel, position), setEncoder(channel, value), and setSymbol(channel, value), which correspond to the other available MIDI messages detailed at the earlier link.