Tone.js/test/deps/teoria.js
2017-12-30 11:26:29 -05:00

1451 lines
37 KiB
JavaScript

!function(e){
if("object"===typeof exports&&"undefined"!==typeof module){
module.exports=e();
}else if("function"===typeof define&&define.amd){
define([],e);
}else{
var f;"undefined"!==typeof window?f=window:"undefined"!==typeof global?f=global:"undefined"!==typeof self&&(f=self),f.teoria=e();
}
}(function(){
var define,module,exports;return (function e(t,n,r){
function s(o,u){
if(!n[o]){
if(!t[o]){
var a=typeof require==="function"&&require;if(!u&&a){
return a(o,!0);
}if(i){
return i(o,!0);
}var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f;
}var l=n[o]={ exports : {} };t[o][0].call(l.exports,function(e){
var n=t[o][1][e];return s(n?n:e);
},l,l.exports,e,t,n,r);
}return n[o].exports;
}var i=typeof require==="function"&&require;for(var o=0;o<r.length;o++){
s(r[o]);
}return s;
})({ 1 : [function(require,module,exports){
var Note = require("./lib/note");
var Interval = require("./lib/interval");
var Chord = require("./lib/chord");
var Scale = require("./lib/scale");
// never thought I would write this, but: Legacy support
function intervalConstructor(from, to) {
// Construct a Interval object from string representation
if (typeof from === "string") {
return Interval.toCoord(from);
}
if (typeof to === "string" && from instanceof Note) {
return Interval.from(from, Interval.toCoord(to));
}
if (to instanceof Interval && from instanceof Note) {
return Interval.from(from, to);
}
if (to instanceof Note && from instanceof Note) {
return Interval.between(from, to);
}
throw new Error("Invalid parameters");
}
intervalConstructor.toCoord = Interval.toCoord;
intervalConstructor.from = Interval.from;
intervalConstructor.between = Interval.between;
intervalConstructor.invert = Interval.invert;
function noteConstructor(name, duration) {
if (typeof name === "string") {
return Note.fromString(name, duration);
} else {
return new Note(name, duration);
}
}
noteConstructor.fromString = Note.fromString;
noteConstructor.fromKey = Note.fromKey;
noteConstructor.fromFrequency = Note.fromFrequency;
noteConstructor.fromMIDI = Note.fromMIDI;
function chordConstructor(name, symbol) {
if (typeof name === "string") {
var root, octave;
root = name.match(/^([a-h])(x|#|bb|b?)/i);
if (root && root[0]) {
octave = typeof symbol === "number" ? symbol.toString(10) : "4";
return new Chord(Note.fromString(root[0].toLowerCase() + octave),
name.substr(root[0].length));
}
} else if (name instanceof Note) {
return new Chord(name, symbol);
}
throw new Error("Invalid Chord. Couldn't find note name");
}
function scaleConstructor(tonic, scale) {
tonic = (tonic instanceof Note) ? tonic : teoria.note(tonic);
return new Scale(tonic, scale);
}
var teoria = {
note : noteConstructor,
chord : chordConstructor,
interval : intervalConstructor,
scale : scaleConstructor,
Note : Note,
Chord : Chord,
Scale : Scale,
Interval : Interval
};
require("./lib/sugar")(teoria);
exports = module.exports = teoria;
},{ "./lib/chord" : 2,"./lib/interval" : 3,"./lib/note" : 5,"./lib/scale" : 6,"./lib/sugar" : 7 }],2 : [function(require,module,exports){
var daccord = require("daccord");
var knowledge = require("./knowledge");
var Note = require("./note");
var Interval = require("./interval");
function Chord(root, name) {
if (!(this instanceof Chord)) {
return new Chord(root, name);
}
name = name || "";
this.name = root.name().toUpperCase() + root.accidental() + name;
this.symbol = name;
this.root = root;
this.intervals = [];
this._voicing = [];
var bass = name.split("/");
if (bass.length === 2 && bass[1].trim() !== "9") {
name = bass[0];
bass = bass[1].trim();
} else {
bass = null;
}
this.intervals = daccord(name).map(Interval.toCoord);
this._voicing = this.intervals.slice();
if (bass) {
var intervals = this.intervals, bassInterval, note;
// Make sure the bass is atop of the root note
note = Note.fromString(bass + (root.octave() + 1)); // crude
bassInterval = Interval.between(root, note);
bass = bassInterval.simple();
bassInterval = bassInterval.invert().direction("down");
this._voicing = [bassInterval];
for (var i = 0, length = intervals.length; i < length; i++) {
if (!intervals[i].simple().equal(bass)) {
this._voicing.push(intervals[i]);
}
}
}
}
Chord.prototype = {
notes : function() {
var root = this.root;
return this.voicing().map(function(interval) {
return root.interval(interval);
});
},
simple : function() {
return this.notes().map(function(n) {
return n.toString(true);
});
},
bass : function() {
return this.root.interval(this._voicing[0]);
},
voicing : function(voicing) {
// Get the voicing
if (!voicing) {
return this._voicing;
}
// Set the voicing
this._voicing = [];
for (var i = 0, length = voicing.length; i < length; i++) {
this._voicing[i] = Interval.toCoord(voicing[i]);
}
return this;
},
resetVoicing : function() {
this._voicing = this.intervals;
},
dominant : function(additional) {
additional = additional || "";
return new Chord(this.root.interval("P5"), additional);
},
subdominant : function(additional) {
additional = additional || "";
return new Chord(this.root.interval("P4"), additional);
},
parallel : function(additional) {
additional = additional || "";
var quality = this.quality();
if (this.chordType() !== "triad" || quality === "diminished" ||
quality === "augmented") {
throw new Error("Only major/minor triads have parallel chords");
}
if (quality === "major") {
return new Chord(this.root.interval("m3", "down"), "m");
} else {
return new Chord(this.root.interval("m3", "up"));
}
},
quality : function() {
var third, fifth, seventh, intervals = this.intervals;
for (var i = 0, length = intervals.length; i < length; i++) {
if (intervals[i].number() === 3) {
third = intervals[i];
} else if (intervals[i].number() === 5) {
fifth = intervals[i];
} else if (intervals[i].number() === 7) {
seventh = intervals[i];
}
}
if (!third) {
return;
}
third = (third.direction() === "down") ? third.invert() : third;
third = third.simple().toString();
if (fifth) {
fifth = (fifth.direction === "down") ? fifth.invert() : fifth;
fifth = fifth.simple().toString();
}
if (seventh) {
seventh = (seventh.direction === "down") ? seventh.invert() : seventh;
seventh = seventh.simple().toString();
}
if (third === "M3") {
if (fifth === "A5") {
return "augmented";
} else if (fifth === "P5") {
return (seventh === "m7") ? "dominant" : "major";
}
return "major";
} else if (third === "m3") {
if (fifth === "P5") {
return "minor";
} else if (fifth === "d5") {
return (seventh === "m7") ? "half-diminished" : "diminished";
}
return "minor";
}
},
chordType : function() { // In need of better name
var length = this.intervals.length, interval, has, invert, i, name;
if (length === 2) {
return "dyad";
} else if (length === 3) {
has = { first : false, third : false, fifth : false };
for (i = 0; i < length; i++) {
interval = this.intervals[i];
invert = interval.invert();
if (interval.base() in has) {
has[interval.base()] = true;
} else if (invert.base() in has) {
has[invert.base()] = true;
}
}
name = (has.first && has.third && has.fifth) ? "triad" : "trichord";
} else if (length === 4) {
has = { first : false, third : false, fifth : false, seventh : false };
for (i = 0; i < length; i++) {
interval = this.intervals[i];
invert = interval.invert();
if (interval.base() in has) {
has[interval.base()] = true;
} else if (invert.base() in has) {
has[invert.base()] = true;
}
}
if (has.first && has.third && has.fifth && has.seventh) {
name = "tetrad";
}
}
return name || "unknown";
},
get : function(interval) {
if (typeof interval === "string" && interval in knowledge.stepNumber) {
var intervals = this.intervals, i, length;
interval = knowledge.stepNumber[interval];
for (i = 0, length = intervals.length; i < length; i++) {
if (intervals[i].number() === interval) {
return this.root.interval(intervals[i]);
}
}
return null;
} else {
throw new Error("Invalid interval name");
}
},
interval : function(interval) {
return new Chord(this.root.interval(interval), this.symbol);
},
transpose : function(interval) {
this.root.transpose(interval);
this.name = this.root.name().toUpperCase() +
this.root.accidental() + this.symbol;
return this;
},
toString : function() {
return this.name;
}
};
module.exports = Chord;
},{ "./interval" : 3,"./knowledge" : 4,"./note" : 5,"daccord" : 9 }],3 : [function(require,module,exports){
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 without = vector.sub(this.coord,
vector.mul(knowledge.sharp, Math.floor((this.coord[1] - 2) / 7) + 1))
, i, val;
i = knowledge.intervalFromFifth[without[1] + 5];
val = knowledge.stepNumber[i] + (without[0] - knowledge.intervals[i][0]) * 7;
return (val > 0) ? val : val - 2;
},
type : function() {
return knowledge.intervals[this.base()][0] <= 1 ? "perfect" : "minor";
},
base : function() {
var fifth = vector.sub(this.coord, vector.mul(knowledge.sharp, this.qualityValue()))[1], name;
fifth = this.value() > 0 ? fifth + 5 : -(fifth - 5) % 7;
fifth = fifth < 0 ? knowledge.intervalFromFifth.length + fifth : fifth;
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()];
simple = vector.add(simple, vector.mul(knowledge.sharp, this.qualityValue()));
// 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 without, octaves;
if (this.direction() === "up") {
without = vector.sub(this.coord, vector.mul(knowledge.sharp, this.qualityValue()));
octaves = without[0] - knowledge.intervals[this.base()][0];
} else {
without = vector.sub(this.coord, vector.mul(knowledge.sharp, -this.qualityValue()));
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 coord = knowledge.intervals[knowledge.intervalsIndex[9 - knowledge.stepNumber[i] - 1]];
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;
},{ "./knowledge" : 4,"./vector" : 8,"interval-coords" : 13 }],4 : [function(require,module,exports){
// Note coordinates [octave, fifth] relative to C
module.exports = {
notes : {
c : [0, 0],
d : [-1, 2],
e : [-2, 4],
f : [1, -1],
g : [0, 1],
a : [-1, 3],
b : [-2, 5],
h : [-2, 5]
},
intervals : {
unison : [0, 0],
second : [3, -5],
third : [2, -3],
fourth : [1, -1],
fifth : [0, 1],
sixth : [3, -4],
seventh : [2, -2],
octave : [1, 0]
},
intervalFromFifth : ["second", "sixth", "third", "seventh", "fourth",
"unison", "fifth"],
intervalsIndex : ["unison", "second", "third", "fourth", "fifth",
"sixth", "seventh", "octave", "ninth", "tenth",
"eleventh", "twelfth", "thirteenth", "fourteenth",
"fifteenth"],
// linaer index to fifth = (2 * index + 1) % 7
fifths : ["f", "c", "g", "d", "a", "e", "b"],
accidentals : ["bb", "b", "", "#", "x"],
sharp : [-4, 7],
A4 : [3, 3],
durations : {
"0.25" : "longa",
"0.5" : "breve",
"1" : "whole",
"2" : "half",
"4" : "quarter",
"8" : "eighth",
"16" : "sixteenth",
"32" : "thirty-second",
"64" : "sixty-fourth",
"128" : "hundred-twenty-eighth"
},
qualityLong : {
P : "perfect",
M : "major",
m : "minor",
A : "augmented",
AA : "doubly augmented",
d : "diminished",
dd : "doubly diminished"
},
alterations : {
perfect : ["dd", "d", "P", "A", "AA"],
minor : ["dd", "d", "m", "M", "A", "AA"]
},
symbols : {
"min" : ["m3", "P5"],
"m" : ["m3", "P5"],
"-" : ["m3", "P5"],
"M" : ["M3", "P5"],
"" : ["M3", "P5"],
"+" : ["M3", "A5"],
"aug" : ["M3", "A5"],
"dim" : ["m3", "d5"],
"o" : ["m3", "d5"],
"maj" : ["M3", "P5", "M7"],
"dom" : ["M3", "P5", "m7"],
"ø" : ["m3", "d5", "m7"],
"5" : ["P5"]
},
chordShort : {
"major" : "M",
"minor" : "m",
"augmented" : "aug",
"diminished" : "dim",
"half-diminished" : "7b5",
"power" : "5",
"dominant" : "7"
},
stepNumber : {
"unison" : 1,
"first" : 1,
"second" : 2,
"third" : 3,
"fourth" : 4,
"fifth" : 5,
"sixth" : 6,
"seventh" : 7,
"octave" : 8,
"ninth" : 9,
"eleventh" : 11,
"thirteenth" : 13
},
// Adjusted Shearer syllables - Chromatic solfege system
// Some intervals are not provided for. These include:
// dd2 - Doubly diminished second
// dd3 - Doubly diminished third
// AA3 - Doubly augmented third
// dd6 - Doubly diminished sixth
// dd7 - Doubly diminished seventh
// AA7 - Doubly augmented seventh
intervalSolfege : {
"dd1" : "daw",
"d1" : "de",
"P1" : "do",
"A1" : "di",
"AA1" : "dai",
"d2" : "raw",
"m2" : "ra",
"M2" : "re",
"A2" : "ri",
"AA2" : "rai",
"d3" : "maw",
"m3" : "me",
"M3" : "mi",
"A3" : "mai",
"dd4" : "faw",
"d4" : "fe",
"P4" : "fa",
"A4" : "fi",
"AA4" : "fai",
"dd5" : "saw",
"d5" : "se",
"P5" : "so",
"A5" : "si",
"AA5" : "sai",
"d6" : "law",
"m6" : "le",
"M6" : "la",
"A6" : "li",
"AA6" : "lai",
"d7" : "taw",
"m7" : "te",
"M7" : "ti",
"A7" : "tai",
"dd8" : "daw",
"d8" : "de",
"P8" : "do",
"A8" : "di",
"AA8" : "dai"
}
};
},{}],5 : [function(require,module,exports){
var scientific = require("scientific-notation");
var helmholtz = require("helmholtz");
var knowledge = require("./knowledge");
var vector = require("./vector");
var Interval = require("./interval");
function pad(str, ch, len) {
for (; len > 0; len--) {
str += ch;
}
return str;
}
function Note(coord, duration) {
if (!(this instanceof Note)) {
return new Note(coord, duration);
}
duration = duration || {};
this.duration = { value : duration.value || 4, dots : duration.dots || 0 };
this.coord = coord;
}
Note.prototype = {
octave : function() {
return this.coord[0] + knowledge.A4[0] - knowledge.notes[this.name()][0] +
this.accidentalValue() * 4;
},
name : function() {
return knowledge.fifths[this.coord[1] + knowledge.A4[1] - this.accidentalValue() * 7 + 1];
},
accidentalValue : function() {
return Math.round((this.coord[1] + knowledge.A4[1] - 2) / 7);
},
accidental : function() {
return knowledge.accidentals[this.accidentalValue() + 2];
},
/**
* Returns the key number of the note
*/
key : function(white) {
if (white) {
return this.coord[0] * 7 + this.coord[1] * 4 + 29;
} else {
return this.coord[0] * 12 + this.coord[1] * 7 + 49;
}
},
/**
* Returns a number ranging from 0-127 representing a MIDI note value
*/
midi : function() {
return this.key() + 20;
},
/**
* Calculates and returns the frequency of the note.
* Optional concert pitch (def. 440)
*/
fq : function(concertPitch) {
concertPitch = concertPitch || 440;
return concertPitch *
Math.pow(2, (this.coord[0] * 12 + this.coord[1] * 7) / 12);
},
/**
* Returns the pitch class index (chroma) of the note
*/
chroma : function() {
var value = (vector.sum(vector.mul(this.coord, [12, 7])) - 3) % 12;
return (value < 0) ? value + 12 : value;
},
interval : function(interval) {
if (typeof interval === "string") {
interval = Interval.toCoord(interval);
}
if (interval instanceof Interval) {
return new Note(vector.add(this.coord, interval.coord));
} else if (interval instanceof Note) {
return new Interval(vector.sub(interval.coord, this.coord));
}
},
transpose : function(interval) {
this.coord = vector.add(this.coord, interval.coord);
return this;
},
/**
* Returns the Helmholtz notation form of the note (fx C,, d' F# g#'')
*/
helmholtz : function() {
var octave = this.octave();
var name = this.name();
name = octave < 3 ? name.toUpperCase() : name.toLowerCase();
var padchar = octave < 3 ? "," : "'";
var padcount = octave < 2 ? 2 - octave : octave - 3;
return pad(name + this.accidental(), padchar, padcount);
},
/**
* Returns the scientific notation form of the note (fx E4, Bb3, C#7 etc.)
*/
scientific : function() {
return this.name().toUpperCase() + this.accidental() + this.octave();
},
/**
* Returns notes that are enharmonic with this note.
*/
enharmonics : function(oneaccidental) {
var key = this.key(), limit = oneaccidental ? 2 : 3;
return ["m3", "m2", "m-2", "m-3"]
.map(this.interval.bind(this))
.filter(function(note) {
var acc = note.accidentalValue();
var diff = key - (note.key() - acc);
if (diff < limit && diff > -limit) {
note.coord = vector.add(note.coord, vector.mul(knowledge.sharp, diff - acc));
return true;
}
});
},
solfege : function(scale, showOctaves) {
var interval = scale.tonic.interval(this), solfege, stroke, count;
if (interval.direction() === "down") {
interval = interval.invert();
}
if (showOctaves) {
count = (this.key(true) - scale.tonic.key(true)) / 7;
count = (count >= 0) ? Math.floor(count) : -(Math.ceil(-count));
stroke = (count >= 0) ? "'" : ",";
}
solfege = knowledge.intervalSolfege[interval.simple(true).toString()];
return (showOctaves) ? pad(solfege, stroke, Math.abs(count)) : solfege;
},
scaleDegree : function(scale) {
var inter = scale.tonic.interval(this);
// If the direction is down, or we're dealing with an octave - invert it
if (inter.direction() === "down" ||
(inter.coord[1] === 0 && inter.coord[0] !== 0)) {
inter = inter.invert();
}
inter = inter.simple(true).coord;
return scale.scale.reduce(function(index, current, i) {
var coord = Interval.toCoord(current).coord;
return coord[0] === inter[0] && coord[1] === inter[1] ? i + 1 : index;
}, 0);
},
/**
* Returns the name of the duration value,
* such as 'whole', 'quarter', 'sixteenth' etc.
*/
durationName : function() {
return knowledge.durations[this.duration.value];
},
/**
* Returns the duration of the note (including dots)
* in seconds. The first argument is the tempo in beats
* per minute, the second is the beat unit (i.e. the
* lower numeral in a time signature).
*/
durationInSeconds : function(bpm, beatUnit) {
var secs = (60 / bpm) / (this.duration.value / 4) / (beatUnit / 4);
return secs * 2 - secs / Math.pow(2, this.duration.dots);
},
/**
* Returns the name of the note, with an optional display of octave number
*/
toString : function(dont) {
return this.name() + this.accidental() + (dont ? "" : this.octave());
}
};
Note.fromString = function(name, dur) {
var coord = scientific(name);
if (!coord) {
coord = helmholtz(name);
}
return new Note(coord, dur);
};
Note.fromKey = function(key) {
var octave = Math.floor((key - 4) / 12);
var distance = key - (octave * 12) - 4;
var name = knowledge.fifths[(2 * Math.round(distance / 2) + 1) % 7];
var note = vector.add(vector.sub(knowledge.notes[name], knowledge.A4), [octave + 1, 0]);
var diff = (key - 49) - vector.sum(vector.mul(note, [12, 7]));
return new Note(diff ? vector.add(note, vector.mul(knowledge.sharp, diff)) : note);
};
Note.fromFrequency = function(fq, concertPitch) {
var key, cents, originalFq;
concertPitch = concertPitch || 440;
key = 49 + 12 * ((Math.log(fq) - Math.log(concertPitch)) / Math.log(2));
key = Math.round(key);
originalFq = concertPitch * Math.pow(2, (key - 49) / 12);
cents = 1200 * (Math.log(fq / originalFq) / Math.log(2));
return { note : Note.fromKey(key), cents : cents };
};
Note.fromMIDI = function(note) {
return Note.fromKey(note - 20);
};
module.exports = Note;
},{ "./interval" : 3,"./knowledge" : 4,"./vector" : 8,"helmholtz" : 10,"scientific-notation" : 14 }],6 : [function(require,module,exports){
var knowledge = require("./knowledge");
var Interval = require("./interval");
var scales = {
aeolian : ["P1", "M2", "m3", "P4", "P5", "m6", "m7"],
blues : ["P1", "m3", "P4", "A4", "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"]
};
// 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) {
i = (typeof i === "string" && i in knowledge.stepNumber) ? knowledge.stepNumber[i] : i;
return this.tonic.interval(this.scale[i - 1]);
},
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;
}
};
module.exports = Scale;
},{ "./interval" : 3,"./knowledge" : 4 }],7 : [function(require,module,exports){
var knowledge = require("./knowledge");
module.exports = function(teoria) {
var Note = teoria.Note;
var Chord = teoria.Chord;
var Scale = teoria.Scale;
Note.prototype.chord = function(chord) {
chord = (chord in knowledge.chordShort) ? knowledge.chordShort[chord] : chord;
return new Chord(this, chord);
};
Note.prototype.scale = function(scale) {
return new Scale(this, scale);
};
};
},{ "./knowledge" : 4 }],8 : [function(require,module,exports){
module.exports = {
add : function(note, interval) {
return [note[0] + interval[0], note[1] + interval[1]];
},
sub : function(note, interval) {
return [note[0] - interval[0], note[1] - interval[1]];
},
mul : function(note, interval) {
if (typeof interval === "number") {
return [note[0] * interval, note[1] * interval];
} else {
return [note[0] * interval[0], note[1] * interval[1]];
}
},
sum : function(coord) {
return coord[0] + coord[1];
}
};
},{}],9 : [function(require,module,exports){
var SYMBOLS = {
"m" : ["m3", "P5"],
"mi" : ["m3", "P5"],
"min" : ["m3", "P5"],
"-" : ["m3", "P5"],
"M" : ["M3", "P5"],
"ma" : ["M3", "P5"],
"" : ["M3", "P5"],
"+" : ["M3", "A5"],
"aug" : ["M3", "A5"],
"dim" : ["m3", "d5"],
"o" : ["m3", "d5"],
"maj" : ["M3", "P5", "M7"],
"dom" : ["M3", "P5", "m7"],
"ø" : ["m3", "d5", "m7"],
"5" : ["P5"],
"6/9" : ["M3", "P5", "M6", "M9"]
};
module.exports = function(symbol) {
var c, parsing = "quality", additionals = [], name, chordLength = 2;
var notes = ["P1", "M3", "P5", "m7", "M9", "P11", "M13"];
var explicitMajor = false;
function setChord(name) {
var intervals = SYMBOLS[name];
for (var i = 0, len = intervals.length; i < len; i++) {
notes[i + 1] = intervals[i];
}
chordLength = intervals.length;
}
// Remove whitespace, commas and parentheses
symbol = symbol.replace(/[,\s\(\)]/g, "");
for (var i = 0, len = symbol.length; i < len; i++) {
if (!(c = symbol[i])) {
return;
}
if (parsing === "quality") {
var sub3 = (i + 2) < len ? symbol.substr(i, 3).toLowerCase() : null;
var sub2 = (i + 1) < len ? symbol.substr(i, 2).toLowerCase() : null;
if (sub3 in SYMBOLS) {
name = sub3;
} else if (sub2 in SYMBOLS) {
name = sub2;
} else if (c in SYMBOLS) {
name = c;
} else {
name = "";
}
if (name) {
setChord(name);
}
if (name === "M" || name === "ma" || name === "maj") {
explicitMajor = true;
}
i += name.length - 1;
parsing = "extension";
} else if (parsing === "extension") {
c = (c === "1" && symbol[i + 1]) ? +symbol.substr(i, 2) : +c;
if (!isNaN(c) && c !== 6) {
chordLength = (c - 1) / 2;
if (chordLength !== Math.round(chordLength)) {
return new Error("Invalid interval extension: " + c.toString(10));
}
if (name === "o" || name === "dim") {
notes[3] = "d7";
} else if (explicitMajor) {
notes[3] = "M7";
}
i += c >= 10 ? 1 : 0;
} else if (c === 6) {
notes[3] = "M6";
chordLength = Math.max(3, chordLength);
} else {
i -= 1;
}
parsing = "alterations";
} else if (parsing === "alterations") {
var alterations = symbol.substr(i).split(/(#|b|add|maj|sus|M)/i),
next, flat = false, sharp = false;
if (alterations.length === 1) {
return new Error("Invalid alteration");
} else if (alterations[0].length !== 0) {
return new Error("Invalid token: '" + alterations[0] + "'");
}
var ignore = false;
alterations.forEach(function(alt, i, arr) {
if (ignore || !alt.length) {
return ignore = false;
}
var next = arr[i + 1], lower = alt.toLowerCase();
if (alt === "M" || lower === "maj") {
if (next === "7") {
ignore = true;
}
chordLength = Math.max(3, chordLength);
notes[3] = "M7";
} else if (lower === "sus") {
var type = "P4";
if (next === "2" || next === "4") {
ignore = true;
if (next === "2") {
type = "M2";
}
}
notes[1] = type; // Replace third with M2 or P4
} else if (lower === "add") {
if (next === "9") {
additionals.push("M9");
} else if (next === "11") {
additionals.push("P11");
} else if (next === "13") {
additionals.push("M13");
}
ignore = true;
} else if (lower === "b") {
flat = true;
} else if (lower === "#") {
sharp = true;
} else {
var token = +alt, quality, intPos;
if (isNaN(token) || String(token).length !== alt.length) {
return new Error("Invalid token: '" + alt + "'");
}
if (token === 6) {
if (sharp) {
notes[3] = "A6";
} else if (flat) {
notes[3] = "m6";
} else {
notes[3] = "M6";
}
chordLength = Math.max(3, chordLength);
return;
}
// Calculate the position in the 'note' array
intPos = (token - 1) / 2;
if (chordLength < intPos) {
chordLength = intPos;
}
if (token < 5 || token === 7 || intPos !== Math.round(intPos)) {
return new Error("Invalid interval alteration: " + token);
}
quality = notes[intPos][0];
// Alterate the quality of the interval according the accidentals
if (sharp) {
if (quality === "d") {
quality = "m";
} else if (quality === "m") {
quality = "M";
} else if (quality === "M" || quality === "P") {
quality = "A";
}
} else if (flat) {
if (quality === "A") {
quality = "M";
} else if (quality === "M") {
quality = "m";
} else if (quality === "m" || quality === "P") {
quality = "d";
}
}
sharp = flat = false;
notes[intPos] = quality + token;
}
});
parsing = "ended";
} else if (parsing === "ended") {
break;
}
}
return notes.slice(0, chordLength + 1).concat(additionals);
};
},{}],10 : [function(require,module,exports){
var coords = require("notecoord");
var accval = require("accidental-value");
module.exports = function helmholtz(name) {
var name = name.replace(/\u2032/g, "'").replace(/\u0375/g, ",");
var parts = name.match(/^(,*)([a-h])(x|#|bb|b?)([,\']*)$/i);
if (!parts || name !== parts[0]) {
throw new Error("Invalid formatting");
}
var note = parts[2];
var octaveFirst = parts[1];
var octaveLast = parts[4];
var lower = note === note.toLowerCase();
var octave;
if (octaveFirst) {
if (lower) {
throw new Error("Invalid formatting - found commas before lowercase note");
}
octave = 2 - octaveFirst.length;
} else if (octaveLast) {
if (octaveLast.match(/^'+$/) && lower) {
octave = 3 + octaveLast.length;
} else if (octaveLast.match(/^,+$/) && !lower) {
octave = 2 - octaveLast.length;
} else {
throw new Error("Invalid formatting - mismatch between octave " +
"indicator and letter case");
}
} else {
octave = lower ? 3 : 2;
}
var accidentalValue = accval.interval(parts[3].toLowerCase());
var coord = coords(note.toLowerCase());
coord[0] += octave;
coord[0] += accidentalValue[0] - coords.A4[0];
coord[1] += accidentalValue[1] - coords.A4[1];
return coord;
};
},{ "accidental-value" : 11,"notecoord" : 12 }],11 : [function(require,module,exports){
var accidentalValues = {
"bb" : -2,
"b" : -1,
"" : 0,
"#" : 1,
"x" : 2
};
module.exports = function accidentalNumber(acc) {
return accidentalValues[acc];
};
module.exports.interval = function accidentalInterval(acc) {
var val = accidentalValues[acc];
return [-4 * val, 7 * val];
};
},{}],12 : [function(require,module,exports){
// First coord is octaves, second is fifths. Distances are relative to c
var notes = {
c : [0, 0],
d : [-1, 2],
e : [-2, 4],
f : [1, -1],
g : [0, 1],
a : [-1, 3],
b : [-2, 5],
h : [-2, 5]
};
module.exports = function(name) {
return name in notes ? [notes[name][0], notes[name][1]] : null;
};
module.exports.notes = notes;
module.exports.A4 = [3, 3]; // Relative to C0 (scientic notation, ~16.35Hz)
module.exports.sharp = [-4, 7];
},{}],13 : [function(require,module,exports){
var pattern = /^(AA|A|P|M|m|d|dd)(-?\d+)$/;
// The interval it takes to raise a note a semitone
var sharp = [-4, 7];
var pAlts = ["dd", "d", "P", "A", "AA"];
var mAlts = ["dd", "d", "m", "M", "A", "AA"];
var baseIntervals = [
[0, 0],
[3, -5],
[2, -3],
[1, -1],
[0, 1],
[3, -4],
[2, -2],
[1, 0]
];
module.exports = function(simple) {
var parser = simple.match(pattern);
if (!parser) {
return null;
}
var quality = parser[1];
var number = +parser[2];
var sign = number < 0 ? -1 : 1;
number = sign < 0 ? -number : number;
var lower = number > 8 ? (number % 7 || 7) : number;
var octaves = (number - lower) / 7;
var base = baseIntervals[lower - 1];
var alts = base[0] <= 1 ? pAlts : mAlts;
var alt = alts.indexOf(quality) - 2;
// this happens, if the alteration wasn't suitable for this type
// of interval, such as P2 or M5 (no "perfect second" or "major fifth")
if (alt === -3) {
return null;
}
return [
sign * (base[0] + octaves + sharp[0] * alt),
sign * (base[1] + sharp[1] * alt)
];
};
// Copy to avoid overwriting internal base intervals
module.exports.coords = baseIntervals.slice(0);
},{}],14 : [function(require,module,exports){
var coords = require("notecoord");
var accval = require("accidental-value");
module.exports = function scientific(name) {
var format = /^([a-h])(x|#|bb|b?)(-?\d*)/i;
parser = name.match(format);
if (!(parser && name === parser[0] && parser[3].length)) {
return;
}
var noteName = parser[1];
var octave = +parser[3];
var accidental = parser[2].length ? parser[2].toLowerCase() : "";
var accidentalValue = accval.interval(accidental);
var coord = coords(noteName.toLowerCase());
coord[0] += octave;
coord[0] += accidentalValue[0] - coords.A4[0];
coord[1] += accidentalValue[1] - coords.A4[1];
return coord;
};
},{ "accidental-value" : 15,"notecoord" : 16 }],15 : [function(require,module,exports){
arguments[4][11][0].apply(exports,arguments);
},{ "dup" : 11 }],16 : [function(require,module,exports){
arguments[4][12][0].apply(exports,arguments);
},{ "dup" : 12 }] },{},[1])(1);
});