2 37: 0, 39: 1, 41: 2, 43: 3, 45: 4, 47: 5, 49: 6, 51: 7,
3 36: 8, 38: 9, 40: 10, 42: 11, 44: 12, 46: 13, 48: 14, 50: 15
8 onMIDISuccess: function(midiAccess) {
9 console.log('MIDI Access Object', midiAccess);
13 midiAccess.onstatechange = function(e) {
14 self.onMIDIAccessChange(e);
16 this.midiAccess = midiAccess;
23 initPorts: function() {
26 var inputs = this.midiAccess.inputs;
27 if (inputs.size > 0) {
31 self.registerPort(port);
35 $("#midiinputs").append("<p>No connected inputs</p>");
38 var outputs = this.midiAccess.outputs;
39 if (outputs.size > 0) {
42 self.registerPort(port);
43 self.renderPort(port);
47 $("#midioutputs").append("<p>No connected outputs</p>");
51 onMIDIAccessChange: function(e) {
55 var portContainer = $("#midi" + port.type + "s");
56 if (portContainer.html().startsWith("<p>No connected")) {
57 portContainer.empty();
60 if (port.type == "input") {
61 if (this.inputs[port.name] === undefined) {
62 this.registerPort(port);
65 if (this.outputs[port.name] === undefined) {
66 this.registerPort(port);
70 this.renderPort(port);
73 renderPort: function(port) {
74 if (port.state == "connected") {
75 $("#midi" + port.type + "s").append(port.name);
79 registerPort: function(port) {
81 if (port.type == "input") {
82 this.inputs[port.name] = port;
83 port.onmidimessage = function(m) { self.onMIDIMessage(m); };
85 this.outputs[port.name] = port;
88 port.onstatechange = function(e) { self.onPortStateChange(e); };
91 onMIDIFailure: function(e) {
92 alert("No access to MIDI devices or your browser doesn't support WebMIDI API. Please use WebMIDIAPIShim " + e);
95 onPortStateChange: function(event) {
99 onMIDIMessage: function(message) {
100 var port = message.target;
101 var data = message.data;
102 if (data[0] == 144) { /* touch on */
103 var sample_idx = NANOPAD_TOUCHS[data[1]];
104 this.onTouchOn(port, data, sample_idx);
108 onTouchOn: function(port, data, sample_idx) {}
112 // status bytes on channel 1
123 var device = function(outputName) {
124 this.current = midi.outputs[outputName];
127 // makes device visible inside of nested function defs
130 this._send = function(status, data) {
131 var messageArr = [status + (self.channel - 1)].concat(data);
132 console.log("sending " + messageArr + " to " + self.current.name);
133 self.current.send(messageArr);
137 this.ch = function(channel) {
138 self.channel = channel;
142 this.cc = function(b1, b2) {
143 return self._send(messages.cc, [b1, b2]);
146 this.on = function(b1, b2) {
147 return self._send(messages.on, [b1, b2]);
150 this.off = function(b1, b2) {
151 return self._send(messages.off, [b1, b2]);
154 this.pp = function(b1, b2) {
155 return self._send(messages.pp, [b1, b2]);
158 this.cp = function(b1) {
159 return self._send(messages.cp, [b1]);
162 this.pb = function(b1) {
171 this.pc = function(b1) {
172 return self._send(messages.pc, [b1]);
175 this.panic = function() {
176 return self.cc(123, 0)
179 this.rpn = function(b1, b2) {
180 return self.cc(101, b1 >> 7)
188 this.nrpn = function(b1, b2) {
189 return self.cc(99, b1 >> 7)
197 this.raw = function(data) {
198 console.log("sending raw data: " + data);
199 self.current.send(data);
203 this.toString = function() {
204 var s = "no connected devices";
205 if (typeof this.current != 'undefined') {
214 var nanofun = function() {
217 self.initAudio = function() {
218 self.sample_buffers = Array(16);
219 self.samples = Array(16);
220 self.audioCtx = new window.AudioContext();
221 self.gainNode = self.audioCtx.createGain();
222 self.gainNode.connect(self.audioCtx.destination);
225 self.initMIDI = function() {
226 if (navigator.requestMIDIAccess) {
227 navigator.requestMIDIAccess({sysex: true}).then(
228 function(midiAccess) { midi.onMIDISuccess(midiAccess); },
229 function(e) { midi.onMIDIFailure(e); }
234 self.initUI = function() {
235 var $nanopad = $('#nanopad');
237 $('.nanotouch input').on('change', function(ev) {
238 var nanotouch = $(this).parent();
239 var sample_idx = $nanopad.children().index(nanotouch);
240 var reader = new FileReader();
241 reader.onload = function(e) {
242 self.audioCtx.decodeAudioData(this.result, function(buffer) {
243 sample_buffers[sample_idx] = buffer;
244 $(nanotouch).find('span.duration').text(parseInt(buffer.duration) + 's');
245 $(nanotouch).removeClass('error').addClass('loaded');
247 $(nanotouch).find('span').text('');
248 $(nanotouch).removeClass('loaded').addClass('error');
251 reader.readAsArrayBuffer(this.files[0]);
252 $(nanotouch).find('span.name').text(this.files[0].name);
255 midi.onTouchOn = function(port, data, sample_idx) {
256 var sample_buffer = self.sample_buffers[sample_idx];
257 var nanotouch = $('.nanotouch')[sample_idx];
258 if (typeof(sample_buffer) != 'undefined') {
259 if (typeof(samples[sample_idx]) != 'undefined' && samples[sample_idx].context.state == 'running') {
260 self.samples[sample_idx].stop(0);
261 self.samples[sample_idx] = undefined;
263 var sample = self.audioCtx.createBufferSource();
264 self.samples[sample_idx] = sample;
266 sample.connect(gainNode);
267 sample.buffer = sample_buffer;
268 sample.onended = function() {
269 console.log('ended');
270 $(nanotouch).removeClass('playing');
271 self.samples[sample_idx] = undefined;
273 $(nanotouch).addClass('playing');
286 $(function() { nanofun(); });