UNPKG

teoria

Version:
172 lines (134 loc) 4.69 kB
var knowledge = require('./knowledge'); var vector = require('./vector'); var toCoord = require('interval-coords'); function Interval(coord) { if (!(this instanceof Interval)) return new Interval(coord); this.coord = coord; } Interval.prototype = { name: function() { return knowledge.intervalsIndex[this.number() - 1]; }, semitones: function() { return vector.sum(vector.mul(this.coord, [12, 7])); }, number: function() { return Math.abs(this.value()); }, value: function() { var toMultiply = Math.floor((this.coord[1] - 2) / 7) + 1; var product = vector.mul(knowledge.sharp, toMultiply); var without = vector.sub(this.coord, product); var i = knowledge.intervalFromFifth[without[1] + 5]; var diff = without[0] - knowledge.intervals[i][0]; var val = knowledge.stepNumber[i] + diff * 7; return (val > 0) ? val : val - 2; }, type: function() { return knowledge.intervals[this.base()][0] <= 1 ? 'perfect' : 'minor'; }, base: function() { var product = vector.mul(knowledge.sharp, this.qualityValue()); var fifth = vector.sub(this.coord, product)[1]; fifth = this.value() > 0 ? fifth + 5 : -(fifth - 5) % 7; fifth = fifth < 0 ? knowledge.intervalFromFifth.length + fifth : fifth; var name = knowledge.intervalFromFifth[fifth]; if (name === 'unison' && this.number() >= 8) name = 'octave'; return name; }, direction: function(dir) { if (dir) { var is = this.value() >= 1 ? 'up' : 'down'; if (is !== dir) this.coord = vector.mul(this.coord, -1); return this; } else return this.value() >= 1 ? 'up' : 'down'; }, simple: function(ignore) { // Get the (upwards) base interval (with quality) var simple = knowledge.intervals[this.base()]; var toAdd = vector.mul(knowledge.sharp, this.qualityValue()); simple = vector.add(simple, toAdd); // Turn it around if necessary if (!ignore) simple = this.direction() === 'down' ? vector.mul(simple, -1) : simple; return new Interval(simple); }, isCompound: function() { return this.number() > 8; }, octaves: function() { var toSubtract, without, octaves; if (this.direction() === 'up') { toSubtract = vector.mul(knowledge.sharp, this.qualityValue()); without = vector.sub(this.coord, toSubtract); octaves = without[0] - knowledge.intervals[this.base()][0]; } else { toSubtract = vector.mul(knowledge.sharp, -this.qualityValue()); without = vector.sub(this.coord, toSubtract); octaves = -(without[0] + knowledge.intervals[this.base()][0]); } return octaves; }, invert: function() { var i = this.base(); var qual = this.qualityValue(); var acc = this.type() === 'minor' ? -(qual - 1) : -qual; var idx = 9 - knowledge.stepNumber[i] - 1; var coord = knowledge.intervals[knowledge.intervalsIndex[idx]]; coord = vector.add(coord, vector.mul(knowledge.sharp, acc)); return new Interval(coord); }, quality: function(lng) { var quality = knowledge.alterations[this.type()][this.qualityValue() + 2]; return lng ? knowledge.qualityLong[quality] : quality; }, qualityValue: function() { if (this.direction() === 'down') return Math.floor((-this.coord[1] - 2) / 7) + 1; else return Math.floor((this.coord[1] - 2) / 7) + 1; }, equal: function(interval) { return this.coord[0] === interval.coord[0] && this.coord[1] === interval.coord[1]; }, greater: function(interval) { var semi = this.semitones(); var isemi = interval.semitones(); // If equal in absolute size, measure which interval is bigger // For example P4 is bigger than A3 return (semi === isemi) ? (this.number() > interval.number()) : (semi > isemi); }, smaller: function(interval) { return !this.equal(interval) && !this.greater(interval); }, add: function(interval) { return new Interval(vector.add(this.coord, interval.coord)); }, toString: function(ignore) { // If given true, return the positive value var number = ignore ? this.number() : this.value(); return this.quality() + number; } }; Interval.toCoord = function(simple) { var coord = toCoord(simple); if (!coord) throw new Error('Invalid simple format interval'); return new Interval(coord); }; Interval.from = function(from, to) { return from.interval(to); }; Interval.between = function(from, to) { return new Interval(vector.sub(to.coord, from.coord)); }; Interval.invert = function(sInterval) { return Interval.toCoord(sInterval).invert().toString(); }; module.exports = Interval;