]> git.0d.be Git - nanofun.git/blobdiff - nanofun.js
display keyboard shortcut
[nanofun.git] / nanofun.js
index bde7c13012ee3ce1e320097e7aec5bdda744b3dc..5deb9c4eba92a4a24a1677b5314e6adeb8ba4ec4 100644 (file)
@@ -295,6 +295,8 @@ var nanofun = function() {
   var self = this;
 
   self.initAudio = function() {
+    self.focused_pad = undefined;
+    self.cycle_being_pressed = false;
     self.sample_buffers = Array(NANOPAD_TOUCHS.length);
     self.samples = Array(NANOPAD_TOUCHS.length);
     self.sample_start_times = Array(NANOPAD_TOUCHS.length);
@@ -340,6 +342,7 @@ var nanofun = function() {
     for (var i=0; i<NANOPAD_TOUCHS.length; i++) {
       var $new_touch = $nanotouch.clone();
       $new_touch.attr('data-touch', i);
+      $new_touch.find('.key').text(KEYBOARD_CODES[i]);
       $new_touch.appendTo($nanopad);
     }
     $nanotouch.remove(); /* remove template */
@@ -370,12 +373,18 @@ var nanofun = function() {
     });
 
     midi.onTouchOn = function(port, data, sample_idx) {
-      self.startSample(sample_idx);
+      self.toggleSample(sample_idx);
     }
 
     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 */
+      if (control == 46) {
+        /* cycle -> alternate mode, make faders control effects when pressed
+         * (moving faders when cycle is pressed) */
+        self.cycle_being_pressed = (value != 0);
+        return;
+      }
+      if (control >= 32 && control < 40) { /* "S" buttons -> loop */
           var nanotouch = $('.nanotouch')[control-32];
           if (value == 127) {
             var checked = $(nanotouch).find('.loop input').prop('checked');
@@ -386,9 +395,63 @@ var nanofun = function() {
               $(nanotouch).find('.loop input').prop('checked', true);
               device("nanoKONTROL2 MIDI 1").cc(control, 127);
             }
+            $(nanotouch).find('.loop input').trigger('change');
           }
       }
+      if (control >= 48 && control < 55) { /* "M" buttons -> effects */
+          var nanotouch = $('.nanotouch')[control-48];
+          if (value == 127) {
+            var checked = $(nanotouch).find('.effects input').prop('checked');
+            if (checked) {
+              $(nanotouch).find('.effects input').prop('checked', false);
+              device("nanoKONTROL2 MIDI 1").cc(control, 0);
+            } else {
+              $(nanotouch).find('.effects input').prop('checked', true);
+              device("nanoKONTROL2 MIDI 1").cc(control, 127);
+            }
+            $(nanotouch).find('.effects input').trigger('change');
+          }
+
+      }
+      if ((control == 58 || control == 59) && value == 127) {
+        /* track < and > buttons: move focus between pads for special functions */
+        if (self.focused_pad === undefined) {
+          self.focused_pad = 0;
+        } else if (control == 58) {
+          self.focused_pad = self.focused_pad - 1;
+          if (self.focused_pad < 0) self.focused_pad = 15;
+        } else if (control == 59) {
+          self.focused_pad = self.focused_pad + 1;
+          if (self.focused_pad > 15) self.focused_pad = 0;
+        }
+        $('[data-touch]').removeClass('focus');
+        $('[data-touch=' + self.focused_pad + ']').addClass('focus');
+      }
+      if (control == 41 && value == 127 && self.focused_pad !== undefined) { /* play */
+        var nanotouch = $('.nanotouch')[self.focused_pad];
+        if ($(nanotouch).is('.playing')) {
+          self.samples[self.focused_pad].onended = function() {};  // disable callback
+          self.stopSample(self.focused_pad);
+        }
+        self.startSample(self.focused_pad);
+      }
+      if (control == 42 && value == 127 && self.focused_pad !== undefined) { /* stop */
+        self.stopSample(self.focused_pad);
+      }
       if (control > 23) return; /* after pots */
+      if (self.cycle_being_pressed) {
+        /* 4 -> delay, 5 -> feedback, 6 -> filter, 7 -> master */
+        if (control == 4) {
+          $('#delay').val(value / 127 * 5).trigger('change');
+        } else if (control == 5) {
+          $('#feedback').val(value / 127 * 1).trigger('change');
+        } else if (control == 6) {
+          $('#filter').val(value / 127 * 5000).trigger('change');
+        } else if (control == 7) {
+          $('#master-gain').val(value).trigger('change');
+        }
+        return;
+      }
       if (control < 8) {
         control += 8; /* sliders, control bottom pads (8-15) */
       } else {
@@ -397,10 +460,17 @@ var nanofun = function() {
       $('[data-touch=' + control + '] .touch-gain').val(value).trigger('change');
     }
 
+    $('.loop input').on('change', function() {
+      var sample_idx = parseInt($(this).parents('[data-touch]').data('touch'));
+      if (self.samples[sample_idx]) {
+        self.samples[sample_idx].loop = $(this).is(':checked');
+      }
+    });
+
     $(document).keypress(function(ev) {
       var sample_idx = KEYBOARD_CODES.indexOf(ev.key);
       if (sample_idx != -1) {
-        self.startSample(sample_idx);
+        self.toggleSample(sample_idx);
       }
     });
 
@@ -416,7 +486,8 @@ var nanofun = function() {
 
     $('#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) || 0.0001, now + 0.015);
     });
 
     $('#delay').on('change', function() {
@@ -424,13 +495,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 || 0.0001, 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 || 0.0001, now + 0.015);
     });
 
     $('#filter').on('change', function() {
@@ -440,7 +513,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;
+      if (fraction == 0) {
+        self.touchGainNodes[touchIdx].gain.value = 0;
+      } else {
+        self.touchGainNodes[touchIdx].gain.exponentialRampToValueAtTime((fraction * fraction) || 0.0001, now + 0.015);
+      }
     });
 
     self.time_interval_id = setInterval(function() {
@@ -458,29 +536,42 @@ var nanofun = function() {
     }, 250);
   }
 
+  self.toggleSample = function(sample_idx) {
+    var nanotouch = $('.nanotouch')[sample_idx];
+    if ($(nanotouch).is('.playing')) {
+      self.stopSample(sample_idx);
+    } else {
+      self.startSample(sample_idx);
+    }
+  }
+
   self.startSample = function(sample_idx) {
     var sample_buffer = self.sample_buffers[sample_idx];
     var nanotouch = $('.nanotouch')[sample_idx];
     if (typeof(sample_buffer) != 'undefined') {
-      if (typeof(samples[sample_idx]) != 'undefined' && samples[sample_idx].context.state == 'running') {
+      var sample = self.audioCtx.createBufferSource();
+      var gainNode = self.touchGainNodes[sample_idx];
+      self.samples[sample_idx] = sample;
+      sample.loop = ($(nanotouch).find('.loop input:checked').length == 1);
+      sample.connect(gainNode);
+      sample.buffer = sample_buffer;
+      sample.onended = function() {
+        $(nanotouch).removeClass('playing');
+        var duration = $(nanotouch).find('span.duration');
+        $(duration).text(parseInt($(duration).data('duration')) + 's');
+        self.samples[sample_idx] = undefined;
+      }
+      $(nanotouch).addClass('playing');
+      self.sample_start_times[sample_idx] = sample.context.currentTime;
+      sample.start(0);
+    }
+  }
+  self.stopSample = function(sample_idx) {
+    var sample_buffer = self.sample_buffers[sample_idx];
+    if (typeof(sample_buffer) != 'undefined') {
+      if (typeof(samples[sample_idx]) != 'undefined') {
         self.samples[sample_idx].stop(0);
         self.samples[sample_idx] = undefined;
-      } else {
-        var sample = self.audioCtx.createBufferSource();
-        var gainNode = self.touchGainNodes[sample_idx];
-        self.samples[sample_idx] = sample;
-        sample.loop = ($(nanotouch).find('.loop input:checked').length == 1);
-        sample.connect(gainNode);
-        sample.buffer = sample_buffer;
-        sample.onended = function() {
-          $(nanotouch).removeClass('playing');
-          var duration = $(nanotouch).find('span.duration');
-          $(duration).text(parseInt($(duration).data('duration')) + 's');
-          self.samples[sample_idx] = undefined;
-        }
-        $(nanotouch).addClass('playing');
-        self.sample_start_times[sample_idx] = sample.context.currentTime;
-        sample.start(0);
       }
     }
   }