]> git.0d.be Git - nanofun.git/blobdiff - nanofun.js
ramp to values to avoid audio glitches
[nanofun.git] / nanofun.js
index 9244d0b11f54d4fb251016acf81df049ffb58a59..b8dcaf96721bd3ad094f32bd6cd8a26b54cce0f5 100644 (file)
@@ -61,8 +61,8 @@ onMIDIAccessChange: function(e) {
       } else {
         this.outputs[port.name] = undefined;
       }
-      if (port.name == 'nanoPAD2 MIDI 1') { $('#devices .nanopad').removeClass('on'); }
-      if (port.name == 'nanoKONTROL2 MIDI 1') { $('#devices .nanokontrol').removeClass('on'); }
+      if (port.name == 'nanoPAD2 MIDI 1' || port.name == 'nanoPAD2 2 PAD') { $('#devices .nanopad').removeClass('on'); }
+      if (port.name == 'nanoKONTROL2 MIDI 1' || port.name == 'nanoKONTROL2 2 SLIDER/KNOB') { $('#devices .nanokontrol').removeClass('on'); }
     } else {
       if (port.type == "input") {
         if (this.inputs[port.name] === undefined) { this.registerPort(port); }
@@ -70,8 +70,8 @@ onMIDIAccessChange: function(e) {
         if (this.outputs[port.name] === undefined) { this.registerPort(port); }
       }
             console.log('hello:', port.name);
-      if (port.name == 'nanoPAD2 MIDI 1') { $('#devices .nanopad').addClass('on'); }
-      if (port.name == 'nanoKONTROL2 MIDI 1') { $('#devices .nanokontrol').addClass('on'); }
+      if (port.name == 'nanoPAD2 MIDI 1' || port.name == 'nanoPAD2 2 PAD') { $('#devices .nanopad').addClass('on'); }
+      if (port.name == 'nanoKONTROL2 MIDI 1' || port.name == 'nanoKONTROL2 2 SLIDER/KNOB') { $('#devices .nanokontrol').addClass('on'); }
       this.renderPort(port);
     }
 },
@@ -90,17 +90,18 @@ registerPort: function(port) {
     } else {
         this.outputs[port.name] = port;
 
-        if (port.name == 'nanoKONTROL2 MIDI 1') {
+        if (port.name == 'nanoKONTROL2 MIDI 1' || port.name == 'nanoKONTROL2 2 SLIDER/KNOB') {
+          var port_name = port.name;
           /* 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(port_name).raw([240, 126, 127, 6, 1, 247]);
+          device(port_name).raw([240, 66, 64, 0, 1, 19, 0, 31, 18, 0, 247]);
+          device(port_name).raw([240, 126, 127, 6, 1, 247]);
 
-          device("nanoKONTROL2 MIDI 1").raw([ 240, 66, 64, 0, 1, 19, 0, 127, 127,
+          device(port_name).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,
@@ -124,11 +125,11 @@ registerPort: function(port) {
                 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]);
+          device(port_name).raw([240, 126, 127, 6, 1, 247]);
+          device(port_name).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); }
+          function on(note) { device(port_name).cc(note, 127); }
+          function off(note) { device(port_name).cc(note, 0); }
           var leds = Array(43, 44, 42, 41, 45);
           for (var i=0; i<8; i++) {
             leds.push(64+i);
@@ -294,13 +295,14 @@ var nanofun = function() {
   var self = this;
 
   self.initAudio = function() {
-    self.sample_buffers = Array(16);
-    self.samples = Array(16);
-    self.sample_start_times = Array(16);
+    self.sample_buffers = Array(NANOPAD_TOUCHS.length);
+    self.samples = Array(NANOPAD_TOUCHS.length);
+    self.sample_start_times = Array(NANOPAD_TOUCHS.length);
     self.audioCtx = new window.AudioContext();
-    self.touchGainNodes = Array(16);
+    self.touchGainNodes = Array(NANOPAD_TOUCHS.length);
     self.masterGainNode = self.audioCtx.createGain();
-    for (var i=0; i<16; i++) {
+    self.effectsGainNode = self.audioCtx.createGain();
+    for (var i=0; i<NANOPAD_TOUCHS.length; i++) {
       self.touchGainNodes[i] = self.audioCtx.createGain();
       self.touchGainNodes[i].connect(self.masterGainNode);
     }
@@ -315,11 +317,12 @@ var nanofun = function() {
     self.filter = self.audioCtx.createBiquadFilter();
     self.filter.frequency.value = 1000;
 
+    self.effectsGainNode.connect(self.delay);
     self.delay.connect(self.feedback);
     self.feedback.connect(self.filter);
     self.filter.connect(self.delay);
 
-    self.masterGainNode.connect(self.delay);
+    self.filter.connect(self.masterGainNode);
   }
 
   self.initMIDI = function() {
@@ -334,7 +337,7 @@ var nanofun = function() {
   self.initUI = function() {
     var $nanopad = $('#nanopad');
     var $nanotouch = $('.nanotouch');
-    for (var i=0; i<16; i++) {
+    for (var i=0; i<NANOPAD_TOUCHS.length; i++) {
       var $new_touch = $nanotouch.clone();
       $new_touch.attr('data-touch', i);
       $new_touch.appendTo($nanopad);
@@ -372,6 +375,19 @@ var nanofun = function() {
 
     midi.onControlChange = function(port, data, control, value) {
       if (control > 7 && control < 16) return; /* range between sliders and pots */
+      if (control >= 32 && control < 40) { /* "S" buttons */
+          var nanotouch = $('.nanotouch')[control-32];
+          if (value == 127) {
+            var checked = $(nanotouch).find('.loop input').prop('checked');
+            if (checked) {
+              $(nanotouch).find('.loop input').prop('checked', false);
+              device("nanoKONTROL2 MIDI 1").cc(control, 0);
+            } else {
+              $(nanotouch).find('.loop input').prop('checked', true);
+              device("nanoKONTROL2 MIDI 1").cc(control, 127);
+            }
+          }
+      }
       if (control > 23) return; /* after pots */
       if (control < 8) {
         control += 8; /* sliders, control bottom pads (8-15) */
@@ -388,9 +404,20 @@ var nanofun = function() {
       }
     });
 
+    $('.effects input').on('change', function() {
+      var effects = $(this).prop('checked');
+      var i = parseInt($(this).parents('.nanotouch').data('touch'));
+      if (effects) {
+        self.touchGainNodes[i].connect(self.effectsGainNode);
+      } else {
+        self.touchGainNodes[i].disconnect(self.effectsGainNode);
+      }
+    });
+
     $('#master-gain').on('change', function() {
       var fraction = parseInt(this.value) / parseInt(127);
-      self.masterGainNode.gain.value = fraction * fraction;
+      var now = self.audioCtx.currentTime;
+      self.masterGainNode.gain.exponentialRampToValueAtTime(fraction * fraction, now + 0.015);
     });
 
     $('#delay').on('change', function() {
@@ -398,13 +425,15 @@ var nanofun = function() {
       if (value == 0) {
         self.delay.disconnect();
       } else {
-        self.delay.delayTime.value = value;
+        var now = self.audioCtx.currentTime;
+        self.delay.delayTime.exponentialRampToValueAtTime(value, now + 0.015);
         self.delay.connect(self.audioCtx.destination);
       }
     });
 
     $('#feedback').on('change', function() {
-      self.feedback.gain.value = this.value;
+      var now = self.audioCtx.currentTime;
+      self.feedback.gain.exponentialRampToValueAtTime(this.value, now + 0.015);
     });
 
     $('#filter').on('change', function() {
@@ -414,11 +443,12 @@ var nanofun = function() {
     $('.touch-gain').on('change', function() {
       var fraction = parseInt(this.value) / parseInt(127);
       var touchIdx = parseInt($(this).parent().data('touch'));
-      self.touchGainNodes[touchIdx].gain.value = fraction * fraction;
+      var now = self.audioCtx.currentTime;
+      self.touchGainNodes[touchIdx].gain.exponentialRampToValueAtTime(fraction * fraction, now + 0.015);
     });
 
     self.time_interval_id = setInterval(function() {
-      for (var i=0; i<16; i++) {
+      for (var i=0; i<NANOPAD_TOUCHS.length; i++) {
         var sample = self.samples[i];
         if (sample !== undefined) {
           var start_time = self.sample_start_times[i];
@@ -443,7 +473,7 @@ var nanofun = function() {
         var sample = self.audioCtx.createBufferSource();
         var gainNode = self.touchGainNodes[sample_idx];
         self.samples[sample_idx] = sample;
-        sample.loop = false;
+        sample.loop = ($(nanotouch).find('.loop input:checked').length == 1);
         sample.connect(gainNode);
         sample.buffer = sample_buffer;
         sample.onended = function() {