var knowledge = require('./knowledge');
var Interval = require('./interval');
var scales = {
aeolian: ['P1', 'M2', 'm3', 'P4', 'P5', 'm6', 'm7'],
blues: ['P1', 'm3', 'P4', 'd5', 'P5', 'm7'],
chromatic: ['P1', 'm2', 'M2', 'm3', 'M3', 'P4',
'A4', 'P5', 'm6', 'M6', 'm7', 'M7'],
dorian: ['P1', 'M2', 'm3', 'P4', 'P5', 'M6', 'm7'],
doubleharmonic: ['P1', 'm2', 'M3', 'P4', 'P5', 'm6', 'M7'],
harmonicminor: ['P1', 'M2', 'm3', 'P4', 'P5', 'm6', 'M7'],
ionian: ['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'M7'],
locrian: ['P1', 'm2', 'm3', 'P4', 'd5', 'm6', 'm7'],
lydian: ['P1', 'M2', 'M3', 'A4', 'P5', 'M6', 'M7'],
majorpentatonic: ['P1', 'M2', 'M3', 'P5', 'M6'],
melodicminor: ['P1', 'M2', 'm3', 'P4', 'P5', 'M6', 'M7'],
minorpentatonic: ['P1', 'm3', 'P4', 'P5', 'm7'],
mixolydian: ['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'm7'],
phrygian: ['P1', 'm2', 'm3', 'P4', 'P5', 'm6', 'm7'],
wholetone: ['P1', 'M2', 'M3', 'A4', 'A5', 'A6']
};
// synonyms
scales.harmonicchromatic = scales.chromatic;
scales.minor = scales.aeolian;
scales.major = scales.ionian;
scales.flamenco = scales.doubleharmonic;
function Scale(tonic, scale) {
if (!(this instanceof Scale)) return new Scale(tonic, scale);
var scaleName, i;
if (!('coord' in tonic)) {
throw new Error('Invalid Tonic');
}
if (typeof scale === 'string') {
scaleName = scale;
scale = scales[scale];
if (!scale)
throw new Error('Invalid Scale');
} else {
for (i in scales) {
if (scales.hasOwnProperty(i)) {
if (scales[i].toString() === scale.toString()) {
scaleName = i;
break;
}
}
}
}
this.name = scaleName;
this.tonic = tonic;
this.scale = scale;
}
Scale.prototype = {
notes: function() {
var notes = [];
for (var i = 0, length = this.scale.length; i < length; i++) {
notes.push(this.tonic.interval(this.scale[i]));
}
return notes;
},
simple: function() {
return this.notes().map(function(n) { return n.toString(true); });
},
type: function() {
var length = this.scale.length - 2;
if (length < 8) {
return ['di', 'tri', 'tetra', 'penta', 'hexa', 'hepta', 'octa'][length] +
'tonic';
}
},
get: function(i) {
var isStepStr = typeof i === 'string' && i in knowledge.stepNumber;
i = isStepStr ? knowledge.stepNumber[i] : i;
var len = this.scale.length;
var interval, octaves;
if (i < 0) {
interval = this.scale[i % len + len - 1];
octaves = Math.floor((i - 1) / len);
} else if (i % len === 0) {
interval = this.scale[len - 1];
octaves = (i / len) - 1;
} else {
interval = this.scale[i % len - 1];
octaves = Math.floor(i / len);
}
return this.tonic.interval(interval).interval(new Interval([octaves, 0]));
},
solfege: function(index, showOctaves) {
if (index)
return this.get(index).solfege(this, showOctaves);
return this.notes().map(function(n) {
return n.solfege(this, showOctaves);
});
},
interval: function(interval) {
interval = (typeof interval === 'string') ?
Interval.toCoord(interval) : interval;
return new Scale(this.tonic.interval(interval), this.scale);
},
transpose: function(interval) {
var scale = this.interval(interval);
this.scale = scale.scale;
this.tonic = scale.tonic;
return this;
}
};
Scale.KNOWN_SCALES = Object.keys(scales);
module.exports = Scale;