Web MIDI API | MIDIメッセージの送信

(function() {

    var onDOMContentLoaded = function() {

        if (!navigator.requestMIDIAccess) {
            window.alert('Cannot use Web MIDI API.');
            return;
        }

        var output   = null;
        var velocity = document.getElementById('range-velocity').valueAsNumber;

        var successCallback = function(midiAccess) {
            var inputs  = [];
            var outputs = [];

            if (typeof midiAccess === 'function') {
                // Legacy Chrome
                inputs  = midiAccess.inputs();
                outputs = midiAccess.outputs();
            } else {
                // Chrome 39 and later
                var inputIterator  = midiAccess.inputs.values();
                var outputIterator = midiAccess.outputs.values();

                for (var i = inputIterator.next(); !i.done; i = inputIterator.next()) {
                    inputs.push(i.value);
                }

                for (var o = outputIterator.next(); !o.done; o = outputIterator.next()) {
                    outputs.push(o.value);
                }
            }

            outputs.forEach(function(element, index) {
                var option = document.createElement('option');

                option.appendChild(document.createTextNode(element.name));
                option.setAttribute('value', index);

                document.getElementById('select-midi-output-device').appendChild(option);
            });

            document.getElementById('select-midi-output-device').onchange = function() {
                output = outputs[this.value];
            };

            var NOTE_NUMBER_OFFSET = 21;

            var PIANO_88S = [
                            'A-4', 'A-4h', 'B-4',
                            'C-3', 'C-3h', 'D-3', 'D-3h', 'E-3', 'F-3', 'F-3h', 'G-3', 'G-3h', 'A-3', 'A-3h', 'B-3',
                            'C-2', 'C-2h', 'D-2', 'D-2h', 'E-2', 'F-2', 'F-2h', 'G-2', 'G-2h', 'A-2', 'A-2h', 'B-2',
                            'C-1', 'C-1h', 'D-1', 'D-1h', 'E-1', 'F-1', 'F-1h', 'G-1', 'G-1h', 'A-1', 'A-1h', 'B-1',
                            'C',   'Ch',   'D',   'Dh',   'E',   'F',   'Fh',   'G',   'Gh',   'A',   'Ah',   'B',
                            'C1',  'C1h',  'D1',  'D1h',  'E1',  'F1',  'F1h',  'G1',  'G1h',  'A1',  'A1h',  'B1',
                            'C2',  'C2h',  'D2',  'D2h',  'E2',  'F2',  'F2h',  'G2',  'G2h',  'A2',  'A2h',  'B2',
                            'C3',  'C3h',  'D3',  'D3h',  'E3',  'F3',  'F3h',  'G3',  'G3h',  'A3',  'A3h',  'B3',
                            'C4'
                            ];

            var pianoKeys = Array.prototype.slice.call(document.querySelectorAll('#piano ul li'), 0);

            pianoKeys.forEach(function(element, index, array) {
                // note on
                element['on' + EventWrapper.START] = function() {
                    if (output === null) {
                        return;
                    }

                    var noteNumber = PIANO_88S.indexOf(this.id) + NOTE_NUMBER_OFFSET;

                    output.send([0x90, noteNumber, velocity], (window.performance.now() + 0));

                    this.classList.remove('key-off');
                    this.classList.add('key-on');
                };

                // note off
                element['on' + EventWrapper.END] = function() {
                    if (output === null) {
                        return;
                    }

                    var noteNumber = PIANO_88S.indexOf(this.id) + NOTE_NUMBER_OFFSET;

                    output.send([0x80, noteNumber, velocity], (window.performance.now() + 0));

                    this.classList.remove('key-on');
                    this.classList.add('key-off');
                };
            });
        };

        var errorCallback = function(error) {
            console.dir(error);
        };

        navigator.requestMIDIAccess({sysex : true}).then(successCallback, errorCallback);

        // Control Velocity
        document.getElementById('range-velocity').addEventListener('input', function() {
            velocity = this.valueAsNumber;
            document.getElementById('output-velocity').textContent = this.value;
        });
    };

    if ((document.readyState === 'interactive') || (document.readyState === 'complete')) {
        onDOMContentLoaded();
    } else {
        document.addEventListener('DOMContentLoaded', onDOMContentLoaded, true);
    }

})();
function EventWrapper(){
}

(function(){
    var click = '';
    var start = '';
    var move  = '';
    var end   = '';

    // Touch Panel ?
    if (/iPhone|iPad|iPod|Android/.test(navigator.userAgent)) {
        click = 'click';
        start = 'touchstart';
        move  = 'touchmove';
        end   = 'touchend';
    } else {
        click = 'click';
        start = 'mousedown';
        move  = 'mousemove';
        end   = 'mouseup';
    }

    EventWrapper.CLICK = click;
    EventWrapper.START = start;
    EventWrapper.MOVE  = move;
    EventWrapper.END   = end;
})();