File size: 7,089 Bytes
af6912c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
// abc_transpose.js: Handles the automatic transposition of key signatures, chord symbols, and notes.
var allNotes = require("./all-notes");
var transposeChordName = require("../parse/transpose-chord")
var keyAccidentals = require('../const/key-accidentals');
var transpose = {};
var keyIndex = {
'C': 0,
'C#': 1,
'Db': 1,
'D': 2,
'D#': 3,
'Eb': 3,
'E': 4,
'F': 5,
'F#': 6,
'Gb': 6,
'G': 7,
'G#': 8,
'Ab': 8,
'A': 9,
'A#': 10,
'Bb': 10,
'B': 11
};
var newKey = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B'];
var newKeyMinor = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'Bb', 'B'];
transpose.keySignature = function(multilineVars, keyName, root, acc, localTranspose) {
if (multilineVars.clef.type === "perc" || multilineVars.clef.type === "none")
return { accidentals: keyAccidentals(keyName), root: root, acc: acc };
if (!localTranspose) localTranspose = 0;
multilineVars.localTransposeVerticalMovement = 0;
multilineVars.localTransposePreferFlats = false;
var k = keyAccidentals(keyName);
if (!k) return multilineVars.key; // If the key isn't in the list, it is non-standard. We won't attempt to transpose it.
multilineVars.localTranspose = (multilineVars.globalTranspose ? multilineVars.globalTranspose : 0) + localTranspose;
if (!multilineVars.localTranspose)
return { accidentals: k, root: root, acc: acc };
multilineVars.globalTransposeOrigKeySig = k;
if (multilineVars.localTranspose % 12 === 0) {
multilineVars.localTransposeVerticalMovement = (multilineVars.localTranspose / 12) * 7;
return { accidentals: k, root: root, acc: acc };
}
var baseKey = keyName[0];
if (keyName[1] === 'b' || keyName[1] === '#') {
baseKey += keyName[1];
keyName = keyName.substr(2);
} else
keyName = keyName.substr(1);
var thisKeyIndex = keyIndex[baseKey]
var recognized = thisKeyIndex !== undefined
if (!recognized) {
// Either the key sig is "none" or we don't recognize it. Either way we don't change it, and we assume key of C for the purposes of this calculation.
thisKeyIndex = 0
baseKey = "C"
keyName = ""
}
var index = thisKeyIndex + multilineVars.localTranspose;
while (index < 0) index += 12;
if (index > 11) index = index % 12;
var newKeyName = (keyName[0] === 'm' ? newKeyMinor[index] : newKey[index]);
var transposedKey = newKeyName + keyName;
var newKeySig = keyAccidentals(transposedKey);
if (newKeySig.length > 0 && newKeySig[0].acc === 'flat')
multilineVars.localTransposePreferFlats = true;
var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
if (multilineVars.localTranspose > 0) {
if (distance < 0)
distance += 7;
else if (distance === 0) {
// There's a funny thing that happens when the key changes only an accidental's distance, for instance, from Ab to A.
// If the distance is positive (we are raising pitch), and the change is higher (that is, Ab -> A), then raise an octave.
// This test is easier because we know the keys are not equal (or we wouldn't get this far), so if the base key is a flat key, then
// the transposed key must be higher. Likewise, if the transposed key is sharp, then the base key must be lower. And one
// of those two things must be true because they are not both natural.
if (baseKey[1] === '#' || transposedKey[1] === 'b')
distance += 7;
}
} else if (multilineVars.localTranspose < 0) {
if (distance > 0)
distance -= 7;
else if (distance === 0) {
// There's a funny thing that happens when the key changes only an accidental's distance, for instance, from Ab to A.
// If the distance is negative (we are dropping pitch), and the change is lower (that is, A -> Ab), then drop an octave.
if (baseKey[1] === 'b' || transposedKey[1] === '#')
distance -= 7;
}
}
if (multilineVars.localTranspose > 0)
multilineVars.localTransposeVerticalMovement = distance + Math.floor(multilineVars.localTranspose / 12) * 7;
else
multilineVars.localTransposeVerticalMovement = distance + Math.ceil(multilineVars.localTranspose / 12) * 7;
if (recognized)
return { accidentals: newKeySig, root: newKeyName[0], acc: newKeyName.length > 1 ? newKeyName[1] : "" };
else
return { accidentals: [], root: root, acc: acc };
};
transpose.chordName = function(multilineVars, chord) {
return transposeChordName(chord, multilineVars.localTranspose, multilineVars.localTransposePreferFlats, multilineVars.freegchord)
};
var pitchToLetter = [ 'c', 'd', 'e', 'f', 'g', 'a', 'b' ];
function accidentalChange(origPitch, newPitch, accidental, origKeySig, newKeySig) {
var origPitchLetter = pitchToLetter[(origPitch + 49) % 7]; // Make sure it is a positive pitch before normalizing.
var origAccidental = 0;
for (var i = 0; i < origKeySig.length; i++) {
if (origKeySig[i].note.toLowerCase() === origPitchLetter)
origAccidental = accidentals[origKeySig[i].acc];
}
var currentAccidental = accidentals[accidental];
var delta = currentAccidental - origAccidental;
var newPitchLetter = pitchToLetter[(newPitch + 49) % 7]; // Make sure it is a positive pitch before normalizing.
var newAccidental = 0;
for (var j = 0; j < newKeySig.accidentals.length; j++) {
if (newKeySig.accidentals[j].note.toLowerCase() === newPitchLetter)
newAccidental = accidentals[newKeySig.accidentals[j].acc];
}
var calcAccidental = delta + newAccidental;
if (calcAccidental < -2) {
newPitch--;
calcAccidental += (newPitchLetter === 'c' || newPitchLetter === 'f') ? 1 : 2;
}
if (calcAccidental > 2) {
newPitch++;
calcAccidental -= (newPitchLetter === 'b' || newPitchLetter === 'e') ? 1 : 2;
}
return [newPitch, calcAccidental];
}
var accidentals = {
dblflat: -2,
flat: -1,
natural: 0,
sharp: 1,
dblsharp: 2
};
var accidentals2 = {
"-2": "dblflat",
"-1": "flat",
"0": "natural",
"1": "sharp",
"2": "dblsharp"
};
var accidentals3 = {
"-2": "__",
"-1": "_",
"0": "=",
"1": "^",
"2": "^^"
};
//var count = 0
transpose.note = function(multilineVars, el) {
// the "el" that is passed in has el.name, el.accidental, and el.pitch. "pitch" is the vertical position (0=middle C)
// localTranspose is the number of half steps
// localTransposeVerticalMovement is the vertical distance to move.
//console.log(count++,multilineVars.localTranspose, el)
if (!multilineVars.localTranspose || multilineVars.clef.type === "perc")
return;
var origPitch = el.pitch;
if (multilineVars.localTransposeVerticalMovement) {
el.pitch = el.pitch + multilineVars.localTransposeVerticalMovement;
if (el.name) {
var actual = el.accidental ? el.name.substring(1) : el.name
var acc = el.accidental ? el.name[0] : ''
var p = allNotes.pitchIndex(actual)
el.name = acc + allNotes.noteName(p+multilineVars.localTransposeVerticalMovement)
}
}
if (el.accidental) {
var ret = accidentalChange(origPitch, el.pitch, el.accidental, multilineVars.globalTransposeOrigKeySig, multilineVars.targetKey);
el.pitch = ret[0];
el.accidental = accidentals2[ret[1]];
if (el.name) {
el.name = accidentals3[ret[1]] + el.name.replace(/[_^=]/g,'');
}
}
};
module.exports = transpose;
|