Artifact
30e971a9328416bd094ca72ef4b5f0a7565f44d33ae82414b5fb728d5f209693:
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;