adamrenklint/bap

View on GitHub
lib/Params.js

Summary

Maintainability
A
1 hr
Test Coverage
A
96%
require('es6-object-assign').polyfill();

var Model = require('./Model');
var triggerParams = require('./mixins/triggerParams');
var volumeParams = require('./mixins/volumeParams');
var oscillatorParams = require('./mixins/oscillatorParams');
var sampleParams = require('./mixins/sampleParams');
var numberInRangeType = require('./types/numberInRange');
var bypassable = require('./mixins/bypassable');
var memoize = require('meemo');

var prime = null;

var Params = Model.extend(triggerParams, volumeParams, oscillatorParams, sampleParams, bypassable, {
  type: 'params',

  props: {
    tempo: ['positiveNumber', true, 120]
  },

  dataTypes: {
    positiveNumber: numberInRangeType('positiveNumber', 1, Infinity)
  }
});

Params.mergeSources = memoize(function (params, sources) {
  var multipliers = ['volume'];
  var adders = ['pitch', 'pan'];
  var keys = Object.keys(Params.prototype._definition);

  keys.forEach(function (key) {
    if (key === 'id') { return; }
    sources.forEach(function (source) {
      if (!source) { return; }
      var value = source[key];
      if (~multipliers.indexOf(key)) {
        if (value < 0) { return; }
        if (value === 0) {
          params[key] = 0;
          return;
        }
        var multiplier = value / 100;
        params[key] = params[key] !== undefined ? params[key] * multiplier : value;
      }
      else if (~adders.indexOf(key)) {
        value = value || 0;
        params[key] = (params[key] || 0) + value;
      }
      else {
        params[key] = value || params[key];
      }
    });

    if (key === 'pan' && params[key] < -100) {
      params[key] = -100;
    }
    else if (key === 'pan' && params[key] > 100) {
      params[key] = 100;
    }
  });

  return params;
}, function (params, sources) {
  var hash = '';
  var keys = Object.keys(Params.prototype._definition);
  var length = keys.length - 1;
  var source, value;

  for (var i = 0, max = sources.length; i < max; i++) {
    source = sources[i];
    if (source) {
      for (var j = 0, maxJ = keys.length; j < maxJ; j++) {
        value = source[keys[j]];
        if (value && keys[j] !== 'id') {
          hash += keys[j] + ':' + value + '/';
        }
      }
    }
    else {
      hash += '*';
    }

    hash += '//';
  }
  return hash;
});

Params.calculateLength = function (params) {
  var existingLength = params.length || 0;
  var lengthFromDuration = params.duration && Params.lengthFromDuration(params, params.duration) || 0;
  if (lengthFromDuration && lengthFromDuration < existingLength || !existingLength) {
    params.hasLengthFromDuration = true;
    return lengthFromDuration;
  }
  else {
    return existingLength;
  }
};

Params.lengthFromDuration = function (params, duration) {
  var secondsPerBeat = 60 / params.tempo;
  var secondsPerTick = secondsPerBeat / 96;
  return duration * secondsPerTick;
};

Params.correctAttackAndRelease = function (params) {
  var total = params.attack + params.release;
  if (params.length && total > params.length) {
    params.attack = (params.length / total) * params.attack;
    params.release = (params.length / total) * params.release;
  }
};

Params.fromSources = function (sources) {
  prime = prime || new Params();
  var params = prime.toJSON();
  params = Object.assign({}, Params.mergeSources(params, sources.reverse()));
  prime.vent.trigger('clock:tempo', params);
  params.length = Params.calculateLength(params);
  Params.correctAttackAndRelease(params);
  return params;
};

module.exports = Params;