var NANOPAD_TOUCHS = Array(37, 39, 41, 43, 45, 47, 49, 51, 36, 38, 40, 42, 44, 46, 48, 50); /* on French/Belgian keyboards, emulate pad touches with keypresses */ var KEYBOARD_CODES = Array('a', 'z', 'e', 'r', 't', 'y', 'u', 'i', 'q', 's', 'd', 'f', 'g', 'h', 'j', 'k'); var midi = { onMIDISuccess: function(midiAccess) { console.log('MIDI Access Object', midiAccess); //console.log(this); var self = this; midiAccess.onstatechange = function(e) { self.onMIDIAccessChange(e); } this.midiAccess = midiAccess; this.inputs = {}; this.outputs = {}; this.initPorts(); }, initPorts: function() { var self = this; var inputs = this.midiAccess.inputs; if (inputs.size > 0) { inputs.forEach( function(port, key) { //console.log(port); self.registerPort(port); } ); } else { $("#midiinputs").append("
No connected inputs
"); } var outputs = this.midiAccess.outputs; if (outputs.size > 0) { outputs.forEach( function(port, key) { self.registerPort(port); self.renderPort(port); } ); } else { $("#midioutputs").append("No connected outputs
"); } }, onMIDIAccessChange: function(e) { console.log('on midi access change', e); //console.log(this); var port = e.port; var portContainer = $("#midi" + port.type + "s"); if (portContainer.html().startsWith("No connected")) {
portContainer.empty();
}
if (port.state == "disconnected") {
if (port.type == "input") {
this.inputs[port.name] = undefined;
} else {
this.outputs[port.name] = undefined;
}
} else {
if (port.type == "input") {
if (this.inputs[port.name] === undefined) { this.registerPort(port); }
} else {
if (this.outputs[port.name] === undefined) { this.registerPort(port); }
}
this.renderPort(port);
}
},
renderPort: function(port) {
if (port.state == "connected") {
$("#midi" + port.type + "s").append(port.name);
}
},
registerPort: function(port) {
var self = this;
if (port.type == "input") {
this.inputs[port.name] = port;
port.onmidimessage = function(m) { self.onMIDIMessage(m); };
} else {
this.outputs[port.name] = port;
if (port.name == 'nanoKONTROL2 MIDI 1') {
/* turn "external leds" mode on.
*
* The sysex dump has been recorded from the official Korg kontrol
* editor by the Overtone project, original code at:
* https://github.com/overtone/overtone/blob/master/src/overtone/device/midi/nanoKONTROL2.clj */
device("nanoKONTROL2 MIDI 1").raw([240, 126, 127, 6, 1, 247]);
device("nanoKONTROL2 MIDI 1").raw([240, 66, 64, 0, 1, 19, 0, 31, 18, 0, 247]);
device("nanoKONTROL2 MIDI 1").raw([240, 126, 127, 6, 1, 247]);
device("nanoKONTROL2 MIDI 1").raw([ 240, 66, 64, 0, 1, 19, 0, 127, 127,
2, 3, 5, 64, 0, 0, 0, 1, 16, 1, 0, 0, 0, 0, 127, 0, 1, 0, 16,
0, 0, 127, 0, 1, 0, 32, 0, 127, 0, 0, 1, 0, 48, 0, 127, 0, 0,
1, 0, 64, 0, 127, 0, 16, 0, 1, 0, 1, 0, 127, 0, 1, 0, 0, 17, 0,
127, 0, 1, 0, 0, 33, 0, 127, 0, 1, 0, 49, 0, 0, 127, 0, 1, 0,
65, 0, 0, 127, 0, 16, 1, 0, 2, 0, 0, 127, 0, 1, 0, 18, 0, 127,
0, 0, 1, 0, 34, 0, 127, 0, 0, 1, 0, 50, 0, 127, 0, 1, 0, 0, 66,
0, 127, 0, 16, 1, 0, 0, 3, 0, 127, 0, 1, 0, 0, 19, 0, 127, 0,
1, 0, 35, 0, 0, 127, 0, 1, 0, 51, 0, 0, 127, 0, 1, 0, 67, 0,
127, 0, 0, 16, 1, 0, 4, 0, 127, 0, 0, 1, 0, 20, 0, 127, 0, 0,
1, 0, 36, 0, 127, 0, 1, 0, 0, 52, 0, 127, 0, 1, 0, 0, 68, 0,
127, 0, 16, 1, 0, 0, 5, 0, 127, 0, 1, 0, 21, 0, 0, 127, 0, 1,
0, 37, 0, 0, 127, 0, 1, 0, 53, 0, 127, 0, 0, 1, 0, 69, 0, 127,
0, 0, 16, 1, 0, 6, 0, 127, 0, 0, 1, 0, 22, 0, 127, 0, 1, 0, 0,
38, 0, 127, 0, 1, 0, 0, 54, 0, 127, 0, 1, 0, 70, 0, 0, 127, 0,
16, 1, 0, 7, 0, 0, 127, 0, 1, 0, 23, 0, 0, 127, 0, 1, 0, 39, 0,
127, 0, 0, 1, 0, 55, 0, 127, 0, 0, 1, 0, 71, 0, 127, 0, 16, 0,
1, 0, 58, 0, 127, 0, 1, 0, 0, 59, 0, 127, 0, 1, 0, 0, 46, 0,
127, 0, 1, 0, 60, 0, 0, 127, 0, 1, 0, 61, 0, 0, 127, 0, 1, 0,
62, 0, 127, 0, 0, 1, 0, 43, 0, 127, 0, 0, 1, 0, 44, 0, 127, 0,
1, 0, 0, 42, 0, 127, 0, 1, 0, 0, 41, 0, 127, 0, 1, 0, 45, 0, 0,
127, 0, 127, 127, 127, 127, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 247]);
device("nanoKONTROL2 MIDI 1").raw([240, 126, 127, 6, 1, 247]);
device("nanoKONTROL2 MIDI 1").raw([240, 66, 64, 0, 1, 19, 0, 31, 17, 0, 247]);
function on(note) { device("nanoKONTROL2 MIDI 1").cc(note, 127); }
function off(note) { device("nanoKONTROL2 MIDI 1").cc(note, 0); }
var leds = Array(43, 44, 42, 41, 45);
for (var i=0; i<8; i++) {
leds.push(64+i);
leds.push(48+i);
leds.push(32+i);
i += 1;
leds.push(32+i);
leds.push(48+i);
leds.push(64+i);
}
console.log(leds);
on(leds[0]);
on(leds[1]);
on(leds[2]);
var led_idx = 2;
var interval_id = setInterval(function() {
if (led_idx < leds.length) {
on(leds[led_idx+1]);
}
off(leds[led_idx-2]);
if (led_idx-1 == leds.length) {
clearInterval(interval_id);
}
led_idx += 1;
}, 50);
}
}
port.onstatechange = function(e) { self.onPortStateChange(e); };
},
onMIDIFailure: function(e) {
alert("No access to MIDI devices or your browser doesn't support WebMIDI API. Please use WebMIDIAPIShim " + e);
},
onPortStateChange: function(event) {
console.log(event);
},
onMIDIMessage: function(message) {
var port = message.target;
var data = message.data;
console.log(message);
if (data[0] == 144) { /* touch on */
var sample_idx = NANOPAD_TOUCHS.indexOf(data[1]);
if (sample_idx != -1) {
this.onTouchOn(port, data, sample_idx);
}
}
if (data[0] == 176) { /* control change */
this.onControlChange(port, data, data[1], data[2]);
}
},
onTouchOn: function(port, data, sample_idx) {},
onControlChange: function(port, data, number, value) {}
};
// status bytes on channel 1
var messages = {
off: 128,
on: 144,
pp: 160,
cc: 176,
pc: 192,
cp: 208,
pb: 224
}
var device = function(outputName) {
this.current = midi.outputs[outputName];
this.channel = 1;
// makes device visible inside of nested function defs
var self = this;
this._send = function(status, data) {
var messageArr = [status + (self.channel - 1)].concat(data);
console.log("sending " + messageArr + " to " + self.current.name);
self.current.send(messageArr);
return self;
}
this.ch = function(channel) {
self.channel = channel;
return self;
}
this.cc = function(b1, b2) {
return self._send(messages.cc, [b1, b2]);
}
this.on = function(b1, b2) {
return self._send(messages.on, [b1, b2]);
}
this.off = function(b1, b2) {
return self._send(messages.off, [b1, b2]);
}
this.pp = function(b1, b2) {
return self._send(messages.pp, [b1, b2]);
}
this.cp = function(b1) {
return self._send(messages.cp, [b1]);
}
this.pb = function(b1) {
return self._send(
messages.pb, [
b1 & 127,
b1 >> 7
]
);
}
this.pc = function(b1) {
return self._send(messages.pc, [b1]);
}
this.panic = function() {
return self.cc(123, 0)
}
this.rpn = function(b1, b2) {
return self.cc(101, b1 >> 7)
.cc(100, b1 & 127)
.cc(6, b2 >> 7)
.cc(38, b2 & 127)
.cc(101, 127)
.cc(100, 127);
}
this.nrpn = function(b1, b2) {
return self.cc(99, b1 >> 7)
.cc(98, b1 & 127)
.cc(6, b2 >> 7)
.cc(38, b2 & 127)
.cc(101, 127)
.cc(100, 127);
}
this.raw = function(data) {
console.log("sending raw data: " + data);
self.current.send(data);
return self;
}
this.toString = function() {
var s = "no connected devices";
if (typeof this.current != 'undefined') {
s = "";
}
return s;
}
return this;
};
var nanofun = function() {
var self = this;
self.initAudio = function() {
self.sample_buffers = Array(16);
self.samples = Array(16);
self.audioCtx = new window.AudioContext();
self.touchGainNodes = Array(16);
self.masterGainNode = self.audioCtx.createGain();
for (var i=0; i<16; i++) {
self.touchGainNodes[i] = self.audioCtx.createGain();
self.touchGainNodes[i].connect(self.masterGainNode);
}
self.masterGainNode.connect(self.audioCtx.destination);
}
self.initMIDI = function() {
if (navigator.requestMIDIAccess) {
navigator.requestMIDIAccess({sysex: true}).then(
function(midiAccess) { midi.onMIDISuccess(midiAccess); },
function(e) { midi.onMIDIFailure(e); }
);
}
}
self.initUI = function() {
var $nanopad = $('#nanopad');
var $nanotouch = $('.nanotouch');
for (var i=1; i<16; i++) {
var $new_touch = $nanotouch.clone();
$new_touch.attr('data-touch', i);
$new_touch.appendTo($nanopad);
}
$('.nanotouch input[type=file]').on('change', function(ev) {
var sample_idx = parseInt($(this).parent().data('touch'));
for (var i=0; i