abcjs 6.0.3 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/RELEASE.md +53 -1
- package/SECURITY.md +9 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +1378 -484
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/index.js +2 -0
- package/package.json +2 -2
- package/src/api/abc_tunebook_svg.js +2 -98
- package/src/const/key-accidentals.js +53 -0
- package/src/const/relative-major.js +92 -0
- package/src/parse/abc_parse.js +20 -9
- package/src/parse/abc_parse_book.js +3 -2
- package/src/parse/abc_parse_key_voice.js +1 -148
- package/src/parse/abc_parse_music.js +2 -2
- package/src/parse/abc_transpose.js +9 -68
- package/src/parse/transpose-chord.js +80 -0
- package/src/str/output.js +433 -0
- package/src/synth/abc_midi_flattener.js +1 -1
- package/src/synth/create-note-map.js +4 -0
- package/src/test/abc_parser_lint.js +1 -1
- package/src/write/abc_abstract_engraver.js +12 -0
- package/src/write/abc_beam_element.js +24 -0
- package/src/write/abc_decoration.js +13 -0
- package/src/write/abc_engraver_controller.js +64 -1
- package/src/write/abc_glissando_element.js +7 -0
- package/src/write/draw/draw.js +8 -0
- package/src/write/draw/glissando.js +75 -0
- package/src/write/draw/staff-group.js +5 -5
- package/src/write/draw/voice.js +4 -0
- package/src/write/selection.js +48 -12
- package/src/write/top-text.js +1 -1
- package/types/index.d.ts +56 -8
- package/version.js +1 -1
- package/temp.txt +0 -16
package/dist/abcjs-basic.js
CHANGED
|
@@ -48,6 +48,8 @@ var tuneBook = __webpack_require__(/*! ./src/api/abc_tunebook */ "./src/api/abc_
|
|
|
48
48
|
|
|
49
49
|
var sequence = __webpack_require__(/*! ./src/synth/abc_midi_sequencer */ "./src/synth/abc_midi_sequencer.js");
|
|
50
50
|
|
|
51
|
+
var strTranspose = __webpack_require__(/*! ./src/str/output */ "./src/str/output.js");
|
|
52
|
+
|
|
51
53
|
var abcjs = {};
|
|
52
54
|
abcjs.signature = "abcjs-basic v" + version;
|
|
53
55
|
Object.keys(animation).forEach(function (key) {
|
|
@@ -62,6 +64,7 @@ abcjs.TimingCallbacks = __webpack_require__(/*! ./src/api/abc_timing_callbacks *
|
|
|
62
64
|
var glyphs = __webpack_require__(/*! ./src/write/abc_glyphs */ "./src/write/abc_glyphs.js");
|
|
63
65
|
|
|
64
66
|
abcjs.setGlyph = glyphs.setSymbol;
|
|
67
|
+
abcjs.strTranspose = strTranspose;
|
|
65
68
|
|
|
66
69
|
var CreateSynth = __webpack_require__(/*! ./src/synth/create-synth */ "./src/synth/create-synth.js");
|
|
67
70
|
|
|
@@ -1098,101 +1101,6 @@ function renderOne(div, tune, params, tuneNumber, lineOffset) {
|
|
|
1098
1101
|
var parent = div.parentNode;
|
|
1099
1102
|
parent.style.width = div.style.width;
|
|
1100
1103
|
}
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
function renderEachLineSeparately(div, tune, params, tuneNumber) {
|
|
1104
|
-
function initializeTuneLine(tune) {
|
|
1105
|
-
var obj = new Tune();
|
|
1106
|
-
obj.formatting = tune.formatting;
|
|
1107
|
-
obj.media = tune.media;
|
|
1108
|
-
obj.version = tune.version;
|
|
1109
|
-
return obj;
|
|
1110
|
-
} // Before rendering, chop up the returned tune into an array where each element is a line.
|
|
1111
|
-
// The first element of the array gets the title and other items that go on top, the last element
|
|
1112
|
-
// of the array gets the extra text that goes on bottom. Each element gets any non-music info that comes before it.
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
var tunes = [];
|
|
1116
|
-
var tuneLine;
|
|
1117
|
-
|
|
1118
|
-
for (var i = 0; i < tune.lines.length; i++) {
|
|
1119
|
-
var line = tune.lines[i];
|
|
1120
|
-
if (!tuneLine) tuneLine = initializeTuneLine(tune);
|
|
1121
|
-
|
|
1122
|
-
if (i === 0) {
|
|
1123
|
-
// These items go on top of the music
|
|
1124
|
-
tuneLine.copyTopInfo(tune);
|
|
1125
|
-
} // push the lines until we get to a music line
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
tuneLine.lines.push(line);
|
|
1129
|
-
|
|
1130
|
-
if (line.staff) {
|
|
1131
|
-
tunes.push(tuneLine);
|
|
1132
|
-
tuneLine = undefined;
|
|
1133
|
-
}
|
|
1134
|
-
} // Add any extra stuff to the last line.
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
if (tuneLine) {
|
|
1138
|
-
var lastLine = tunes[tunes.length - 1];
|
|
1139
|
-
|
|
1140
|
-
for (var j = 0; j < tuneLine.lines.length; j++) {
|
|
1141
|
-
lastLine.lines.push(tuneLine.lines[j]);
|
|
1142
|
-
}
|
|
1143
|
-
} // These items go below the music
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
tuneLine = tunes[tunes.length - 1];
|
|
1147
|
-
tuneLine.copyBottomInfo(tune); // Now create sub-divs and render each line. Need to copy the params to change the padding for the interior slices.
|
|
1148
|
-
|
|
1149
|
-
var ep = {};
|
|
1150
|
-
|
|
1151
|
-
for (var key in params) {
|
|
1152
|
-
if (params.hasOwnProperty(key)) {
|
|
1153
|
-
ep[key] = params[key];
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
var origPaddingTop = ep.paddingtop;
|
|
1158
|
-
var origPaddingBottom = ep.paddingbottom;
|
|
1159
|
-
var currentScrollY = div.parentNode.scrollTop; // If there is scrolling it will be lost during the redraw so remember it.
|
|
1160
|
-
|
|
1161
|
-
var currentScrollX = div.parentNode.scrollLeft;
|
|
1162
|
-
div.innerHTML = "";
|
|
1163
|
-
var lineCount = 0;
|
|
1164
|
-
|
|
1165
|
-
for (var k = 0; k < tunes.length; k++) {
|
|
1166
|
-
var lineEl = document.createElement("div");
|
|
1167
|
-
div.appendChild(lineEl);
|
|
1168
|
-
|
|
1169
|
-
if (k === 0) {
|
|
1170
|
-
ep.paddingtop = origPaddingTop;
|
|
1171
|
-
ep.paddingbottom = 0;
|
|
1172
|
-
} else if (k === tunes.length - 1) {
|
|
1173
|
-
ep.paddingtop = 10;
|
|
1174
|
-
ep.paddingbottom = origPaddingBottom;
|
|
1175
|
-
} else {
|
|
1176
|
-
ep.paddingtop = 10;
|
|
1177
|
-
ep.paddingbottom = 0;
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
if (k < tunes.length - 1) {
|
|
1181
|
-
// If it is not the last line, force stretchlast. If it is, stretchlast might have been set by the input parameters.
|
|
1182
|
-
tunes[k].formatting = parseCommon.clone(tunes[k].formatting);
|
|
1183
|
-
tunes[k].formatting.stretchlast = true;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
renderOne(lineEl, tunes[k], ep, tuneNumber, lineCount);
|
|
1187
|
-
lineCount += tunes[k].lines.length;
|
|
1188
|
-
if (k === 0) tune.engraver = tunes[k].engraver;else {
|
|
1189
|
-
if (!tune.engraver.staffgroups) tune.engraver.staffgroups = tunes[k].engraver.staffgroups;else if (tunes[k].engraver.staffgroups.length > 0) tune.engraver.staffgroups.push(tunes[k].engraver.staffgroups[0]);
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
if (currentScrollX || currentScrollY) {
|
|
1194
|
-
div.parentNode.scrollTo(currentScrollX, currentScrollY);
|
|
1195
|
-
}
|
|
1196
1104
|
} // A quick way to render a tune from javascript when interactivity is not required.
|
|
1197
1105
|
// This is used when a javascript routine has some abc text that it wants to render
|
|
1198
1106
|
// in a div or collection of divs. One tune or many can be rendered.
|
|
@@ -1263,8 +1171,9 @@ var renderAbc = function renderAbc(output, abc, parserParams, engraverParams, re
|
|
|
1263
1171
|
if (!removeDiv && params.wrap && params.staffwidth) {
|
|
1264
1172
|
tune = doLineWrapping(div, tune, tuneNumber, abcString, params);
|
|
1265
1173
|
return tune;
|
|
1266
|
-
}
|
|
1174
|
+
}
|
|
1267
1175
|
|
|
1176
|
+
renderOne(div, tune, params, tuneNumber, 0);
|
|
1268
1177
|
if (removeDiv) div.parentNode.removeChild(div);
|
|
1269
1178
|
return null;
|
|
1270
1179
|
}
|
|
@@ -1285,7 +1194,7 @@ function doLineWrapping(div, tune, tuneNumber, abcString, params) {
|
|
|
1285
1194
|
if (warnings) tune.warnings = warnings;
|
|
1286
1195
|
}
|
|
1287
1196
|
|
|
1288
|
-
|
|
1197
|
+
renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
|
|
1289
1198
|
tune.explanation = ret.explanation;
|
|
1290
1199
|
return tune;
|
|
1291
1200
|
}
|
|
@@ -1294,6 +1203,262 @@ module.exports = renderAbc;
|
|
|
1294
1203
|
|
|
1295
1204
|
/***/ }),
|
|
1296
1205
|
|
|
1206
|
+
/***/ "./src/const/key-accidentals.js":
|
|
1207
|
+
/*!**************************************!*\
|
|
1208
|
+
!*** ./src/const/key-accidentals.js ***!
|
|
1209
|
+
\**************************************/
|
|
1210
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
1211
|
+
|
|
1212
|
+
var _require = __webpack_require__(/*! ./relative-major */ "./src/const/relative-major.js"),
|
|
1213
|
+
relativeMajor = _require.relativeMajor;
|
|
1214
|
+
|
|
1215
|
+
var key1sharp = {
|
|
1216
|
+
acc: 'sharp',
|
|
1217
|
+
note: 'f'
|
|
1218
|
+
};
|
|
1219
|
+
var key2sharp = {
|
|
1220
|
+
acc: 'sharp',
|
|
1221
|
+
note: 'c'
|
|
1222
|
+
};
|
|
1223
|
+
var key3sharp = {
|
|
1224
|
+
acc: 'sharp',
|
|
1225
|
+
note: 'g'
|
|
1226
|
+
};
|
|
1227
|
+
var key4sharp = {
|
|
1228
|
+
acc: 'sharp',
|
|
1229
|
+
note: 'd'
|
|
1230
|
+
};
|
|
1231
|
+
var key5sharp = {
|
|
1232
|
+
acc: 'sharp',
|
|
1233
|
+
note: 'A'
|
|
1234
|
+
};
|
|
1235
|
+
var key6sharp = {
|
|
1236
|
+
acc: 'sharp',
|
|
1237
|
+
note: 'e'
|
|
1238
|
+
};
|
|
1239
|
+
var key7sharp = {
|
|
1240
|
+
acc: 'sharp',
|
|
1241
|
+
note: 'B'
|
|
1242
|
+
};
|
|
1243
|
+
var key1flat = {
|
|
1244
|
+
acc: 'flat',
|
|
1245
|
+
note: 'B'
|
|
1246
|
+
};
|
|
1247
|
+
var key2flat = {
|
|
1248
|
+
acc: 'flat',
|
|
1249
|
+
note: 'e'
|
|
1250
|
+
};
|
|
1251
|
+
var key3flat = {
|
|
1252
|
+
acc: 'flat',
|
|
1253
|
+
note: 'A'
|
|
1254
|
+
};
|
|
1255
|
+
var key4flat = {
|
|
1256
|
+
acc: 'flat',
|
|
1257
|
+
note: 'd'
|
|
1258
|
+
};
|
|
1259
|
+
var key5flat = {
|
|
1260
|
+
acc: 'flat',
|
|
1261
|
+
note: 'G'
|
|
1262
|
+
};
|
|
1263
|
+
var key6flat = {
|
|
1264
|
+
acc: 'flat',
|
|
1265
|
+
note: 'c'
|
|
1266
|
+
};
|
|
1267
|
+
var key7flat = {
|
|
1268
|
+
acc: 'flat',
|
|
1269
|
+
note: 'F'
|
|
1270
|
+
};
|
|
1271
|
+
var keys = {
|
|
1272
|
+
'C#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
1273
|
+
'F#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
|
|
1274
|
+
'B': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
|
|
1275
|
+
'E': [key1sharp, key2sharp, key3sharp, key4sharp],
|
|
1276
|
+
'A': [key1sharp, key2sharp, key3sharp],
|
|
1277
|
+
'D': [key1sharp, key2sharp],
|
|
1278
|
+
'G': [key1sharp],
|
|
1279
|
+
'C': [],
|
|
1280
|
+
'F': [key1flat],
|
|
1281
|
+
'Bb': [key1flat, key2flat],
|
|
1282
|
+
'Eb': [key1flat, key2flat, key3flat],
|
|
1283
|
+
'Cm': [key1flat, key2flat, key3flat],
|
|
1284
|
+
'Ab': [key1flat, key2flat, key3flat, key4flat],
|
|
1285
|
+
'Db': [key1flat, key2flat, key3flat, key4flat, key5flat],
|
|
1286
|
+
'Gb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
|
|
1287
|
+
'Cb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
|
|
1288
|
+
// The following are not in the 2.0 spec, but seem normal enough.
|
|
1289
|
+
// TODO-PER: These SOUND the same as what's written, but they aren't right
|
|
1290
|
+
'A#': [key1flat, key2flat],
|
|
1291
|
+
'B#': [],
|
|
1292
|
+
'D#': [key1flat, key2flat, key3flat],
|
|
1293
|
+
'E#': [key1flat],
|
|
1294
|
+
'G#': [key1flat, key2flat, key3flat, key4flat],
|
|
1295
|
+
'none': []
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1298
|
+
function keyAccidentals(key) {
|
|
1299
|
+
var newKey = keys[relativeMajor(key)];
|
|
1300
|
+
if (!newKey) // If we don't recognize the key then there is no change
|
|
1301
|
+
return null;
|
|
1302
|
+
return JSON.parse(JSON.stringify(newKey));
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
;
|
|
1306
|
+
module.exports = keyAccidentals;
|
|
1307
|
+
|
|
1308
|
+
/***/ }),
|
|
1309
|
+
|
|
1310
|
+
/***/ "./src/const/relative-major.js":
|
|
1311
|
+
/*!*************************************!*\
|
|
1312
|
+
!*** ./src/const/relative-major.js ***!
|
|
1313
|
+
\*************************************/
|
|
1314
|
+
/***/ (function(module) {
|
|
1315
|
+
|
|
1316
|
+
// All these keys have the same number of accidentals
|
|
1317
|
+
var keys = {
|
|
1318
|
+
'C': {
|
|
1319
|
+
modes: ['CMaj', 'Amin', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'],
|
|
1320
|
+
stepsFromC: 0
|
|
1321
|
+
},
|
|
1322
|
+
'Db': {
|
|
1323
|
+
modes: ['DbMaj', 'Bbmin', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'],
|
|
1324
|
+
stepsFromC: 1
|
|
1325
|
+
},
|
|
1326
|
+
'D': {
|
|
1327
|
+
modes: ['DMaj', 'Bmin', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'],
|
|
1328
|
+
stepsFromC: 2
|
|
1329
|
+
},
|
|
1330
|
+
'Eb': {
|
|
1331
|
+
modes: ['EbMaj', 'Cmin', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'],
|
|
1332
|
+
stepsFromC: 3
|
|
1333
|
+
},
|
|
1334
|
+
'E': {
|
|
1335
|
+
modes: ['EMaj', 'C#min', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'],
|
|
1336
|
+
stepsFromC: 4
|
|
1337
|
+
},
|
|
1338
|
+
'F': {
|
|
1339
|
+
modes: ['FMaj', 'Dmin', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'],
|
|
1340
|
+
stepsFromC: 5
|
|
1341
|
+
},
|
|
1342
|
+
'Gb': {
|
|
1343
|
+
modes: ['GbMaj', 'Ebmin', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'],
|
|
1344
|
+
stepsFromC: 6
|
|
1345
|
+
},
|
|
1346
|
+
'G': {
|
|
1347
|
+
modes: ['GMaj', 'Emin', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'],
|
|
1348
|
+
stepsFromC: 7
|
|
1349
|
+
},
|
|
1350
|
+
'Ab': {
|
|
1351
|
+
modes: ['AbMaj', 'Fmin', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'],
|
|
1352
|
+
stepsFromC: 8
|
|
1353
|
+
},
|
|
1354
|
+
'A': {
|
|
1355
|
+
modes: ['AMaj', 'F#min', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'],
|
|
1356
|
+
stepsFromC: 9
|
|
1357
|
+
},
|
|
1358
|
+
'Bb': {
|
|
1359
|
+
modes: ['BbMaj', 'Gmin', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'],
|
|
1360
|
+
stepsFromC: 10
|
|
1361
|
+
},
|
|
1362
|
+
'B': {
|
|
1363
|
+
modes: ['BMaj', 'G#min', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'],
|
|
1364
|
+
stepsFromC: 11
|
|
1365
|
+
},
|
|
1366
|
+
// Enharmonic keys
|
|
1367
|
+
'C#': {
|
|
1368
|
+
modes: ['C#Maj', 'A#min', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'],
|
|
1369
|
+
stepsFromC: 1
|
|
1370
|
+
},
|
|
1371
|
+
'F#': {
|
|
1372
|
+
modes: ['F#Maj', 'D#min', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'],
|
|
1373
|
+
stepsFromC: 6
|
|
1374
|
+
},
|
|
1375
|
+
'Cb': {
|
|
1376
|
+
modes: ['CbMaj', 'Abmin', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'],
|
|
1377
|
+
stepsFromC: 11
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
var keyReverse = null;
|
|
1381
|
+
|
|
1382
|
+
function createKeyReverse() {
|
|
1383
|
+
keyReverse = {};
|
|
1384
|
+
var allKeys = Object.keys(keys);
|
|
1385
|
+
|
|
1386
|
+
for (var i = 0; i < allKeys.length; i++) {
|
|
1387
|
+
var keyObj = keys[allKeys[i]];
|
|
1388
|
+
keyReverse[allKeys[i].toLowerCase()] = allKeys[i];
|
|
1389
|
+
|
|
1390
|
+
for (var j = 0; j < keyObj.modes.length; j++) {
|
|
1391
|
+
var mode = keyObj.modes[j].toLowerCase();
|
|
1392
|
+
keyReverse[mode] = allKeys[i];
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function relativeMajor(key) {
|
|
1398
|
+
// Translate a key to its relative major. If it doesn't exist, do the best we can
|
|
1399
|
+
// by just returning the original key.
|
|
1400
|
+
// There are alternate spellings of these - so the search needs to be case insensitive.
|
|
1401
|
+
// To make this efficient, the first time this is called the "keys" object is reversed so this search is fast in the future
|
|
1402
|
+
if (!keyReverse) {
|
|
1403
|
+
createKeyReverse();
|
|
1404
|
+
} // get the key portion itself - there might be other stuff, like extra sharps and flats, or the mode written out.
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|min|mix|dor|phr|lyd|loc|m)?/);
|
|
1408
|
+
if (!mode || !mode[2]) return key;
|
|
1409
|
+
mode = mode[1] + mode[2];
|
|
1410
|
+
var maj = keyReverse[mode];
|
|
1411
|
+
if (maj) return maj;
|
|
1412
|
+
return key;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
function relativeMode(majorKey, mode) {
|
|
1416
|
+
// The reverse of the relativeMajor. Translate it back to the original mode.
|
|
1417
|
+
// If it isn't a recognized mode or it is already major, then just return the major key.
|
|
1418
|
+
var group = keys[majorKey];
|
|
1419
|
+
if (!group) return majorKey;
|
|
1420
|
+
if (mode === '') return majorKey;
|
|
1421
|
+
var match = mode.toLowerCase().match(/^(maj|min|mix|dor|phr|lyd|loc|m)/);
|
|
1422
|
+
if (!match) return majorKey;
|
|
1423
|
+
var regMode = match[1];
|
|
1424
|
+
|
|
1425
|
+
for (var i = 0; i < group.modes.length; i++) {
|
|
1426
|
+
var thisMode = group.modes[i];
|
|
1427
|
+
var ind = thisMode.toLowerCase().indexOf(regMode);
|
|
1428
|
+
if (ind !== -1 && ind === thisMode.length - regMode.length) return thisMode.substring(0, thisMode.length - regMode.length);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
return majorKey;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
function transposeKey(key, steps) {
|
|
1435
|
+
// This takes a major key and adds the desired steps.
|
|
1436
|
+
// It assigns each key a number that is the number of steps from C so that there can just be arithmetic.
|
|
1437
|
+
var match = keys[key];
|
|
1438
|
+
if (!match) return key;
|
|
1439
|
+
|
|
1440
|
+
while (steps < 0) {
|
|
1441
|
+
steps += 12;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
var fromC = (match.stepsFromC + steps) % 12;
|
|
1445
|
+
|
|
1446
|
+
for (var i = 0; i < Object.keys(keys).length; i++) {
|
|
1447
|
+
var k = Object.keys(keys)[i];
|
|
1448
|
+
if (keys[k].stepsFromC === fromC) return k;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
return key;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
module.exports = {
|
|
1455
|
+
relativeMajor: relativeMajor,
|
|
1456
|
+
relativeMode: relativeMode,
|
|
1457
|
+
transposeKey: transposeKey
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1460
|
+
/***/ }),
|
|
1461
|
+
|
|
1297
1462
|
/***/ "./src/data/abc_tune.js":
|
|
1298
1463
|
/*!******************************!*\
|
|
1299
1464
|
!*** ./src/data/abc_tune.js ***!
|
|
@@ -3232,6 +3397,7 @@ var Parse = function Parse() {
|
|
|
3232
3397
|
|
|
3233
3398
|
var addWord = function addWord(i) {
|
|
3234
3399
|
var word = parseCommon.strip(words.substring(last_divider, i));
|
|
3400
|
+
word = word.replace(/\\([-_*|~])/g, '$1');
|
|
3235
3401
|
last_divider = i + 1;
|
|
3236
3402
|
|
|
3237
3403
|
if (word.length > 0) {
|
|
@@ -3249,15 +3415,17 @@ var Parse = function Parse() {
|
|
|
3249
3415
|
return false;
|
|
3250
3416
|
};
|
|
3251
3417
|
|
|
3418
|
+
var escNext = false;
|
|
3419
|
+
|
|
3252
3420
|
for (var i = 0; i < words.length; i++) {
|
|
3253
|
-
switch (words
|
|
3421
|
+
switch (words[i]) {
|
|
3254
3422
|
case ' ':
|
|
3255
3423
|
case '\x12':
|
|
3256
3424
|
addWord(i);
|
|
3257
3425
|
break;
|
|
3258
3426
|
|
|
3259
3427
|
case '-':
|
|
3260
|
-
if (!addWord(i) && word_list.length > 0) {
|
|
3428
|
+
if (!escNext && !addWord(i) && word_list.length > 0) {
|
|
3261
3429
|
parseCommon.last(word_list).divider = '-';
|
|
3262
3430
|
word_list.push({
|
|
3263
3431
|
skip: true,
|
|
@@ -3268,33 +3436,47 @@ var Parse = function Parse() {
|
|
|
3268
3436
|
break;
|
|
3269
3437
|
|
|
3270
3438
|
case '_':
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3439
|
+
if (!escNext) {
|
|
3440
|
+
addWord(i);
|
|
3441
|
+
word_list.push({
|
|
3442
|
+
skip: true,
|
|
3443
|
+
to: 'slur'
|
|
3444
|
+
});
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3276
3447
|
break;
|
|
3277
3448
|
|
|
3278
3449
|
case '*':
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3450
|
+
if (!escNext) {
|
|
3451
|
+
addWord(i);
|
|
3452
|
+
word_list.push({
|
|
3453
|
+
skip: true,
|
|
3454
|
+
to: 'next'
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3284
3458
|
break;
|
|
3285
3459
|
|
|
3286
3460
|
case '|':
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3461
|
+
if (!escNext) {
|
|
3462
|
+
addWord(i);
|
|
3463
|
+
word_list.push({
|
|
3464
|
+
skip: true,
|
|
3465
|
+
to: 'bar'
|
|
3466
|
+
});
|
|
3467
|
+
}
|
|
3468
|
+
|
|
3292
3469
|
break;
|
|
3293
3470
|
|
|
3294
3471
|
case '~':
|
|
3295
|
-
|
|
3472
|
+
if (!escNext) {
|
|
3473
|
+
replace = true;
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3296
3476
|
break;
|
|
3297
3477
|
}
|
|
3478
|
+
|
|
3479
|
+
escNext = words[i] === '\\';
|
|
3298
3480
|
}
|
|
3299
3481
|
|
|
3300
3482
|
var inSlur = false;
|
|
@@ -3678,15 +3860,16 @@ var bookParser = function bookParser(book) {
|
|
|
3678
3860
|
"use strict";
|
|
3679
3861
|
|
|
3680
3862
|
var directives = "";
|
|
3863
|
+
var initialWhiteSpace = book.match(/(\s*)/);
|
|
3681
3864
|
book = parseCommon.strip(book);
|
|
3682
3865
|
var tuneStrings = book.split("\nX:"); // Put back the X: that we lost when splitting the tunes.
|
|
3683
3866
|
|
|
3684
3867
|
for (var i = 1; i < tuneStrings.length; i++) {
|
|
3685
3868
|
tuneStrings[i] = "X:" + tuneStrings[i];
|
|
3686
|
-
} // Keep track of the character position each tune starts with.
|
|
3869
|
+
} // Keep track of the character position each tune starts with. If the string starts with white space, count that, too.
|
|
3687
3870
|
|
|
3688
3871
|
|
|
3689
|
-
var pos = 0;
|
|
3872
|
+
var pos = initialWhiteSpace ? initialWhiteSpace[0].length : 0;
|
|
3690
3873
|
var tunes = [];
|
|
3691
3874
|
parseCommon.each(tuneStrings, function (tune) {
|
|
3692
3875
|
tunes.push({
|
|
@@ -5979,179 +6162,7 @@ var parseKeyVoice = {};
|
|
|
5979
6162
|
};
|
|
5980
6163
|
|
|
5981
6164
|
parseKeyVoice.standardKey = function (keyName, root, acc, localTranspose) {
|
|
5982
|
-
|
|
5983
|
-
acc: 'sharp',
|
|
5984
|
-
note: 'f'
|
|
5985
|
-
};
|
|
5986
|
-
var key2sharp = {
|
|
5987
|
-
acc: 'sharp',
|
|
5988
|
-
note: 'c'
|
|
5989
|
-
};
|
|
5990
|
-
var key3sharp = {
|
|
5991
|
-
acc: 'sharp',
|
|
5992
|
-
note: 'g'
|
|
5993
|
-
};
|
|
5994
|
-
var key4sharp = {
|
|
5995
|
-
acc: 'sharp',
|
|
5996
|
-
note: 'd'
|
|
5997
|
-
};
|
|
5998
|
-
var key5sharp = {
|
|
5999
|
-
acc: 'sharp',
|
|
6000
|
-
note: 'A'
|
|
6001
|
-
};
|
|
6002
|
-
var key6sharp = {
|
|
6003
|
-
acc: 'sharp',
|
|
6004
|
-
note: 'e'
|
|
6005
|
-
};
|
|
6006
|
-
var key7sharp = {
|
|
6007
|
-
acc: 'sharp',
|
|
6008
|
-
note: 'B'
|
|
6009
|
-
};
|
|
6010
|
-
var key1flat = {
|
|
6011
|
-
acc: 'flat',
|
|
6012
|
-
note: 'B'
|
|
6013
|
-
};
|
|
6014
|
-
var key2flat = {
|
|
6015
|
-
acc: 'flat',
|
|
6016
|
-
note: 'e'
|
|
6017
|
-
};
|
|
6018
|
-
var key3flat = {
|
|
6019
|
-
acc: 'flat',
|
|
6020
|
-
note: 'A'
|
|
6021
|
-
};
|
|
6022
|
-
var key4flat = {
|
|
6023
|
-
acc: 'flat',
|
|
6024
|
-
note: 'd'
|
|
6025
|
-
};
|
|
6026
|
-
var key5flat = {
|
|
6027
|
-
acc: 'flat',
|
|
6028
|
-
note: 'G'
|
|
6029
|
-
};
|
|
6030
|
-
var key6flat = {
|
|
6031
|
-
acc: 'flat',
|
|
6032
|
-
note: 'c'
|
|
6033
|
-
};
|
|
6034
|
-
var key7flat = {
|
|
6035
|
-
acc: 'flat',
|
|
6036
|
-
note: 'F'
|
|
6037
|
-
};
|
|
6038
|
-
var keys = {
|
|
6039
|
-
'C#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
6040
|
-
'A#m': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
6041
|
-
'G#Mix': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
6042
|
-
'D#Dor': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
6043
|
-
'E#Phr': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
6044
|
-
'F#Lyd': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
6045
|
-
'B#Loc': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
6046
|
-
'F#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
|
|
6047
|
-
'D#m': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
|
|
6048
|
-
'C#Mix': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
|
|
6049
|
-
'G#Dor': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
|
|
6050
|
-
'A#Phr': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
|
|
6051
|
-
'BLyd': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
|
|
6052
|
-
'E#Loc': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
|
|
6053
|
-
'B': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
|
|
6054
|
-
'G#m': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
|
|
6055
|
-
'F#Mix': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
|
|
6056
|
-
'C#Dor': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
|
|
6057
|
-
'D#Phr': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
|
|
6058
|
-
'ELyd': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
|
|
6059
|
-
'A#Loc': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
|
|
6060
|
-
'E': [key1sharp, key2sharp, key3sharp, key4sharp],
|
|
6061
|
-
'C#m': [key1sharp, key2sharp, key3sharp, key4sharp],
|
|
6062
|
-
'BMix': [key1sharp, key2sharp, key3sharp, key4sharp],
|
|
6063
|
-
'F#Dor': [key1sharp, key2sharp, key3sharp, key4sharp],
|
|
6064
|
-
'G#Phr': [key1sharp, key2sharp, key3sharp, key4sharp],
|
|
6065
|
-
'ALyd': [key1sharp, key2sharp, key3sharp, key4sharp],
|
|
6066
|
-
'D#Loc': [key1sharp, key2sharp, key3sharp, key4sharp],
|
|
6067
|
-
'A': [key1sharp, key2sharp, key3sharp],
|
|
6068
|
-
'F#m': [key1sharp, key2sharp, key3sharp],
|
|
6069
|
-
'EMix': [key1sharp, key2sharp, key3sharp],
|
|
6070
|
-
'BDor': [key1sharp, key2sharp, key3sharp],
|
|
6071
|
-
'C#Phr': [key1sharp, key2sharp, key3sharp],
|
|
6072
|
-
'DLyd': [key1sharp, key2sharp, key3sharp],
|
|
6073
|
-
'G#Loc': [key1sharp, key2sharp, key3sharp],
|
|
6074
|
-
'D': [key1sharp, key2sharp],
|
|
6075
|
-
'Bm': [key1sharp, key2sharp],
|
|
6076
|
-
'AMix': [key1sharp, key2sharp],
|
|
6077
|
-
'EDor': [key1sharp, key2sharp],
|
|
6078
|
-
'F#Phr': [key1sharp, key2sharp],
|
|
6079
|
-
'GLyd': [key1sharp, key2sharp],
|
|
6080
|
-
'C#Loc': [key1sharp, key2sharp],
|
|
6081
|
-
'G': [key1sharp],
|
|
6082
|
-
'Em': [key1sharp],
|
|
6083
|
-
'DMix': [key1sharp],
|
|
6084
|
-
'ADor': [key1sharp],
|
|
6085
|
-
'BPhr': [key1sharp],
|
|
6086
|
-
'CLyd': [key1sharp],
|
|
6087
|
-
'F#Loc': [key1sharp],
|
|
6088
|
-
'C': [],
|
|
6089
|
-
'Am': [],
|
|
6090
|
-
'GMix': [],
|
|
6091
|
-
'DDor': [],
|
|
6092
|
-
'EPhr': [],
|
|
6093
|
-
'FLyd': [],
|
|
6094
|
-
'BLoc': [],
|
|
6095
|
-
'F': [key1flat],
|
|
6096
|
-
'Dm': [key1flat],
|
|
6097
|
-
'CMix': [key1flat],
|
|
6098
|
-
'GDor': [key1flat],
|
|
6099
|
-
'APhr': [key1flat],
|
|
6100
|
-
'BbLyd': [key1flat],
|
|
6101
|
-
'ELoc': [key1flat],
|
|
6102
|
-
'Bb': [key1flat, key2flat],
|
|
6103
|
-
'Gm': [key1flat, key2flat],
|
|
6104
|
-
'FMix': [key1flat, key2flat],
|
|
6105
|
-
'CDor': [key1flat, key2flat],
|
|
6106
|
-
'DPhr': [key1flat, key2flat],
|
|
6107
|
-
'EbLyd': [key1flat, key2flat],
|
|
6108
|
-
'ALoc': [key1flat, key2flat],
|
|
6109
|
-
'Eb': [key1flat, key2flat, key3flat],
|
|
6110
|
-
'Cm': [key1flat, key2flat, key3flat],
|
|
6111
|
-
'BbMix': [key1flat, key2flat, key3flat],
|
|
6112
|
-
'FDor': [key1flat, key2flat, key3flat],
|
|
6113
|
-
'GPhr': [key1flat, key2flat, key3flat],
|
|
6114
|
-
'AbLyd': [key1flat, key2flat, key3flat],
|
|
6115
|
-
'DLoc': [key1flat, key2flat, key3flat],
|
|
6116
|
-
'Ab': [key1flat, key2flat, key3flat, key4flat],
|
|
6117
|
-
'Fm': [key1flat, key2flat, key3flat, key4flat],
|
|
6118
|
-
'EbMix': [key1flat, key2flat, key3flat, key4flat],
|
|
6119
|
-
'BbDor': [key1flat, key2flat, key3flat, key4flat],
|
|
6120
|
-
'CPhr': [key1flat, key2flat, key3flat, key4flat],
|
|
6121
|
-
'DbLyd': [key1flat, key2flat, key3flat, key4flat],
|
|
6122
|
-
'GLoc': [key1flat, key2flat, key3flat, key4flat],
|
|
6123
|
-
'Db': [key1flat, key2flat, key3flat, key4flat, key5flat],
|
|
6124
|
-
'Bbm': [key1flat, key2flat, key3flat, key4flat, key5flat],
|
|
6125
|
-
'AbMix': [key1flat, key2flat, key3flat, key4flat, key5flat],
|
|
6126
|
-
'EbDor': [key1flat, key2flat, key3flat, key4flat, key5flat],
|
|
6127
|
-
'FPhr': [key1flat, key2flat, key3flat, key4flat, key5flat],
|
|
6128
|
-
'GbLyd': [key1flat, key2flat, key3flat, key4flat, key5flat],
|
|
6129
|
-
'CLoc': [key1flat, key2flat, key3flat, key4flat, key5flat],
|
|
6130
|
-
'Gb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
|
|
6131
|
-
'Ebm': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
|
|
6132
|
-
'DbMix': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
|
|
6133
|
-
'AbDor': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
|
|
6134
|
-
'BbPhr': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
|
|
6135
|
-
'CbLyd': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
|
|
6136
|
-
'FLoc': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
|
|
6137
|
-
'Cb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
|
|
6138
|
-
'Abm': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
|
|
6139
|
-
'GbMix': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
|
|
6140
|
-
'DbDor': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
|
|
6141
|
-
'EbPhr': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
|
|
6142
|
-
'FbLyd': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
|
|
6143
|
-
'BbLoc': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
|
|
6144
|
-
// The following are not in the 2.0 spec, but seem normal enough.
|
|
6145
|
-
// TODO-PER: These SOUND the same as what's written, but they aren't right
|
|
6146
|
-
'A#': [key1flat, key2flat],
|
|
6147
|
-
'B#': [],
|
|
6148
|
-
'D#': [key1flat, key2flat, key3flat],
|
|
6149
|
-
'E#': [key1flat],
|
|
6150
|
-
'G#': [key1flat, key2flat, key3flat, key4flat],
|
|
6151
|
-
'Gbm': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
|
|
6152
|
-
'none': []
|
|
6153
|
-
};
|
|
6154
|
-
return transpose.keySignature(multilineVars, keys, keyName, root, acc, localTranspose);
|
|
6165
|
+
return transpose.keySignature(multilineVars, keyName, root, acc, localTranspose);
|
|
6155
6166
|
};
|
|
6156
6167
|
|
|
6157
6168
|
var clefLines = {
|
|
@@ -7546,7 +7557,7 @@ MusicParser.prototype.parseMusic = function (line) {
|
|
|
7546
7557
|
}
|
|
7547
7558
|
|
|
7548
7559
|
multilineVars.addFormattingOptions(el, tune.formatting, 'bar');
|
|
7549
|
-
tuneBuilder.appendElement('bar', startOfLine +
|
|
7560
|
+
tuneBuilder.appendElement('bar', startOfLine + startI, startOfLine + i + ret[0], bar);
|
|
7550
7561
|
multilineVars.measureNotEmpty = false;
|
|
7551
7562
|
el = {};
|
|
7552
7563
|
}
|
|
@@ -7985,7 +7996,7 @@ function durationOfMeasure(multilineVars) {
|
|
|
7985
7996
|
|
|
7986
7997
|
var legalAccents = ["trill", "lowermordent", "uppermordent", "mordent", "pralltriller", "accent", "fermata", "invertedfermata", "tenuto", "0", "1", "2", "3", "4", "5", "+", "wedge", "open", "thumb", "snap", "turn", "roll", "breath", "shortphrase", "mediumphrase", "longphrase", "segno", "coda", "D.S.", "D.C.", "fine", "beambr1", "beambr2", "slide", "marcato", "upbow", "downbow", "/", "//", "///", "////", "trem1", "trem2", "trem3", "trem4", "turnx", "invertedturn", "invertedturnx", "trill(", "trill)", "arpeggio", "xstem", "mark", "umarcato", "style=normal", "style=harmonic", "style=rhythm", "style=x", "style=triangle"];
|
|
7987
7998
|
var volumeDecorations = ["p", "pp", "f", "ff", "mf", "mp", "ppp", "pppp", "fff", "ffff", "sfz"];
|
|
7988
|
-
var dynamicDecorations = ["crescendo(", "crescendo)", "diminuendo(", "diminuendo)"];
|
|
7999
|
+
var dynamicDecorations = ["crescendo(", "crescendo)", "diminuendo(", "diminuendo)", "glissando(", "glissando)"];
|
|
7989
8000
|
var accentPseudonyms = [["<", "accent"], [">", "accent"], ["tr", "trill"], ["plus", "+"], ["emphasis", "accent"], ["^", "umarcato"], ["marcato", "umarcato"]];
|
|
7990
8001
|
var accentDynamicPseudonyms = [["<(", "crescendo("], ["<)", "crescendo)"], [">(", "diminuendo("], [">)", "diminuendo)"]];
|
|
7991
8002
|
|
|
@@ -10135,6 +10146,10 @@ module.exports = Tokenizer;
|
|
|
10135
10146
|
// abc_transpose.js: Handles the automatic transposition of key signatures, chord symbols, and notes.
|
|
10136
10147
|
var allNotes = __webpack_require__(/*! ./all-notes */ "./src/parse/all-notes.js");
|
|
10137
10148
|
|
|
10149
|
+
var transposeChordName = __webpack_require__(/*! ../parse/transpose-chord */ "./src/parse/transpose-chord.js");
|
|
10150
|
+
|
|
10151
|
+
var keyAccidentals = __webpack_require__(/*! ../const/key-accidentals */ "./src/const/key-accidentals.js");
|
|
10152
|
+
|
|
10138
10153
|
var transpose = {};
|
|
10139
10154
|
var keyIndex = {
|
|
10140
10155
|
'C': 0,
|
|
@@ -10158,16 +10173,16 @@ var keyIndex = {
|
|
|
10158
10173
|
var newKey = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B'];
|
|
10159
10174
|
var newKeyMinor = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'Bb', 'B'];
|
|
10160
10175
|
|
|
10161
|
-
transpose.keySignature = function (multilineVars,
|
|
10162
|
-
if (multilineVars.clef.type === "perc") return {
|
|
10163
|
-
accidentals:
|
|
10176
|
+
transpose.keySignature = function (multilineVars, keyName, root, acc, localTranspose) {
|
|
10177
|
+
if (multilineVars.clef.type === "perc" || multilineVars.clef.type === "none") return {
|
|
10178
|
+
accidentals: keyAccidentals(keyName),
|
|
10164
10179
|
root: root,
|
|
10165
10180
|
acc: acc
|
|
10166
10181
|
};
|
|
10167
10182
|
if (!localTranspose) localTranspose = 0;
|
|
10168
10183
|
multilineVars.localTransposeVerticalMovement = 0;
|
|
10169
10184
|
multilineVars.localTransposePreferFlats = false;
|
|
10170
|
-
var k =
|
|
10185
|
+
var k = keyAccidentals(keyName);
|
|
10171
10186
|
if (!k) return multilineVars.key; // If the key isn't in the list, it is non-standard. We won't attempt to transpose it.
|
|
10172
10187
|
|
|
10173
10188
|
multilineVars.localTranspose = (multilineVars.globalTranspose ? multilineVars.globalTranspose : 0) + localTranspose;
|
|
@@ -10203,7 +10218,7 @@ transpose.keySignature = function (multilineVars, keys, keyName, root, acc, loca
|
|
|
10203
10218
|
if (index > 11) index = index % 12;
|
|
10204
10219
|
var newKeyName = keyName[0] === 'm' ? newKeyMinor[index] : newKey[index];
|
|
10205
10220
|
var transposedKey = newKeyName + keyName;
|
|
10206
|
-
var newKeySig =
|
|
10221
|
+
var newKeySig = keyAccidentals(transposedKey);
|
|
10207
10222
|
if (newKeySig.length > 0 && newKeySig[0].acc === 'flat') multilineVars.localTransposePreferFlats = true;
|
|
10208
10223
|
var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
|
|
10209
10224
|
|
|
@@ -10232,76 +10247,8 @@ transpose.keySignature = function (multilineVars, keys, keyName, root, acc, loca
|
|
|
10232
10247
|
};
|
|
10233
10248
|
};
|
|
10234
10249
|
|
|
10235
|
-
var sharpChords = ['C', 'C♯', 'D', "D♯", 'E', 'F', "F♯", 'G', 'G♯', 'A', 'A♯', 'B'];
|
|
10236
|
-
var flatChords = ['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B'];
|
|
10237
|
-
var sharpChordsFree = ['C', 'C#', 'D', "D#", 'E', 'F', "F#", 'G', 'G#', 'A', 'A#', 'B'];
|
|
10238
|
-
var flatChordsFree = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
|
|
10239
|
-
|
|
10240
10250
|
transpose.chordName = function (multilineVars, chord) {
|
|
10241
|
-
|
|
10242
|
-
// The chords are the same if it is an exact octave change.
|
|
10243
|
-
var transposeFactor = multilineVars.localTranspose;
|
|
10244
|
-
|
|
10245
|
-
while (transposeFactor < 0) {
|
|
10246
|
-
transposeFactor += 12;
|
|
10247
|
-
}
|
|
10248
|
-
|
|
10249
|
-
if (transposeFactor > 11) transposeFactor = transposeFactor % 12;
|
|
10250
|
-
|
|
10251
|
-
if (multilineVars.freegchord) {
|
|
10252
|
-
chord = chord.replace(/Cb/g, "`~11`");
|
|
10253
|
-
chord = chord.replace(/Db/g, "`~1`");
|
|
10254
|
-
chord = chord.replace(/Eb/g, "`~3`");
|
|
10255
|
-
chord = chord.replace(/Fb/g, "`~4`");
|
|
10256
|
-
chord = chord.replace(/Gb/g, "`~6`");
|
|
10257
|
-
chord = chord.replace(/Ab/g, "`~8`");
|
|
10258
|
-
chord = chord.replace(/Bb/g, "`~10`");
|
|
10259
|
-
chord = chord.replace(/C#/g, "`~1`");
|
|
10260
|
-
chord = chord.replace(/D#/g, "`~3`");
|
|
10261
|
-
chord = chord.replace(/E#/g, "`~5`");
|
|
10262
|
-
chord = chord.replace(/F#/g, "`~6`");
|
|
10263
|
-
chord = chord.replace(/G#/g, "`~8`");
|
|
10264
|
-
chord = chord.replace(/A#/g, "`~10`");
|
|
10265
|
-
chord = chord.replace(/B#/g, "`~0`");
|
|
10266
|
-
} else {
|
|
10267
|
-
chord = chord.replace(/C♭/g, "`~11`");
|
|
10268
|
-
chord = chord.replace(/D♭/g, "`~1`");
|
|
10269
|
-
chord = chord.replace(/E♭/g, "`~3`");
|
|
10270
|
-
chord = chord.replace(/F♭/g, "`~4`");
|
|
10271
|
-
chord = chord.replace(/G♭/g, "`~6`");
|
|
10272
|
-
chord = chord.replace(/A♭/g, "`~8`");
|
|
10273
|
-
chord = chord.replace(/B♭/g, "`~10`");
|
|
10274
|
-
chord = chord.replace(/C♯/g, "`~1`");
|
|
10275
|
-
chord = chord.replace(/D♯/g, "`~3`");
|
|
10276
|
-
chord = chord.replace(/E♯/g, "`~5`");
|
|
10277
|
-
chord = chord.replace(/F♯/g, "`~6`");
|
|
10278
|
-
chord = chord.replace(/G♯/g, "`~8`");
|
|
10279
|
-
chord = chord.replace(/A♯/g, "`~10`");
|
|
10280
|
-
chord = chord.replace(/B♯/g, "`~0`");
|
|
10281
|
-
}
|
|
10282
|
-
|
|
10283
|
-
chord = chord.replace(/C/g, "`~0`");
|
|
10284
|
-
chord = chord.replace(/D/g, "`~2`");
|
|
10285
|
-
chord = chord.replace(/E/g, "`~4`");
|
|
10286
|
-
chord = chord.replace(/F/g, "`~5`");
|
|
10287
|
-
chord = chord.replace(/G/g, "`~7`");
|
|
10288
|
-
chord = chord.replace(/A/g, "`~9`");
|
|
10289
|
-
chord = chord.replace(/B/g, "`~11`");
|
|
10290
|
-
var arr = chord.split("`");
|
|
10291
|
-
|
|
10292
|
-
for (var i = 0; i < arr.length; i++) {
|
|
10293
|
-
if (arr[i][0] === '~') {
|
|
10294
|
-
var chordNum = parseInt(arr[i].substr(1), 10);
|
|
10295
|
-
chordNum += transposeFactor;
|
|
10296
|
-
if (chordNum > 11) chordNum -= 12;
|
|
10297
|
-
if (multilineVars.freegchord) arr[i] = multilineVars.localTransposePreferFlats ? flatChordsFree[chordNum] : sharpChordsFree[chordNum];else arr[i] = multilineVars.localTransposePreferFlats ? flatChords[chordNum] : sharpChords[chordNum];
|
|
10298
|
-
}
|
|
10299
|
-
}
|
|
10300
|
-
|
|
10301
|
-
chord = arr.join("");
|
|
10302
|
-
}
|
|
10303
|
-
|
|
10304
|
-
return chord;
|
|
10251
|
+
return transposeChordName(chord, multilineVars.localTranspose, multilineVars.localTransposePreferFlats, multilineVars.freegchord);
|
|
10305
10252
|
};
|
|
10306
10253
|
|
|
10307
10254
|
var pitchToLetter = ['c', 'd', 'e', 'f', 'g', 'a', 'b'];
|
|
@@ -10418,6 +10365,83 @@ module.exports = allNotes;
|
|
|
10418
10365
|
|
|
10419
10366
|
/***/ }),
|
|
10420
10367
|
|
|
10368
|
+
/***/ "./src/parse/transpose-chord.js":
|
|
10369
|
+
/*!**************************************!*\
|
|
10370
|
+
!*** ./src/parse/transpose-chord.js ***!
|
|
10371
|
+
\**************************************/
|
|
10372
|
+
/***/ (function(module) {
|
|
10373
|
+
|
|
10374
|
+
var sharpChords = ['C', 'C♯', 'D', "D♯", 'E', 'F', "F♯", 'G', 'G♯', 'A', 'A♯', 'B'];
|
|
10375
|
+
var flatChords = ['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B'];
|
|
10376
|
+
var sharpChordsFree = ['C', 'C#', 'D', "D#", 'E', 'F', "F#", 'G', 'G#', 'A', 'A#', 'B'];
|
|
10377
|
+
var flatChordsFree = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
|
|
10378
|
+
|
|
10379
|
+
function transposeChordName(chord, steps, preferFlats, freeGCchord) {
|
|
10380
|
+
if (!steps || steps % 12 === 0) // The chords are the same if it is an exact octave change.
|
|
10381
|
+
return chord; // There are two things in the chord that might need to be transposed:
|
|
10382
|
+
// The chord will start with a letter from A-G, and might have one accidental after it.
|
|
10383
|
+
// That accidental might be an actual sharp or flat char, or it might be a pound sign or lower case "b".
|
|
10384
|
+
// Then there is a bunch of stuff that isn't transposed and should just be copied. That is stuff like "7" and more complicated chords.
|
|
10385
|
+
// But there is one other exception: right after a slash there will be a bass note and possibly an accidental. That should also be transposed.
|
|
10386
|
+
|
|
10387
|
+
while (steps < 0) {
|
|
10388
|
+
steps += 12;
|
|
10389
|
+
}
|
|
10390
|
+
|
|
10391
|
+
if (steps > 11) steps = steps % 12; // (chord name w/accidental) (a bunch of stuff) (/) (bass note) (anything else)
|
|
10392
|
+
|
|
10393
|
+
var match = chord.match(/^([A-G][b#♭♯]?)([^\/]+)?\/?([A-G][b#♭♯]?)?(.+)?/);
|
|
10394
|
+
if (!match) return chord; // We don't recognize the format of the chord, so skip it.
|
|
10395
|
+
|
|
10396
|
+
var name = match[1];
|
|
10397
|
+
var extra1 = match[2];
|
|
10398
|
+
var bass = match[3];
|
|
10399
|
+
var extra2 = match[4];
|
|
10400
|
+
var index = sharpChords.indexOf(name);
|
|
10401
|
+
if (index < 0) index = flatChords.indexOf(name);
|
|
10402
|
+
if (index < 0) index = sharpChordsFree.indexOf(name);
|
|
10403
|
+
if (index < 0) index = flatChordsFree.indexOf(name);
|
|
10404
|
+
if (index < 0) return chord; // This should never happen, but if we can't find the chord just bail.
|
|
10405
|
+
|
|
10406
|
+
index += steps;
|
|
10407
|
+
index = index % 12;
|
|
10408
|
+
|
|
10409
|
+
if (preferFlats) {
|
|
10410
|
+
if (freeGCchord) chord = flatChordsFree[index];else chord = flatChords[index];
|
|
10411
|
+
} else {
|
|
10412
|
+
if (freeGCchord) chord = sharpChordsFree[index];else chord = sharpChords[index];
|
|
10413
|
+
}
|
|
10414
|
+
|
|
10415
|
+
if (extra1) chord += extra1;
|
|
10416
|
+
|
|
10417
|
+
if (bass) {
|
|
10418
|
+
var index = sharpChords.indexOf(bass);
|
|
10419
|
+
if (index < 0) index = flatChords.indexOf(bass);
|
|
10420
|
+
if (index < 0) index = sharpChordsFree.indexOf(bass);
|
|
10421
|
+
if (index < 0) index = flatChordsFree.indexOf(bass);
|
|
10422
|
+
chord += '/';
|
|
10423
|
+
|
|
10424
|
+
if (index >= 0) {
|
|
10425
|
+
index += steps;
|
|
10426
|
+
index = index % 12;
|
|
10427
|
+
|
|
10428
|
+
if (preferFlats) {
|
|
10429
|
+
if (freeGCchord) chord += flatChordsFree[index];else chord += flatChords[index];
|
|
10430
|
+
} else {
|
|
10431
|
+
if (freeGCchord) chord += sharpChordsFree[index];else chord += sharpChords[index];
|
|
10432
|
+
}
|
|
10433
|
+
} else chord += bass; // Don't know what to do so do nothing
|
|
10434
|
+
|
|
10435
|
+
}
|
|
10436
|
+
|
|
10437
|
+
if (extra2) chord += extra2;
|
|
10438
|
+
return chord;
|
|
10439
|
+
}
|
|
10440
|
+
|
|
10441
|
+
module.exports = transposeChordName;
|
|
10442
|
+
|
|
10443
|
+
/***/ }),
|
|
10444
|
+
|
|
10421
10445
|
/***/ "./src/parse/tune-builder.js":
|
|
10422
10446
|
/*!***********************************!*\
|
|
10423
10447
|
!*** ./src/parse/tune-builder.js ***!
|
|
@@ -11890,107 +11914,676 @@ function getRevisedTuneParams(lineBreaks, staffWidth, params) {
|
|
|
11890
11914
|
}
|
|
11891
11915
|
}
|
|
11892
11916
|
|
|
11893
|
-
return {
|
|
11894
|
-
revisedParams: revisedParams
|
|
11895
|
-
};
|
|
11896
|
-
}
|
|
11917
|
+
return {
|
|
11918
|
+
revisedParams: revisedParams
|
|
11919
|
+
};
|
|
11920
|
+
}
|
|
11921
|
+
|
|
11922
|
+
function calcLineWraps(tune, widths, params) {
|
|
11923
|
+
// For calculating how much can go on the line, it depends on the width of the line. It is a convenience to just divide it here
|
|
11924
|
+
// by the minimum spacing instead of multiplying the min spacing later.
|
|
11925
|
+
// The scaling works differently: this is done by changing the scaling of the outer SVG, so the scaling needs to be compensated
|
|
11926
|
+
// for here, because the actual width will be different from the calculated numbers.
|
|
11927
|
+
// If the desired width is less than the margin, just punt and return the original tune
|
|
11928
|
+
//console.log(widths)
|
|
11929
|
+
if (widths.length === 0 || params.staffwidth < widths[0].left) {
|
|
11930
|
+
return {
|
|
11931
|
+
reParse: false,
|
|
11932
|
+
explanation: "Staff width is narrower than the margin",
|
|
11933
|
+
revisedParams: params
|
|
11934
|
+
};
|
|
11935
|
+
}
|
|
11936
|
+
|
|
11937
|
+
var scale = params.scale ? Math.max(params.scale, 0.1) : 1;
|
|
11938
|
+
var minSpacing = params.wrap.minSpacing ? Math.max(parseFloat(params.wrap.minSpacing), 1) : 1;
|
|
11939
|
+
var minSpacingLimit = params.wrap.minSpacingLimit ? Math.max(parseFloat(params.wrap.minSpacingLimit), 1) : minSpacing - 0.1;
|
|
11940
|
+
var maxSpacing = params.wrap.maxSpacing ? Math.max(parseFloat(params.wrap.maxSpacing), 1) : undefined;
|
|
11941
|
+
if (params.wrap.lastLineLimit && !maxSpacing) maxSpacing = Math.max(parseFloat(params.wrap.lastLineLimit), 1); // var targetHeight = params.wrap.targetHeight ? Math.max(parseInt(params.wrap.targetHeight, 10), 100) : undefined;
|
|
11942
|
+
|
|
11943
|
+
var preferredMeasuresPerLine = params.wrap.preferredMeasuresPerLine ? Math.max(parseInt(params.wrap.preferredMeasuresPerLine, 10), 0) : undefined;
|
|
11944
|
+
var accumulatedLineBreaks = [];
|
|
11945
|
+
var explanations = [];
|
|
11946
|
+
|
|
11947
|
+
for (var s = 0; s < widths.length; s++) {
|
|
11948
|
+
var section = widths[s];
|
|
11949
|
+
var usableWidth = params.staffwidth - section.left;
|
|
11950
|
+
var lineBreakPoint = usableWidth / minSpacing / scale;
|
|
11951
|
+
var minLineSize = usableWidth / maxSpacing / scale;
|
|
11952
|
+
var allowableVariance = usableWidth / minSpacingLimit / scale;
|
|
11953
|
+
var explanation = {
|
|
11954
|
+
widths: section,
|
|
11955
|
+
lineBreakPoint: lineBreakPoint,
|
|
11956
|
+
minLineSize: minLineSize,
|
|
11957
|
+
attempts: [],
|
|
11958
|
+
staffWidth: params.staffwidth,
|
|
11959
|
+
minWidth: Math.round(allowableVariance)
|
|
11960
|
+
}; // If there is a preferred number of measures per line, test that first. If none of the lines is too long, then we're finished.
|
|
11961
|
+
|
|
11962
|
+
var lineBreaks = null;
|
|
11963
|
+
|
|
11964
|
+
if (preferredMeasuresPerLine) {
|
|
11965
|
+
var f = fixedMeasureLineBreaks(section.measureWidths, lineBreakPoint, preferredMeasuresPerLine);
|
|
11966
|
+
explanation.attempts.push({
|
|
11967
|
+
type: "Fixed Measures Per Line",
|
|
11968
|
+
preferredMeasuresPerLine: preferredMeasuresPerLine,
|
|
11969
|
+
lineBreaks: f.lineBreaks,
|
|
11970
|
+
failed: f.failed,
|
|
11971
|
+
totals: f.totals
|
|
11972
|
+
});
|
|
11973
|
+
if (!f.failed) lineBreaks = f.lineBreaks;
|
|
11974
|
+
} // If we don't have lineBreaks yet, use the free form method of line breaks.
|
|
11975
|
+
// This will be called either if Preferred Measures is not used, or if the music is just weird - like a single measure is way too crowded.
|
|
11976
|
+
|
|
11977
|
+
|
|
11978
|
+
if (!lineBreaks) {
|
|
11979
|
+
var ff = freeFormLineBreaks(section.measureWidths, lineBreakPoint);
|
|
11980
|
+
explanation.attempts.push({
|
|
11981
|
+
type: "Free Form",
|
|
11982
|
+
lineBreaks: ff.lineBreaks,
|
|
11983
|
+
totals: ff.totals
|
|
11984
|
+
});
|
|
11985
|
+
lineBreaks = ff.lineBreaks; // We now have an acceptable number of lines, but the measures may not be optimally distributed. See if there is a better distribution.
|
|
11986
|
+
|
|
11987
|
+
if (lineBreaks.length > 0 && section.measureWidths.length < 25) {
|
|
11988
|
+
// Only do this if everything doesn't fit on one line.
|
|
11989
|
+
// This is an intensive operation and it is optional so just do it for shorter music.
|
|
11990
|
+
ff = optimizeLineWidths(section, lineBreakPoint, lineBreaks, explanation);
|
|
11991
|
+
explanation.attempts.push({
|
|
11992
|
+
type: "Optimize",
|
|
11993
|
+
failed: ff.failed,
|
|
11994
|
+
reason: ff.reason,
|
|
11995
|
+
lineBreaks: ff.lineBreaks,
|
|
11996
|
+
totals: ff.totals
|
|
11997
|
+
});
|
|
11998
|
+
if (!ff.failed) lineBreaks = ff.lineBreaks;
|
|
11999
|
+
}
|
|
12000
|
+
}
|
|
12001
|
+
|
|
12002
|
+
accumulatedLineBreaks.push(lineBreaks);
|
|
12003
|
+
explanations.push(explanation);
|
|
12004
|
+
} // If the vertical space exceeds targetHeight, remove a line and try again. If that is too crowded, then don't use it.
|
|
12005
|
+
|
|
12006
|
+
|
|
12007
|
+
var staffWidth = params.staffwidth;
|
|
12008
|
+
var ret = getRevisedTuneParams(accumulatedLineBreaks, staffWidth, params);
|
|
12009
|
+
ret.explanation = explanations;
|
|
12010
|
+
ret.reParse = true;
|
|
12011
|
+
return ret;
|
|
12012
|
+
}
|
|
12013
|
+
|
|
12014
|
+
module.exports = {
|
|
12015
|
+
wrapLines: wrapLines,
|
|
12016
|
+
calcLineWraps: calcLineWraps
|
|
12017
|
+
};
|
|
12018
|
+
|
|
12019
|
+
/***/ }),
|
|
12020
|
+
|
|
12021
|
+
/***/ "./src/str/output.js":
|
|
12022
|
+
/*!***************************!*\
|
|
12023
|
+
!*** ./src/str/output.js ***!
|
|
12024
|
+
\***************************/
|
|
12025
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
12026
|
+
|
|
12027
|
+
var keyAccidentals = __webpack_require__(/*! ../const/key-accidentals */ "./src/const/key-accidentals.js");
|
|
12028
|
+
|
|
12029
|
+
var _require = __webpack_require__(/*! ../const/relative-major */ "./src/const/relative-major.js"),
|
|
12030
|
+
relativeMajor = _require.relativeMajor,
|
|
12031
|
+
transposeKey = _require.transposeKey,
|
|
12032
|
+
relativeMode = _require.relativeMode;
|
|
12033
|
+
|
|
12034
|
+
var transposeChordName = __webpack_require__(/*! ../parse/transpose-chord */ "./src/parse/transpose-chord.js");
|
|
12035
|
+
|
|
12036
|
+
var strTranspose;
|
|
12037
|
+
|
|
12038
|
+
(function () {
|
|
12039
|
+
"use strict";
|
|
12040
|
+
|
|
12041
|
+
strTranspose = function strTranspose(abc, abcTune, steps) {
|
|
12042
|
+
if (abcTune === "TEST") // Backdoor way to get entry points for unit tests
|
|
12043
|
+
return {
|
|
12044
|
+
keyAccidentals: keyAccidentals,
|
|
12045
|
+
relativeMajor: relativeMajor,
|
|
12046
|
+
transposeKey: transposeKey,
|
|
12047
|
+
relativeMode: relativeMode,
|
|
12048
|
+
transposeChordName: transposeChordName
|
|
12049
|
+
};
|
|
12050
|
+
steps = parseInt(steps, 10);
|
|
12051
|
+
var changes = [];
|
|
12052
|
+
var i;
|
|
12053
|
+
|
|
12054
|
+
for (i = 0; i < abcTune.length; i++) {
|
|
12055
|
+
changes = changes.concat(transposeOneTune(abc, abcTune[i], steps));
|
|
12056
|
+
} // Reverse sort so that we are replacing strings from the end to the beginning so that the indexes aren't invalidated as we go.
|
|
12057
|
+
// (Because voices can be written in different ways we can't count on the notes being encountered in the order they appear in the string.)
|
|
12058
|
+
|
|
12059
|
+
|
|
12060
|
+
changes = changes.sort(function (a, b) {
|
|
12061
|
+
return b.start - a.start;
|
|
12062
|
+
});
|
|
12063
|
+
var output = abc.split('');
|
|
12064
|
+
|
|
12065
|
+
for (i = 0; i < changes.length; i++) {
|
|
12066
|
+
var ch = changes[i];
|
|
12067
|
+
output.splice(ch.start, ch.end - ch.start, ch.note);
|
|
12068
|
+
}
|
|
12069
|
+
|
|
12070
|
+
return output.join('');
|
|
12071
|
+
};
|
|
12072
|
+
|
|
12073
|
+
function transposeOneTune(abc, abcTune, steps) {
|
|
12074
|
+
var changes = []; // Don't transpose bagpipe music - that is a special case and is always a particular key
|
|
12075
|
+
|
|
12076
|
+
var key = abcTune.getKeySignature();
|
|
12077
|
+
if (key.root === 'Hp' || key.root === "HP") return changes;
|
|
12078
|
+
changes = changes.concat(changeAllKeySigs(abc, steps));
|
|
12079
|
+
|
|
12080
|
+
for (var i = 0; i < abcTune.lines.length; i++) {
|
|
12081
|
+
var staves = abcTune.lines[i].staff;
|
|
12082
|
+
|
|
12083
|
+
if (staves) {
|
|
12084
|
+
for (var j = 0; j < staves.length; j++) {
|
|
12085
|
+
var staff = staves[j];
|
|
12086
|
+
if (staff.clef.type !== "perc") changes = changes.concat(transposeVoices(abc, staff.voices, staff.key, steps));
|
|
12087
|
+
}
|
|
12088
|
+
}
|
|
12089
|
+
}
|
|
12090
|
+
|
|
12091
|
+
return changes;
|
|
12092
|
+
}
|
|
12093
|
+
|
|
12094
|
+
function changeAllKeySigs(abc, steps) {
|
|
12095
|
+
var changes = [];
|
|
12096
|
+
var arr = abc.split("K:"); // now each line except the first one will start with whatever is right after "K:"
|
|
12097
|
+
|
|
12098
|
+
var count = arr[0].length;
|
|
12099
|
+
|
|
12100
|
+
for (var i = 1; i < arr.length; i++) {
|
|
12101
|
+
var segment = arr[i];
|
|
12102
|
+
var match = segment.match(/^( *)([A-G])([#b]?)(\w*)/);
|
|
12103
|
+
|
|
12104
|
+
if (match) {
|
|
12105
|
+
var start = count + 2 + match[1].length; // move past the 'K:' and optional white space
|
|
12106
|
+
|
|
12107
|
+
var key = match[2] + match[3] + match[4]; // key name, accidental, and mode
|
|
12108
|
+
|
|
12109
|
+
var destinationKey = newKey({
|
|
12110
|
+
root: match[2],
|
|
12111
|
+
acc: match[3],
|
|
12112
|
+
mode: match[4]
|
|
12113
|
+
}, steps);
|
|
12114
|
+
var dest = destinationKey.root + destinationKey.acc + destinationKey.mode;
|
|
12115
|
+
changes.push({
|
|
12116
|
+
start: start,
|
|
12117
|
+
end: start + key.length,
|
|
12118
|
+
note: dest
|
|
12119
|
+
});
|
|
12120
|
+
}
|
|
12121
|
+
|
|
12122
|
+
count += segment.length + 2;
|
|
12123
|
+
}
|
|
12124
|
+
|
|
12125
|
+
return changes;
|
|
12126
|
+
}
|
|
12127
|
+
|
|
12128
|
+
function transposeVoices(abc, voices, key, steps) {
|
|
12129
|
+
var changes = [];
|
|
12130
|
+
var destinationKey = newKey(key, steps);
|
|
12131
|
+
|
|
12132
|
+
for (var i = 0; i < voices.length; i++) {
|
|
12133
|
+
changes = changes.concat(transposeVoice(abc, voices[i], key.root, createKeyAccidentals(key), destinationKey, steps));
|
|
12134
|
+
}
|
|
12135
|
+
|
|
12136
|
+
return changes;
|
|
12137
|
+
}
|
|
12138
|
+
|
|
12139
|
+
function createKeyAccidentals(key) {
|
|
12140
|
+
var ret = {};
|
|
12141
|
+
|
|
12142
|
+
for (var i = 0; i < key.accidentals.length; i++) {
|
|
12143
|
+
var acc = key.accidentals[i];
|
|
12144
|
+
if (acc.acc === 'flat') ret[acc.note.toUpperCase()] = '_';else if (acc.acc === 'sharp') ret[acc.note.toUpperCase()] = '^';
|
|
12145
|
+
}
|
|
12146
|
+
|
|
12147
|
+
return ret;
|
|
12148
|
+
}
|
|
12149
|
+
|
|
12150
|
+
function setLetterDistance(destinationKey, keyRoot, steps) {
|
|
12151
|
+
var letterDistance = letters.indexOf(destinationKey.root) - letters.indexOf(keyRoot);
|
|
12152
|
+
if (keyRoot === "none") letterDistance = letters.indexOf(destinationKey.root);
|
|
12153
|
+
|
|
12154
|
+
if (letterDistance === 0) {
|
|
12155
|
+
// This could either be a half step (like Eb => E) or almost an octave (like E => Eb)
|
|
12156
|
+
if (steps > 2) // If it is a large leap, then we are going up an octave
|
|
12157
|
+
letterDistance += 7;else if (steps === -12) // If it is a large leap, then we are going down an octave
|
|
12158
|
+
letterDistance -= 7;
|
|
12159
|
+
} else if (steps > 0 && letterDistance < 0) letterDistance += 7;else if (steps < 0 && letterDistance > 0) letterDistance -= 7;
|
|
12160
|
+
|
|
12161
|
+
if (steps > 12) letterDistance += 7;else if (steps < -12) letterDistance -= 7;
|
|
12162
|
+
return letterDistance;
|
|
12163
|
+
}
|
|
12164
|
+
|
|
12165
|
+
function transposeVoice(abc, voice, keyRoot, keyAccidentals, destinationKey, steps) {
|
|
12166
|
+
var changes = [];
|
|
12167
|
+
var letterDistance = setLetterDistance(destinationKey, keyRoot, steps);
|
|
12168
|
+
var measureAccidentals = {};
|
|
12169
|
+
var transposedMeasureAccidentals = {};
|
|
12170
|
+
|
|
12171
|
+
for (var i = 0; i < voice.length; i++) {
|
|
12172
|
+
var el = voice[i];
|
|
12173
|
+
|
|
12174
|
+
if (el.chord) {
|
|
12175
|
+
for (var c = 0; c < el.chord.length; c++) {
|
|
12176
|
+
var ch = el.chord[c];
|
|
12177
|
+
|
|
12178
|
+
if (ch.position === 'default') {
|
|
12179
|
+
var prefersFlats = destinationKey.accidentals.length && destinationKey.accidentals[0].acc === 'flat';
|
|
12180
|
+
var newChord = transposeChordName(ch.name, steps, prefersFlats, true);
|
|
12181
|
+
newChord = newChord.replace(/♭/g, "b").replace(/♯/g, "#");
|
|
12182
|
+
if (newChord !== ch.name) // If we didn't recognize the chord the input is returned unchanged and there is nothing to replace
|
|
12183
|
+
changes.push(replaceChord(abc, el.startChar, el.endChar, newChord));
|
|
12184
|
+
}
|
|
12185
|
+
}
|
|
12186
|
+
}
|
|
12187
|
+
|
|
12188
|
+
if (el.el_type === 'note' && el.pitches) {
|
|
12189
|
+
for (var j = 0; j < el.pitches.length; j++) {
|
|
12190
|
+
var note = parseNote(el.pitches[j].name, keyRoot, keyAccidentals, measureAccidentals);
|
|
12191
|
+
if (note.acc) measureAccidentals[note.name.toUpperCase()] = note.acc;
|
|
12192
|
+
var newPitch = transposePitch(note, destinationKey, letterDistance, transposedMeasureAccidentals);
|
|
12193
|
+
if (newPitch.acc) transposedMeasureAccidentals[newPitch.upper] = newPitch.acc;
|
|
12194
|
+
changes.push(replaceNote(abc, el.startChar, el.endChar, newPitch.acc + newPitch.name, j));
|
|
12195
|
+
}
|
|
12196
|
+
|
|
12197
|
+
if (el.gracenotes) {
|
|
12198
|
+
for (var g = 0; g < el.gracenotes.length; g++) {
|
|
12199
|
+
var grace = parseNote(el.gracenotes[g].name, keyRoot, keyAccidentals, measureAccidentals);
|
|
12200
|
+
if (grace.acc) measureAccidentals[grace.name.toUpperCase()] = grace.acc;
|
|
12201
|
+
var newGrace = transposePitch(grace, destinationKey, letterDistance, measureAccidentals);
|
|
12202
|
+
if (newGrace.acc) transposedMeasureAccidentals[newGrace.upper] = newGrace.acc;
|
|
12203
|
+
changes.push(replaceGrace(abc, el.startChar, el.endChar, newGrace.acc + newGrace.name, g));
|
|
12204
|
+
}
|
|
12205
|
+
}
|
|
12206
|
+
} else if (el.el_type === "bar") {
|
|
12207
|
+
measureAccidentals = {};
|
|
12208
|
+
transposedMeasureAccidentals = {};
|
|
12209
|
+
} else if (el.el_type === "keySignature") {
|
|
12210
|
+
keyRoot = el.root;
|
|
12211
|
+
keyAccidentals = createKeyAccidentals(el);
|
|
12212
|
+
destinationKey = newKey(el, steps);
|
|
12213
|
+
letterDistance = setLetterDistance(destinationKey, keyRoot, steps);
|
|
12214
|
+
}
|
|
12215
|
+
}
|
|
12216
|
+
|
|
12217
|
+
return changes;
|
|
12218
|
+
}
|
|
12219
|
+
|
|
12220
|
+
var letters = "CDEFGAB";
|
|
12221
|
+
var octaves = [",,,,", ",,,", ",,", ",", "", "'", "''", "'''", "''''"];
|
|
12222
|
+
|
|
12223
|
+
function newKey(key, steps) {
|
|
12224
|
+
if (key.root === "none") {
|
|
12225
|
+
return {
|
|
12226
|
+
root: transposeKey("C", steps),
|
|
12227
|
+
mode: "",
|
|
12228
|
+
acc: "",
|
|
12229
|
+
accidentals: []
|
|
12230
|
+
};
|
|
12231
|
+
}
|
|
12232
|
+
|
|
12233
|
+
var major = relativeMajor(key.root + key.acc + key.mode);
|
|
12234
|
+
var newMajor = transposeKey(major, steps);
|
|
12235
|
+
var newMode = relativeMode(newMajor, key.mode);
|
|
12236
|
+
var acc = keyAccidentals(newMajor);
|
|
12237
|
+
return {
|
|
12238
|
+
root: newMode[0],
|
|
12239
|
+
mode: key.mode,
|
|
12240
|
+
acc: newMode.length > 1 ? newMode[1] : '',
|
|
12241
|
+
accidentals: acc
|
|
12242
|
+
};
|
|
12243
|
+
}
|
|
12244
|
+
|
|
12245
|
+
function transposePitch(note, key, letterDistance, measureAccidentals) {
|
|
12246
|
+
// Depending on what the current note and new note are, the octave might have changed
|
|
12247
|
+
// The letterDistance is how far the change is to see if we passed "C" when transposing.
|
|
12248
|
+
var pitch = note.pitch;
|
|
12249
|
+
var origDistFromC = letters.indexOf(note.name);
|
|
12250
|
+
var root = letters.indexOf(key.root);
|
|
12251
|
+
var index = (root + pitch) % 7; // if the note crosses "c" then the octave changes, so that is true of "B" when going up one step, "A" and "B" when going up two steps, etc., and reverse when going down.
|
|
12252
|
+
|
|
12253
|
+
var newDistFromC = origDistFromC + letterDistance;
|
|
12254
|
+
var oct = note.oct;
|
|
12255
|
+
|
|
12256
|
+
while (newDistFromC > 6) {
|
|
12257
|
+
oct++;
|
|
12258
|
+
newDistFromC -= 7;
|
|
12259
|
+
}
|
|
12260
|
+
|
|
12261
|
+
while (newDistFromC < 0) {
|
|
12262
|
+
oct--;
|
|
12263
|
+
newDistFromC += 7;
|
|
12264
|
+
}
|
|
12265
|
+
|
|
12266
|
+
var name = letters[index];
|
|
12267
|
+
var acc = '';
|
|
12268
|
+
var adj = note.adj; // the amount of adjustment depends on the key - if there is a sharp in the key sig, then -1 is a natural, if there isn't, then -1 is a flat.
|
|
12269
|
+
|
|
12270
|
+
var keyAcc = '=';
|
|
12271
|
+
|
|
12272
|
+
for (var i = 0; i < key.accidentals.length; i++) {
|
|
12273
|
+
if (key.accidentals[i].note.toLowerCase() === name.toLowerCase()) {
|
|
12274
|
+
adj = adj + (key.accidentals[i].acc === 'flat' ? -1 : 1);
|
|
12275
|
+
keyAcc = key.accidentals[i].acc === 'flat' ? '_' : '^';
|
|
12276
|
+
break;
|
|
12277
|
+
}
|
|
12278
|
+
}
|
|
12279
|
+
|
|
12280
|
+
switch (adj) {
|
|
12281
|
+
case -2:
|
|
12282
|
+
acc = "__";
|
|
12283
|
+
break;
|
|
12284
|
+
|
|
12285
|
+
case -1:
|
|
12286
|
+
acc = "_";
|
|
12287
|
+
break;
|
|
12288
|
+
|
|
12289
|
+
case 0:
|
|
12290
|
+
acc = "=";
|
|
12291
|
+
break;
|
|
12292
|
+
|
|
12293
|
+
case 1:
|
|
12294
|
+
acc = "^";
|
|
12295
|
+
break;
|
|
12296
|
+
|
|
12297
|
+
case 2:
|
|
12298
|
+
acc = "^^";
|
|
12299
|
+
break;
|
|
12300
|
+
|
|
12301
|
+
case -3:
|
|
12302
|
+
// This requires a triple flat, so bump down the pitch and try again
|
|
12303
|
+
var newNote = {};
|
|
12304
|
+
newNote.pitch = note.pitch - 1;
|
|
12305
|
+
newNote.oct = note.oct;
|
|
12306
|
+
newNote.name = letters[letters.indexOf(note.name) - 1];
|
|
12307
|
+
|
|
12308
|
+
if (!newNote.name) {
|
|
12309
|
+
newNote.name = "B";
|
|
12310
|
+
newNote.oct--;
|
|
12311
|
+
}
|
|
12312
|
+
|
|
12313
|
+
if (newNote.name === "B" || newNote.name === "E") newNote.adj = note.adj + 1;else newNote.adj = note.adj + 2;
|
|
12314
|
+
return transposePitch(newNote, key, letterDistance + 1, measureAccidentals);
|
|
12315
|
+
|
|
12316
|
+
case 3:
|
|
12317
|
+
// This requires a triple sharp, so bump up the pitch and try again
|
|
12318
|
+
var newNote = {};
|
|
12319
|
+
newNote.pitch = note.pitch + 1;
|
|
12320
|
+
newNote.oct = note.oct;
|
|
12321
|
+
newNote.name = letters[letters.indexOf(note.name) + 1];
|
|
12322
|
+
|
|
12323
|
+
if (!newNote.name) {
|
|
12324
|
+
newNote.name = "C";
|
|
12325
|
+
newNote.oct++;
|
|
12326
|
+
}
|
|
12327
|
+
|
|
12328
|
+
if (newNote.name === "C" || newNote.name === "F") newNote.adj = note.adj - 1;else newNote.adj = note.adj - 2;
|
|
12329
|
+
return transposePitch(newNote, key, letterDistance + 1, measureAccidentals);
|
|
12330
|
+
}
|
|
12331
|
+
|
|
12332
|
+
if ((measureAccidentals[name] === acc || !measureAccidentals[name] && acc === keyAcc) && !note.courtesy) acc = "";
|
|
12333
|
+
|
|
12334
|
+
switch (oct) {
|
|
12335
|
+
case 0:
|
|
12336
|
+
name = name + ",,,";
|
|
12337
|
+
break;
|
|
12338
|
+
|
|
12339
|
+
case 1:
|
|
12340
|
+
name = name + ",,";
|
|
12341
|
+
break;
|
|
12342
|
+
|
|
12343
|
+
case 2:
|
|
12344
|
+
name = name + ",";
|
|
12345
|
+
break;
|
|
12346
|
+
// case 3: it is already correct
|
|
12347
|
+
|
|
12348
|
+
case 4:
|
|
12349
|
+
name = name.toLowerCase();
|
|
12350
|
+
break;
|
|
12351
|
+
|
|
12352
|
+
case 5:
|
|
12353
|
+
name = name.toLowerCase() + "'";
|
|
12354
|
+
break;
|
|
12355
|
+
|
|
12356
|
+
case 6:
|
|
12357
|
+
name = name.toLowerCase() + "''";
|
|
12358
|
+
break;
|
|
12359
|
+
|
|
12360
|
+
case 7:
|
|
12361
|
+
name = name.toLowerCase() + "'''";
|
|
12362
|
+
break;
|
|
12363
|
+
|
|
12364
|
+
case 8:
|
|
12365
|
+
name = name.toLowerCase() + "''''";
|
|
12366
|
+
break;
|
|
12367
|
+
}
|
|
12368
|
+
|
|
12369
|
+
if (oct > 4) name = name.toLowerCase();
|
|
12370
|
+
return {
|
|
12371
|
+
acc: acc,
|
|
12372
|
+
name: name,
|
|
12373
|
+
upper: name.toUpperCase()
|
|
12374
|
+
};
|
|
12375
|
+
}
|
|
12376
|
+
|
|
12377
|
+
var regPitch = /([_^=]*)([A-Ga-g])([,']*)/;
|
|
12378
|
+
var regNote = /([_^=]*[A-Ga-g][,']*)(\d*\/*\d*)([\>\<\-\)\.\s\\]*)/;
|
|
12379
|
+
var regOptionalNote = /([_^=]*[A-Ga-g][,']*)?(\d*\/*\d*)?([\>\<\-\)]*)?/;
|
|
12380
|
+
var regSpace = /(\s*)$/; // This the relationship of the note to the tonic and an octave. So what is returned is a distance in steps from the tonic and the amount of adjustment from
|
|
12381
|
+
// a normal scale. That is - in the key of D an F# is two steps from the tonic and no adjustment. A G# is three steps from the tonic and one half-step higher.
|
|
12382
|
+
// I don't think there is any adjustment needed for minor keys since the adjustment is based on the key signature and the accidentals.
|
|
12383
|
+
|
|
12384
|
+
function parseNote(note, keyRoot, keyAccidentals, measureAccidentals) {
|
|
12385
|
+
var root = keyRoot === "none" ? 0 : letters.indexOf(keyRoot);
|
|
12386
|
+
var reg = note.match(regPitch); // reg[1] : "__", "_", "", "=", "^", or "^^"
|
|
12387
|
+
// reg[2] : A-G a-g
|
|
12388
|
+
// reg[3] : commas or apostrophes
|
|
12389
|
+
|
|
12390
|
+
var name = reg[2].toUpperCase();
|
|
12391
|
+
var pos = letters.indexOf(name) - root;
|
|
12392
|
+
if (pos < 0) pos += 7;
|
|
12393
|
+
var oct = octaves.indexOf(reg[3]);
|
|
12394
|
+
if (name === reg[2]) // See if it is a capital letter and subtract an octave if so.
|
|
12395
|
+
oct--;
|
|
12396
|
+
var currentAcc = measureAccidentals[name] || keyAccidentals[name] || "="; // use the key accidentals if they exist, but override with the measure accidentals, and if neither of them exist, use a natural.
|
|
12397
|
+
|
|
12398
|
+
return {
|
|
12399
|
+
acc: reg[1],
|
|
12400
|
+
name: name,
|
|
12401
|
+
pitch: pos,
|
|
12402
|
+
oct: oct,
|
|
12403
|
+
adj: calcAdjustment(reg[1], keyAccidentals[name], measureAccidentals[name]),
|
|
12404
|
+
courtesy: reg[1] === currentAcc
|
|
12405
|
+
};
|
|
12406
|
+
}
|
|
12407
|
+
|
|
12408
|
+
function replaceNote(abc, start, end, newPitch, index) {
|
|
12409
|
+
// There may be more than just the note between the start and end - there could be spaces, there could be a chord symbol, there could be a decoration.
|
|
12410
|
+
// This could also be a part of a chord. If so, then the particular note needs to be teased out.
|
|
12411
|
+
var note = abc.substring(start, end);
|
|
12412
|
+
var match = note.match(new RegExp(regNote.source + regSpace.source), '');
|
|
12413
|
+
|
|
12414
|
+
if (match) {
|
|
12415
|
+
// This will match a single note
|
|
12416
|
+
var noteLen = match[1].length;
|
|
12417
|
+
var trailingLen = match[2].length + match[3].length + match[4].length;
|
|
12418
|
+
var leadingLen = end - start - noteLen - trailingLen;
|
|
12419
|
+
start += leadingLen;
|
|
12420
|
+
end -= trailingLen;
|
|
12421
|
+
} else {
|
|
12422
|
+
// I don't know how to capture more than one note, so I'm separating them. There is a limit of the number of notes in a chord depending on the repeats I have here, but it is unlikely to happen in real music.
|
|
12423
|
+
var regPreBracket = /([^\[]*)/;
|
|
12424
|
+
var regOpenBracket = /\[/;
|
|
12425
|
+
var regCloseBracket = /\-?](\d*\/*\d*)?([\>\<\-\)]*)/;
|
|
12426
|
+
match = note.match(new RegExp(regPreBracket.source + regOpenBracket.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regCloseBracket.source + regSpace.source));
|
|
12427
|
+
|
|
12428
|
+
if (match) {
|
|
12429
|
+
// This will match a chord
|
|
12430
|
+
// Get the number of chars used by the previous notes in this chord
|
|
12431
|
+
var count = 1 + match[1].length; // one character for the open bracket
|
|
12432
|
+
|
|
12433
|
+
for (var i = 0; i < index; i++) {
|
|
12434
|
+
// index is the iteration through the chord. This function gets called for each one.
|
|
12435
|
+
if (match[i * 3 + 2]) count += match[i * 3 + 2].length;
|
|
12436
|
+
if (match[i * 3 + 3]) count += match[i * 3 + 3].length;
|
|
12437
|
+
if (match[i * 3 + 4]) count += match[i * 3 + 4].length;
|
|
12438
|
+
}
|
|
12439
|
+
|
|
12440
|
+
start += count;
|
|
12441
|
+
var endLen = match[index * 3 + 2] ? match[index * 3 + 2].length : 0; // endLen += match[index * 3 + 3] ? match[index * 3 + 3].length : 0
|
|
12442
|
+
// endLen += match[index * 3 + 4] ? match[index * 3 + 4].length : 0
|
|
12443
|
+
|
|
12444
|
+
end = start + endLen;
|
|
12445
|
+
}
|
|
12446
|
+
}
|
|
12447
|
+
|
|
12448
|
+
return {
|
|
12449
|
+
start: start,
|
|
12450
|
+
end: end,
|
|
12451
|
+
note: newPitch
|
|
12452
|
+
};
|
|
12453
|
+
}
|
|
12454
|
+
|
|
12455
|
+
function replaceGrace(abc, start, end, newGrace, index) {
|
|
12456
|
+
var note = abc.substring(start, end); // I don't know how to capture more than one note, so I'm separating them. There is a limit of the number of notes in a chord depending on the repeats I have here, but it is unlikely to happen in real music.
|
|
12457
|
+
|
|
12458
|
+
var regOpenBrace = /\{/;
|
|
12459
|
+
var regCloseBrace = /\}/;
|
|
12460
|
+
var regPreBrace = /([^\{]*)/;
|
|
12461
|
+
var regPreNote = /(\/*)/;
|
|
12462
|
+
var match = note.match(new RegExp(regPreBrace.source + regOpenBrace.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regCloseBrace.source));
|
|
12463
|
+
|
|
12464
|
+
if (match) {
|
|
12465
|
+
// This will match all notes inside a grace symbol
|
|
12466
|
+
// Get the number of chars used by the previous graces
|
|
12467
|
+
var count = 1 + match[1].length; // one character for the open brace, and whatever comes before the brace
|
|
12468
|
+
|
|
12469
|
+
for (var i = 0; i < index; i++) {
|
|
12470
|
+
// index is the iteration through the chord. This function gets called for each one.
|
|
12471
|
+
if (match[i * 3 + 2]) count += match[i * 3 + 2].length;
|
|
12472
|
+
if (match[i * 3 + 3]) count += match[i * 3 + 3].length;
|
|
12473
|
+
if (match[i * 3 + 4]) count += match[i * 3 + 4].length;
|
|
12474
|
+
if (match[i * 3 + 5]) count += match[i * 3 + 5].length;
|
|
12475
|
+
}
|
|
12476
|
+
|
|
12477
|
+
if (match[index * 3 + 2]) count += match[i * 3 + 2].length;
|
|
12478
|
+
start += count;
|
|
12479
|
+
var endLen = match[index * 3 + 3] ? match[index * 3 + 3].length : 0;
|
|
12480
|
+
endLen += match[index * 3 + 4] ? match[index * 3 + 4].length : 0;
|
|
12481
|
+
endLen += match[index * 3 + 5] ? match[index * 3 + 5].length : 0;
|
|
12482
|
+
end = start + endLen;
|
|
12483
|
+
}
|
|
12484
|
+
|
|
12485
|
+
return {
|
|
12486
|
+
start: start,
|
|
12487
|
+
end: end,
|
|
12488
|
+
note: newGrace
|
|
12489
|
+
};
|
|
12490
|
+
}
|
|
12491
|
+
|
|
12492
|
+
function replaceChord(abc, start, end, newChord) {
|
|
12493
|
+
// Isolate the chord and just replace that
|
|
12494
|
+
var match = abc.substring(start, end).match(/([^"]+)?(".+")+/);
|
|
12495
|
+
if (match[1]) start += match[1].length;
|
|
12496
|
+
end = start + match[2].length; // leave the quote in, so skip one more
|
|
11897
12497
|
|
|
11898
|
-
function calcLineWraps(tune, widths, params) {
|
|
11899
|
-
// For calculating how much can go on the line, it depends on the width of the line. It is a convenience to just divide it here
|
|
11900
|
-
// by the minimum spacing instead of multiplying the min spacing later.
|
|
11901
|
-
// The scaling works differently: this is done by changing the scaling of the outer SVG, so the scaling needs to be compensated
|
|
11902
|
-
// for here, because the actual width will be different from the calculated numbers.
|
|
11903
|
-
// If the desired width is less than the margin, just punt and return the original tune
|
|
11904
|
-
//console.log(widths)
|
|
11905
|
-
if (widths.length === 0 || params.staffwidth < widths[0].left) {
|
|
11906
12498
|
return {
|
|
11907
|
-
|
|
11908
|
-
|
|
11909
|
-
|
|
12499
|
+
start: start + 1,
|
|
12500
|
+
end: end - 1,
|
|
12501
|
+
note: newChord
|
|
11910
12502
|
};
|
|
11911
12503
|
}
|
|
11912
12504
|
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
|
|
11917
|
-
|
|
12505
|
+
function calcAdjustment(thisAccidental, keyAccidental, measureAccidental) {
|
|
12506
|
+
if (!thisAccidental && measureAccidental) {
|
|
12507
|
+
// There was no accidental on this note, but there was earlier in the measure, so we'll use that
|
|
12508
|
+
thisAccidental = measureAccidental;
|
|
12509
|
+
}
|
|
11918
12510
|
|
|
11919
|
-
|
|
11920
|
-
var accumulatedLineBreaks = [];
|
|
11921
|
-
var explanations = [];
|
|
12511
|
+
if (!thisAccidental) return 0; // there is no deviation from the key.
|
|
11922
12512
|
|
|
11923
|
-
|
|
11924
|
-
|
|
11925
|
-
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
var allowableVariance = usableWidth / minSpacingLimit / scale;
|
|
11929
|
-
var explanation = {
|
|
11930
|
-
widths: section,
|
|
11931
|
-
lineBreakPoint: lineBreakPoint,
|
|
11932
|
-
minLineSize: minLineSize,
|
|
11933
|
-
attempts: [],
|
|
11934
|
-
staffWidth: params.staffwidth,
|
|
11935
|
-
minWidth: Math.round(allowableVariance)
|
|
11936
|
-
}; // If there is a preferred number of measures per line, test that first. If none of the lines is too long, then we're finished.
|
|
12513
|
+
switch (keyAccidental) {
|
|
12514
|
+
case undefined:
|
|
12515
|
+
switch (thisAccidental) {
|
|
12516
|
+
case '__':
|
|
12517
|
+
return -2;
|
|
11937
12518
|
|
|
11938
|
-
|
|
12519
|
+
case '_':
|
|
12520
|
+
return -1;
|
|
11939
12521
|
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
explanation.attempts.push({
|
|
11943
|
-
type: "Fixed Measures Per Line",
|
|
11944
|
-
preferredMeasuresPerLine: preferredMeasuresPerLine,
|
|
11945
|
-
lineBreaks: f.lineBreaks,
|
|
11946
|
-
failed: f.failed,
|
|
11947
|
-
totals: f.totals
|
|
11948
|
-
});
|
|
11949
|
-
if (!f.failed) lineBreaks = f.lineBreaks;
|
|
11950
|
-
} // If we don't have lineBreaks yet, use the free form method of line breaks.
|
|
11951
|
-
// This will be called either if Preferred Measures is not used, or if the music is just weird - like a single measure is way too crowded.
|
|
12522
|
+
case '=':
|
|
12523
|
+
return 0;
|
|
11952
12524
|
|
|
12525
|
+
case '^':
|
|
12526
|
+
return 1;
|
|
11953
12527
|
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
explanation.attempts.push({
|
|
11957
|
-
type: "Free Form",
|
|
11958
|
-
lineBreaks: ff.lineBreaks,
|
|
11959
|
-
totals: ff.totals
|
|
11960
|
-
});
|
|
11961
|
-
lineBreaks = ff.lineBreaks; // We now have an acceptable number of lines, but the measures may not be optimally distributed. See if there is a better distribution.
|
|
12528
|
+
case '^^':
|
|
12529
|
+
return 2;
|
|
11962
12530
|
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
explanation.attempts.push({
|
|
11968
|
-
type: "Optimize",
|
|
11969
|
-
failed: ff.failed,
|
|
11970
|
-
reason: ff.reason,
|
|
11971
|
-
lineBreaks: ff.lineBreaks,
|
|
11972
|
-
totals: ff.totals
|
|
11973
|
-
});
|
|
11974
|
-
if (!ff.failed) lineBreaks = ff.lineBreaks;
|
|
11975
|
-
}
|
|
11976
|
-
}
|
|
12531
|
+
default:
|
|
12532
|
+
return 0;
|
|
12533
|
+
// this should never happen
|
|
12534
|
+
}
|
|
11977
12535
|
|
|
11978
|
-
|
|
11979
|
-
|
|
11980
|
-
|
|
12536
|
+
case '_':
|
|
12537
|
+
switch (thisAccidental) {
|
|
12538
|
+
case '__':
|
|
12539
|
+
return -1;
|
|
11981
12540
|
|
|
12541
|
+
case '_':
|
|
12542
|
+
return 0;
|
|
11982
12543
|
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
ret.explanation = explanations;
|
|
11986
|
-
ret.reParse = true;
|
|
11987
|
-
return ret;
|
|
11988
|
-
}
|
|
12544
|
+
case '=':
|
|
12545
|
+
return 1;
|
|
11989
12546
|
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
|
|
12547
|
+
case '^':
|
|
12548
|
+
return 2;
|
|
12549
|
+
|
|
12550
|
+
case '^^':
|
|
12551
|
+
return 3;
|
|
12552
|
+
|
|
12553
|
+
default:
|
|
12554
|
+
return 0;
|
|
12555
|
+
// this should never happen
|
|
12556
|
+
}
|
|
12557
|
+
|
|
12558
|
+
case '^':
|
|
12559
|
+
switch (thisAccidental) {
|
|
12560
|
+
case '__':
|
|
12561
|
+
return -3;
|
|
12562
|
+
|
|
12563
|
+
case '_':
|
|
12564
|
+
return -2;
|
|
12565
|
+
|
|
12566
|
+
case '=':
|
|
12567
|
+
return -1;
|
|
12568
|
+
|
|
12569
|
+
case '^':
|
|
12570
|
+
return 0;
|
|
12571
|
+
|
|
12572
|
+
case '^^':
|
|
12573
|
+
return 1;
|
|
12574
|
+
|
|
12575
|
+
default:
|
|
12576
|
+
return 0;
|
|
12577
|
+
// this should never happen
|
|
12578
|
+
}
|
|
12579
|
+
|
|
12580
|
+
}
|
|
12581
|
+
|
|
12582
|
+
return 0; // this should never happen
|
|
12583
|
+
}
|
|
12584
|
+
})();
|
|
12585
|
+
|
|
12586
|
+
module.exports = strTranspose;
|
|
11994
12587
|
|
|
11995
12588
|
/***/ }),
|
|
11996
12589
|
|
|
@@ -12771,7 +13364,9 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
12771
13364
|
volume: velocity,
|
|
12772
13365
|
start: timeToRealTime(elem.time),
|
|
12773
13366
|
duration: durationRounded(note.duration),
|
|
12774
|
-
instrument: currentInstrument
|
|
13367
|
+
instrument: currentInstrument,
|
|
13368
|
+
startChar: elem.elem.startChar,
|
|
13369
|
+
endChar: elem.elem.endChar
|
|
12775
13370
|
};
|
|
12776
13371
|
p = adjustForMicroTone(p);
|
|
12777
13372
|
|
|
@@ -14844,6 +15439,8 @@ var createNoteMap = function createNoteMap(sequence) {
|
|
|
14844
15439
|
end: Math.round((ev.start + len - gap) * 1000000) / 1000000,
|
|
14845
15440
|
volume: ev.volume
|
|
14846
15441
|
};
|
|
15442
|
+
if (ev.startChar) obj.startChar = ev.startChar;
|
|
15443
|
+
if (ev.endChar) obj.endChar = ev.endChar;
|
|
14847
15444
|
if (ev.style) obj.style = ev.style;
|
|
14848
15445
|
if (ev.cents) obj.cents = ev.cents;
|
|
14849
15446
|
map[i].push(obj);
|
|
@@ -19354,6 +19951,15 @@ AbstractEngraver.prototype.createBeam = function (isSingleLineStaff, voice, elem
|
|
|
19354
19951
|
if (hint) beamelem.setHint();
|
|
19355
19952
|
|
|
19356
19953
|
for (var i = 0; i < elems.length; i++) {
|
|
19954
|
+
// Do a first pass to figure out the stem direction before creating the notes, so that staccatos and other decorations can be placed correctly.
|
|
19955
|
+
beamelem.runningDirection(elems[i]);
|
|
19956
|
+
}
|
|
19957
|
+
|
|
19958
|
+
beamelem.setStemDirection();
|
|
19959
|
+
var tempStemDir = this.stemdir;
|
|
19960
|
+
this.stemdir = beamelem.stemsUp ? 'up' : 'down';
|
|
19961
|
+
|
|
19962
|
+
for (i = 0; i < elems.length; i++) {
|
|
19357
19963
|
var elem = elems[i];
|
|
19358
19964
|
var abselem = this.createNote(elem, true, isSingleLineStaff, voice);
|
|
19359
19965
|
abselemset.push(abselem);
|
|
@@ -19368,6 +19974,7 @@ AbstractEngraver.prototype.createBeam = function (isSingleLineStaff, voice, elem
|
|
|
19368
19974
|
|
|
19369
19975
|
beamelem.calcDir();
|
|
19370
19976
|
voice.addBeam(beamelem);
|
|
19977
|
+
this.stemdir = tempStemDir;
|
|
19371
19978
|
return abselemset;
|
|
19372
19979
|
};
|
|
19373
19980
|
|
|
@@ -20113,6 +20720,11 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
|
|
|
20113
20720
|
|
|
20114
20721
|
|
|
20115
20722
|
abselem.extraw -= 5;
|
|
20723
|
+
|
|
20724
|
+
if (elem.chord !== undefined) {
|
|
20725
|
+
var ret3 = addChord(this.getTextSize, abselem, elem, 0, 0, 0, false);
|
|
20726
|
+
}
|
|
20727
|
+
|
|
20116
20728
|
return abselem;
|
|
20117
20729
|
};
|
|
20118
20730
|
|
|
@@ -20171,6 +20783,15 @@ BeamElem.prototype.setHint = function () {
|
|
|
20171
20783
|
this.hint = true;
|
|
20172
20784
|
};
|
|
20173
20785
|
|
|
20786
|
+
BeamElem.prototype.runningDirection = function (abcelem) {
|
|
20787
|
+
var pitch = abcelem.averagepitch;
|
|
20788
|
+
if (pitch === undefined) return; // don't include elements like spacers in beams
|
|
20789
|
+
|
|
20790
|
+
this.total = Math.round(this.total + pitch);
|
|
20791
|
+
if (!this.count) this.count = 0;
|
|
20792
|
+
this.count++;
|
|
20793
|
+
};
|
|
20794
|
+
|
|
20174
20795
|
BeamElem.prototype.add = function (abselem) {
|
|
20175
20796
|
var pitch = abselem.abcelem.averagepitch;
|
|
20176
20797
|
if (pitch === undefined) return; // don't include elements like spacers in beams
|
|
@@ -20193,6 +20814,24 @@ BeamElem.prototype.addBeam = function (beam) {
|
|
|
20193
20814
|
this.beams.push(beam);
|
|
20194
20815
|
};
|
|
20195
20816
|
|
|
20817
|
+
BeamElem.prototype.setStemDirection = function () {
|
|
20818
|
+
// Have to figure this out before the notes are placed because placing the notes also places the decorations.
|
|
20819
|
+
this.average = calcAverage(this.total, this.count);
|
|
20820
|
+
|
|
20821
|
+
if (this.forceup) {
|
|
20822
|
+
this.stemsUp = true;
|
|
20823
|
+
} else if (this.forcedown) {
|
|
20824
|
+
this.stemsUp = false;
|
|
20825
|
+
} else {
|
|
20826
|
+
var middleLine = 6; // hardcoded 6 is B
|
|
20827
|
+
|
|
20828
|
+
this.stemsUp = this.average < middleLine; // true is up, false is down;
|
|
20829
|
+
}
|
|
20830
|
+
|
|
20831
|
+
delete this.count;
|
|
20832
|
+
this.total = 0;
|
|
20833
|
+
};
|
|
20834
|
+
|
|
20196
20835
|
BeamElem.prototype.calcDir = function () {
|
|
20197
20836
|
this.average = calcAverage(this.total, this.elems.length);
|
|
20198
20837
|
|
|
@@ -20761,6 +21400,8 @@ var DynamicDecoration = __webpack_require__(/*! ./abc_dynamic_decoration */ "./s
|
|
|
20761
21400
|
|
|
20762
21401
|
var CrescendoElem = __webpack_require__(/*! ./abc_crescendo_element */ "./src/write/abc_crescendo_element.js");
|
|
20763
21402
|
|
|
21403
|
+
var GlissandoElem = __webpack_require__(/*! ./abc_glissando_element */ "./src/write/abc_glissando_element.js");
|
|
21404
|
+
|
|
20764
21405
|
var glyphs = __webpack_require__(/*! ./abc_glyphs */ "./src/write/abc_glyphs.js");
|
|
20765
21406
|
|
|
20766
21407
|
var RelativeElement = __webpack_require__(/*! ./abc_relative_element */ "./src/write/abc_relative_element.js");
|
|
@@ -21077,6 +21718,7 @@ function leftDecoration(decoration, abselem, roomtaken) {
|
|
|
21077
21718
|
Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, positioning) {
|
|
21078
21719
|
var diminuendo;
|
|
21079
21720
|
var crescendo;
|
|
21721
|
+
var glissando;
|
|
21080
21722
|
|
|
21081
21723
|
for (var i = 0; i < decoration.length; i++) {
|
|
21082
21724
|
switch (decoration[i]) {
|
|
@@ -21105,6 +21747,19 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
|
|
|
21105
21747
|
};
|
|
21106
21748
|
this.startCrescendoX = undefined;
|
|
21107
21749
|
break;
|
|
21750
|
+
|
|
21751
|
+
case "glissando(":
|
|
21752
|
+
this.startGlissandoX = abselem;
|
|
21753
|
+
glissando = undefined;
|
|
21754
|
+
break;
|
|
21755
|
+
|
|
21756
|
+
case "glissando)":
|
|
21757
|
+
glissando = {
|
|
21758
|
+
start: this.startGlissandoX,
|
|
21759
|
+
stop: abselem
|
|
21760
|
+
};
|
|
21761
|
+
this.startGlissandoX = undefined;
|
|
21762
|
+
break;
|
|
21108
21763
|
}
|
|
21109
21764
|
}
|
|
21110
21765
|
|
|
@@ -21115,6 +21770,10 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
|
|
|
21115
21770
|
if (crescendo) {
|
|
21116
21771
|
voice.addOther(new CrescendoElem(crescendo.start, crescendo.stop, "<", positioning));
|
|
21117
21772
|
}
|
|
21773
|
+
|
|
21774
|
+
if (glissando) {
|
|
21775
|
+
voice.addOther(new GlissandoElem(glissando.start, glissando.stop));
|
|
21776
|
+
}
|
|
21118
21777
|
};
|
|
21119
21778
|
|
|
21120
21779
|
Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals) {
|
|
@@ -21239,6 +21898,7 @@ var tablatures = __webpack_require__(/*! ../api/abc_tablatures */ "./src/api/abc
|
|
|
21239
21898
|
|
|
21240
21899
|
var EngraverController = function EngraverController(paper, params) {
|
|
21241
21900
|
params = params || {};
|
|
21901
|
+
this.oneSvgPerLine = params.oneSvgPerLine;
|
|
21242
21902
|
this.selectionColor = params.selectionColor;
|
|
21243
21903
|
this.dragColor = params.dragColor ? params.dragColor : params.selectionColor;
|
|
21244
21904
|
this.dragging = !!params.dragging;
|
|
@@ -21455,8 +22115,78 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
21455
22115
|
var ret = draw(this.renderer, this.classes, abcTune, this.width, maxWidth, this.responsive, scale, this.selectTypes, tuneNumber, lineOffset);
|
|
21456
22116
|
this.staffgroups = ret.staffgroups;
|
|
21457
22117
|
this.selectables = ret.selectables;
|
|
21458
|
-
|
|
21459
|
-
|
|
22118
|
+
|
|
22119
|
+
if (this.oneSvgPerLine) {
|
|
22120
|
+
var div = this.renderer.paper.svg.parentNode;
|
|
22121
|
+
this.svgs = splitSvgIntoLines(div, abcTune.metaText.title);
|
|
22122
|
+
} else {
|
|
22123
|
+
this.svgs = [this.renderer.paper.svg];
|
|
22124
|
+
}
|
|
22125
|
+
|
|
22126
|
+
setupSelection(this, this.svgs);
|
|
22127
|
+
};
|
|
22128
|
+
|
|
22129
|
+
function splitSvgIntoLines(output, title) {
|
|
22130
|
+
// Each line is a top level <g> in the svg. To split it into separate
|
|
22131
|
+
// svgs iterate through each of those and put them in a new svg. Since
|
|
22132
|
+
// they are placed absolutely, the viewBox needs to be manipulated to
|
|
22133
|
+
// get the correct vertical positioning.
|
|
22134
|
+
// We copy all the attributes from the original svg except for the aria-label
|
|
22135
|
+
// since we want that to include a count. And the height is now a fraction of the original svg.
|
|
22136
|
+
if (!title) title = "Untitled";
|
|
22137
|
+
var source = output.querySelector("svg");
|
|
22138
|
+
var style = source.querySelector("style");
|
|
22139
|
+
var width = source.getAttribute("width");
|
|
22140
|
+
var sections = output.querySelectorAll("svg > g"); // each section is a line, or the top matter or the bottom matter, or text that has been inserted.
|
|
22141
|
+
|
|
22142
|
+
var nextTop = 0; // There are often gaps between the elements for spacing, so the actual top and height needs to be inferred.
|
|
22143
|
+
|
|
22144
|
+
var wrappers = []; // Create all the elements and place them at once because we use the current svg to get data. It would disappear after placing the first line.
|
|
22145
|
+
|
|
22146
|
+
var svgs = [];
|
|
22147
|
+
|
|
22148
|
+
for (var i = 0; i < sections.length; i++) {
|
|
22149
|
+
var section = sections[i];
|
|
22150
|
+
var box = section.getBBox();
|
|
22151
|
+
var gapBetweenLines = box.y - nextTop; // take the margin into account
|
|
22152
|
+
|
|
22153
|
+
var height = box.height + gapBetweenLines;
|
|
22154
|
+
var wrapper = document.createElement("div");
|
|
22155
|
+
wrapper.setAttribute("style", "overflow: hidden;height:" + height + "px;");
|
|
22156
|
+
var svg = duplicateSvg(source);
|
|
22157
|
+
var fullTitle = "Sheet Music for \"" + title + "\" section " + (i + 1);
|
|
22158
|
+
svg.setAttribute("aria-label", fullTitle);
|
|
22159
|
+
svg.setAttribute("height", height);
|
|
22160
|
+
svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + height);
|
|
22161
|
+
svg.appendChild(style.cloneNode(true));
|
|
22162
|
+
var titleEl = document.createElement("title");
|
|
22163
|
+
titleEl.innerText = fullTitle;
|
|
22164
|
+
svg.appendChild(titleEl);
|
|
22165
|
+
svg.appendChild(section);
|
|
22166
|
+
wrapper.appendChild(svg);
|
|
22167
|
+
svgs.push(svg);
|
|
22168
|
+
output.appendChild(wrapper); //wrappers.push(wrapper)
|
|
22169
|
+
|
|
22170
|
+
nextTop = box.y + box.height;
|
|
22171
|
+
} // for (i = 0; i < wrappers.length; i++)
|
|
22172
|
+
// output.appendChild(wrappers[i])
|
|
22173
|
+
|
|
22174
|
+
|
|
22175
|
+
output.removeChild(source);
|
|
22176
|
+
return svgs;
|
|
22177
|
+
}
|
|
22178
|
+
|
|
22179
|
+
function duplicateSvg(source) {
|
|
22180
|
+
var svgNS = "http://www.w3.org/2000/svg";
|
|
22181
|
+
var svg = document.createElementNS(svgNS, "svg");
|
|
22182
|
+
|
|
22183
|
+
for (var i = 0; i < source.attributes.length; i++) {
|
|
22184
|
+
var attr = source.attributes[i];
|
|
22185
|
+
if (attr.name !== "height" && attr.name != "aria-label") svg.setAttribute(attr.name, attr.value);
|
|
22186
|
+
}
|
|
22187
|
+
|
|
22188
|
+
return svg;
|
|
22189
|
+
}
|
|
21460
22190
|
|
|
21461
22191
|
EngraverController.prototype.getDim = function (historyEl) {
|
|
21462
22192
|
// Get the dimensions on demand because the getBBox call is expensive.
|
|
@@ -21481,6 +22211,23 @@ module.exports = EngraverController;
|
|
|
21481
22211
|
|
|
21482
22212
|
/***/ }),
|
|
21483
22213
|
|
|
22214
|
+
/***/ "./src/write/abc_glissando_element.js":
|
|
22215
|
+
/*!********************************************!*\
|
|
22216
|
+
!*** ./src/write/abc_glissando_element.js ***!
|
|
22217
|
+
\********************************************/
|
|
22218
|
+
/***/ (function(module) {
|
|
22219
|
+
|
|
22220
|
+
var GlissandoElem = function GlissandoElem(anchor1, anchor2) {
|
|
22221
|
+
this.type = "GlissandoElem";
|
|
22222
|
+
this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
|
|
22223
|
+
|
|
22224
|
+
this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
|
|
22225
|
+
};
|
|
22226
|
+
|
|
22227
|
+
module.exports = GlissandoElem;
|
|
22228
|
+
|
|
22229
|
+
/***/ }),
|
|
22230
|
+
|
|
21484
22231
|
/***/ "./src/write/abc_glyphs.js":
|
|
21485
22232
|
/*!*********************************!*\
|
|
21486
22233
|
!*** ./src/write/abc_glyphs.js ***!
|
|
@@ -23806,8 +24553,10 @@ var Selectables = __webpack_require__(/*! ./selectables */ "./src/write/draw/sel
|
|
|
23806
24553
|
|
|
23807
24554
|
function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset) {
|
|
23808
24555
|
var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
|
|
24556
|
+
renderer.paper.openGroup();
|
|
23809
24557
|
renderer.moveY(renderer.padding.top);
|
|
23810
24558
|
nonMusic(renderer, abcTune.topText, selectables);
|
|
24559
|
+
renderer.paper.closeGroup();
|
|
23811
24560
|
renderer.moveY(renderer.spacing.music);
|
|
23812
24561
|
var staffgroups = [];
|
|
23813
24562
|
|
|
@@ -23816,6 +24565,8 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
23816
24565
|
var abcLine = abcTune.lines[line];
|
|
23817
24566
|
|
|
23818
24567
|
if (abcLine.staff) {
|
|
24568
|
+
renderer.paper.openGroup();
|
|
24569
|
+
|
|
23819
24570
|
if (abcLine.vskip) {
|
|
23820
24571
|
renderer.moveY(abcLine.vskip);
|
|
23821
24572
|
}
|
|
@@ -23825,17 +24576,22 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
23825
24576
|
staffgroup.line = lineOffset + line; // If there are non-music lines then the staffgroup array won't line up with the line array, so this keeps track.
|
|
23826
24577
|
|
|
23827
24578
|
staffgroups.push(staffgroup);
|
|
24579
|
+
renderer.paper.closeGroup();
|
|
23828
24580
|
} else if (abcLine.nonMusic) {
|
|
24581
|
+
renderer.paper.openGroup();
|
|
23829
24582
|
nonMusic(renderer, abcLine.nonMusic, selectables);
|
|
24583
|
+
renderer.paper.closeGroup();
|
|
23830
24584
|
}
|
|
23831
24585
|
}
|
|
23832
24586
|
|
|
23833
24587
|
classes.reset();
|
|
23834
24588
|
|
|
23835
24589
|
if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) {
|
|
24590
|
+
renderer.paper.openGroup();
|
|
23836
24591
|
renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be?
|
|
23837
24592
|
|
|
23838
24593
|
nonMusic(renderer, abcTune.bottomText, selectables);
|
|
24594
|
+
renderer.paper.closeGroup();
|
|
23839
24595
|
}
|
|
23840
24596
|
|
|
23841
24597
|
setPaperSize(renderer, maxWidth, scale, responsive);
|
|
@@ -23965,6 +24721,107 @@ module.exports = drawEnding;
|
|
|
23965
24721
|
|
|
23966
24722
|
/***/ }),
|
|
23967
24723
|
|
|
24724
|
+
/***/ "./src/write/draw/glissando.js":
|
|
24725
|
+
/*!*************************************!*\
|
|
24726
|
+
!*** ./src/write/draw/glissando.js ***!
|
|
24727
|
+
\*************************************/
|
|
24728
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
24729
|
+
|
|
24730
|
+
var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
|
|
24731
|
+
|
|
24732
|
+
var printPath = __webpack_require__(/*! ./print-path */ "./src/write/draw/print-path.js");
|
|
24733
|
+
|
|
24734
|
+
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
|
|
24735
|
+
|
|
24736
|
+
function drawGlissando(renderer, params, selectables) {
|
|
24737
|
+
if (!params.anchor1 || !params.anchor2 || !params.anchor1.heads || !params.anchor2.heads || params.anchor1.heads.length === 0 || params.anchor2.heads.length === 0) window.console.error("Glissando Element not set.");
|
|
24738
|
+
var margin = 4;
|
|
24739
|
+
var leftY = renderer.calcY(params.anchor1.heads[0].pitch);
|
|
24740
|
+
var rightY = renderer.calcY(params.anchor2.heads[0].pitch);
|
|
24741
|
+
var leftX = params.anchor1.x + params.anchor1.w / 2;
|
|
24742
|
+
var rightX = params.anchor2.x + params.anchor2.w / 2;
|
|
24743
|
+
var len = lineLength(leftX, leftY, rightX, rightY);
|
|
24744
|
+
var marginLeft = params.anchor1.w / 2 + margin;
|
|
24745
|
+
var marginRight = params.anchor2.w / 2 + margin;
|
|
24746
|
+
var s = slope(leftX, leftY, rightX, rightY);
|
|
24747
|
+
var leftYAdj = getY(leftY, s, marginLeft);
|
|
24748
|
+
var rightYAdj = getY(rightY, s, -marginRight);
|
|
24749
|
+
var num = numSquigglies(len - marginLeft - marginRight);
|
|
24750
|
+
var el = drawSquiggly(renderer, leftX + marginLeft, leftYAdj, num, s);
|
|
24751
|
+
selectables.wrapSvgEl({
|
|
24752
|
+
el_type: "glissando",
|
|
24753
|
+
startChar: -1,
|
|
24754
|
+
endChar: -1
|
|
24755
|
+
}, el);
|
|
24756
|
+
return [el];
|
|
24757
|
+
}
|
|
24758
|
+
|
|
24759
|
+
function lineLength(leftX, leftY, rightX, rightY) {
|
|
24760
|
+
// The length from notehead center to notehead center.
|
|
24761
|
+
var w = rightX - leftX;
|
|
24762
|
+
var h = rightY - leftY;
|
|
24763
|
+
return Math.sqrt(w * w + h * h);
|
|
24764
|
+
}
|
|
24765
|
+
|
|
24766
|
+
function slope(leftX, leftY, rightX, rightY) {
|
|
24767
|
+
return (rightY - leftY) / (rightX - leftX);
|
|
24768
|
+
}
|
|
24769
|
+
|
|
24770
|
+
function getY(y, slope, xOfs) {
|
|
24771
|
+
return roundNumber(y + xOfs * slope);
|
|
24772
|
+
}
|
|
24773
|
+
|
|
24774
|
+
function numSquigglies(length) {
|
|
24775
|
+
var endLen = 5; // The width of the end - that is, the non repeating part
|
|
24776
|
+
|
|
24777
|
+
return Math.max(2, Math.floor((length - endLen * 2) / 6));
|
|
24778
|
+
}
|
|
24779
|
+
|
|
24780
|
+
var leftStart = [[3.5, -4.8]];
|
|
24781
|
+
var right = [[1.5, -1], [.3, -.3], [-3.5, 3.8]];
|
|
24782
|
+
var leftEnd = [[-1.5, 2]];
|
|
24783
|
+
var top = [[3, 4], [3, -4]];
|
|
24784
|
+
var bottom = [[-3, 4], [-3, -4]];
|
|
24785
|
+
|
|
24786
|
+
function segment(arr, slope) {
|
|
24787
|
+
var ret = "";
|
|
24788
|
+
|
|
24789
|
+
for (var i = 0; i < arr.length; i++) {
|
|
24790
|
+
ret += 'l' + arr[i][0] + ' ' + getY(arr[i][1], slope, arr[i][0]);
|
|
24791
|
+
}
|
|
24792
|
+
|
|
24793
|
+
return ret;
|
|
24794
|
+
}
|
|
24795
|
+
|
|
24796
|
+
var drawSquiggly = function drawSquiggly(renderer, x, y, num, slope) {
|
|
24797
|
+
var p = sprintf("M %f %f", x, y);
|
|
24798
|
+
p += segment(leftStart, slope);
|
|
24799
|
+
var i;
|
|
24800
|
+
|
|
24801
|
+
for (i = 0; i < num; i++) {
|
|
24802
|
+
p += segment(top, slope);
|
|
24803
|
+
}
|
|
24804
|
+
|
|
24805
|
+
p += segment(right, slope);
|
|
24806
|
+
|
|
24807
|
+
for (i = 0; i < num; i++) {
|
|
24808
|
+
p += segment(bottom, slope);
|
|
24809
|
+
}
|
|
24810
|
+
|
|
24811
|
+
p += segment(leftEnd, slope) + 'z';
|
|
24812
|
+
return printPath(renderer, {
|
|
24813
|
+
path: p,
|
|
24814
|
+
highlight: "stroke",
|
|
24815
|
+
stroke: renderer.foregroundColor,
|
|
24816
|
+
'class': renderer.controller.classes.generate('decoration'),
|
|
24817
|
+
"data-name": "glissando"
|
|
24818
|
+
});
|
|
24819
|
+
};
|
|
24820
|
+
|
|
24821
|
+
module.exports = drawGlissando;
|
|
24822
|
+
|
|
24823
|
+
/***/ }),
|
|
24824
|
+
|
|
23968
24825
|
/***/ "./src/write/draw/group-elements.js":
|
|
23969
24826
|
/*!******************************************!*\
|
|
23970
24827
|
!*** ./src/write/draw/group-elements.js ***!
|
|
@@ -24801,8 +25658,8 @@ function drawStaffGroup(renderer, params, selectables, lineNumber) {
|
|
|
24801
25658
|
// renderer.y will be offset at the beginning of each staff by the amount required to make the relative pitch work.
|
|
24802
25659
|
// If there are multiple staves, then renderer.y will be incremented for each new staff.
|
|
24803
25660
|
var colorIndex; // An invisible marker is useful to be able to find where each system starts.
|
|
25661
|
+
//addInvisibleMarker(renderer, "abcjs-top-of-system");
|
|
24804
25662
|
|
|
24805
|
-
addInvisibleMarker(renderer, "abcjs-top-of-system");
|
|
24806
25663
|
var startY = renderer.y; // So that it can be restored after we're done.
|
|
24807
25664
|
// Set the absolute Y position for each staff here, so the voice drawing below can just use if.
|
|
24808
25665
|
|
|
@@ -24994,20 +25851,11 @@ function printBrace(renderer, absoluteY, brace, index, selectables) {
|
|
|
24994
25851
|
}
|
|
24995
25852
|
}
|
|
24996
25853
|
}
|
|
24997
|
-
}
|
|
25854
|
+
} // function addInvisibleMarker(renderer, className) {
|
|
25855
|
+
// var y = Math.round(renderer.y);
|
|
25856
|
+
// renderer.paper.pathToBack({path:"M 0 " + y + " L 0 0", stroke:"none", fill:"none", "stroke-opacity": 0, "fill-opacity": 0, 'class': renderer.controller.classes.generate(className), 'data-vertical': y });
|
|
25857
|
+
// }
|
|
24998
25858
|
|
|
24999
|
-
function addInvisibleMarker(renderer, className) {
|
|
25000
|
-
var y = Math.round(renderer.y);
|
|
25001
|
-
renderer.paper.pathToBack({
|
|
25002
|
-
path: "M 0 " + y + " L 0 0",
|
|
25003
|
-
stroke: "none",
|
|
25004
|
-
fill: "none",
|
|
25005
|
-
"stroke-opacity": 0,
|
|
25006
|
-
"fill-opacity": 0,
|
|
25007
|
-
'class': renderer.controller.classes.generate(className),
|
|
25008
|
-
'data-vertical': y
|
|
25009
|
-
});
|
|
25010
|
-
}
|
|
25011
25859
|
|
|
25012
25860
|
function boxAllElements(renderer, voices, which) {
|
|
25013
25861
|
for (var i = 0; i < which.length; i++) {
|
|
@@ -25502,6 +26350,8 @@ module.exports = drawTriplet;
|
|
|
25502
26350
|
\*********************************/
|
|
25503
26351
|
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
25504
26352
|
|
|
26353
|
+
var drawGlissando = __webpack_require__(/*! ./glissando */ "./src/write/draw/glissando.js");
|
|
26354
|
+
|
|
25505
26355
|
var drawCrescendo = __webpack_require__(/*! ./crescendo */ "./src/write/draw/crescendo.js");
|
|
25506
26356
|
|
|
25507
26357
|
var drawDynamics = __webpack_require__(/*! ./dynamics */ "./src/write/draw/dynamics.js");
|
|
@@ -25601,6 +26451,10 @@ function drawVoice(renderer, params, bartop, selectables, staffPos) {
|
|
|
25601
26451
|
renderer.controller.classes.incrMeasure();
|
|
25602
26452
|
} else {
|
|
25603
26453
|
switch (child.type) {
|
|
26454
|
+
case "GlissandoElem":
|
|
26455
|
+
child.elemset = drawGlissando(renderer, child, selectables);
|
|
26456
|
+
break;
|
|
26457
|
+
|
|
25604
26458
|
case "CrescendoElem":
|
|
25605
26459
|
child.elemset = drawCrescendo(renderer, child, selectables);
|
|
25606
26460
|
break;
|
|
@@ -27168,7 +28022,7 @@ module.exports = layoutVoice;
|
|
|
27168
28022
|
|
|
27169
28023
|
var spacing = __webpack_require__(/*! ./abc_spacing */ "./src/write/abc_spacing.js");
|
|
27170
28024
|
|
|
27171
|
-
function setupSelection(engraver) {
|
|
28025
|
+
function setupSelection(engraver, svgs) {
|
|
27172
28026
|
engraver.rangeHighlight = rangeHighlight;
|
|
27173
28027
|
|
|
27174
28028
|
if (engraver.dragging) {
|
|
@@ -27185,14 +28039,21 @@ function setupSelection(engraver) {
|
|
|
27185
28039
|
}
|
|
27186
28040
|
}
|
|
27187
28041
|
|
|
27188
|
-
|
|
27189
|
-
|
|
27190
|
-
|
|
28042
|
+
for (var i = 0; i < svgs.length; i++) {
|
|
28043
|
+
svgs[i].addEventListener('touchstart', mouseDown.bind(engraver));
|
|
28044
|
+
svgs[i].addEventListener('touchmove', mouseMove.bind(engraver));
|
|
28045
|
+
svgs[i].addEventListener('touchend', mouseUp.bind(engraver));
|
|
28046
|
+
svgs[i].addEventListener('mousedown', mouseDown.bind(engraver));
|
|
28047
|
+
svgs[i].addEventListener('mousemove', mouseMove.bind(engraver));
|
|
28048
|
+
svgs[i].addEventListener('mouseup', mouseUp.bind(engraver));
|
|
28049
|
+
}
|
|
27191
28050
|
}
|
|
27192
28051
|
|
|
27193
|
-
function getCoord(ev
|
|
28052
|
+
function getCoord(ev) {
|
|
27194
28053
|
var scaleX = 1;
|
|
27195
|
-
var scaleY = 1;
|
|
28054
|
+
var scaleY = 1;
|
|
28055
|
+
var svg = ev.target.closest('svg');
|
|
28056
|
+
var yOffset = 0; // when renderer.options.responsive === 'resize' the click coords are in relation to the HTML
|
|
27196
28057
|
// element, we need to convert to the SVG viewBox coords
|
|
27197
28058
|
|
|
27198
28059
|
if (svg.viewBox.baseVal) {
|
|
@@ -27200,6 +28061,7 @@ function getCoord(ev, svg) {
|
|
|
27200
28061
|
// Chrome makes these values null when no viewBox is given.
|
|
27201
28062
|
if (svg.viewBox.baseVal.width !== 0) scaleX = svg.viewBox.baseVal.width / svg.clientWidth;
|
|
27202
28063
|
if (svg.viewBox.baseVal.height !== 0) scaleY = svg.viewBox.baseVal.height / svg.clientHeight;
|
|
28064
|
+
yOffset = svg.viewBox.baseVal.y;
|
|
27203
28065
|
}
|
|
27204
28066
|
|
|
27205
28067
|
var svgClicked = ev.target.tagName === "svg";
|
|
@@ -27217,7 +28079,7 @@ function getCoord(ev, svg) {
|
|
|
27217
28079
|
x = x * scaleX;
|
|
27218
28080
|
y = y * scaleY; //console.log(x, y)
|
|
27219
28081
|
|
|
27220
|
-
return [x, y];
|
|
28082
|
+
return [x, y + yOffset];
|
|
27221
28083
|
}
|
|
27222
28084
|
|
|
27223
28085
|
function elementFocused(ev) {
|
|
@@ -27298,7 +28160,7 @@ function keyboardSelection(ev) {
|
|
|
27298
28160
|
|
|
27299
28161
|
function findElementInHistory(selectables, el) {
|
|
27300
28162
|
for (var i = 0; i < selectables.length; i++) {
|
|
27301
|
-
if (el === selectables[i].svgEl) return i;
|
|
28163
|
+
if (el.dataset.index === selectables[i].svgEl.dataset.index) return i;
|
|
27302
28164
|
}
|
|
27303
28165
|
|
|
27304
28166
|
return -1;
|
|
@@ -27391,7 +28253,7 @@ function getMousePosition(self, ev) {
|
|
|
27391
28253
|
y = box[1]; //console.log("clicked on", clickedOn, x, y, self.selectables[clickedOn].svgEl.getBBox(), ev.target.getBBox());
|
|
27392
28254
|
} else {
|
|
27393
28255
|
// See if they clicked close to an element.
|
|
27394
|
-
box = getCoord(ev
|
|
28256
|
+
box = getCoord(ev);
|
|
27395
28257
|
x = box[0];
|
|
27396
28258
|
y = box[1];
|
|
27397
28259
|
clickedOn = findElementByCoord(self, x, y); //console.log("clicked near", clickedOn, x, y, printEl(ev.target));
|
|
@@ -27404,11 +28266,28 @@ function getMousePosition(self, ev) {
|
|
|
27404
28266
|
};
|
|
27405
28267
|
}
|
|
27406
28268
|
|
|
28269
|
+
function attachMissingTouchEventAttributes(touchEv) {
|
|
28270
|
+
var rect = touchEv.target.getBoundingClientRect();
|
|
28271
|
+
var offsetX = touchEv.touches[0].pageX - rect.left;
|
|
28272
|
+
var offsetY = touchEv.touches[0].pageY - rect.top;
|
|
28273
|
+
touchEv.touches[0].offsetX = offsetX;
|
|
28274
|
+
touchEv.touches[0].offsetY = offsetY;
|
|
28275
|
+
touchEv.touches[0].layerX = touchEv.touches[0].pageX;
|
|
28276
|
+
touchEv.touches[0].layerY = touchEv.touches[0].pageY;
|
|
28277
|
+
}
|
|
28278
|
+
|
|
27407
28279
|
function mouseDown(ev) {
|
|
27408
28280
|
// "this" is the EngraverController because of the bind(this) when setting the event listener.
|
|
27409
|
-
var
|
|
28281
|
+
var _ev = ev;
|
|
28282
|
+
|
|
28283
|
+
if (ev.type === 'touchstart') {
|
|
28284
|
+
attachMissingTouchEventAttributes(ev);
|
|
28285
|
+
_ev = ev.touches[0];
|
|
28286
|
+
}
|
|
28287
|
+
|
|
28288
|
+
var positioning = getMousePosition(this, _ev); // Only start dragging if the user clicked close enough to an element and clicked with the main mouse button.
|
|
27410
28289
|
|
|
27411
|
-
if (positioning.clickedOn >= 0 && ev.button === 0) {
|
|
28290
|
+
if (positioning.clickedOn >= 0 && (ev.type === 'touchstart' || ev.button === 0)) {
|
|
27412
28291
|
this.dragTarget = this.selectables[positioning.clickedOn];
|
|
27413
28292
|
this.dragIndex = positioning.clickedOn;
|
|
27414
28293
|
this.dragMechanism = "mouse";
|
|
@@ -27425,9 +28304,17 @@ function mouseDown(ev) {
|
|
|
27425
28304
|
}
|
|
27426
28305
|
|
|
27427
28306
|
function mouseMove(ev) {
|
|
27428
|
-
|
|
28307
|
+
var _ev = ev;
|
|
28308
|
+
|
|
28309
|
+
if (ev.type === 'touchmove') {
|
|
28310
|
+
attachMissingTouchEventAttributes(ev);
|
|
28311
|
+
_ev = ev.touches[0];
|
|
28312
|
+
}
|
|
28313
|
+
|
|
28314
|
+
this.lastTouchMove = ev; // "this" is the EngraverController because of the bind(this) when setting the event listener.
|
|
28315
|
+
|
|
27429
28316
|
if (!this.dragTarget || !this.dragging || !this.dragTarget.isDraggable || this.dragMechanism !== 'mouse') return;
|
|
27430
|
-
var positioning = getMousePosition(this,
|
|
28317
|
+
var positioning = getMousePosition(this, _ev);
|
|
27431
28318
|
var yDist = Math.round((positioning.y - this.dragMouseStart.y) / spacing.STEP);
|
|
27432
28319
|
|
|
27433
28320
|
if (yDist !== this.dragYStep) {
|
|
@@ -27438,6 +28325,13 @@ function mouseMove(ev) {
|
|
|
27438
28325
|
|
|
27439
28326
|
function mouseUp(ev) {
|
|
27440
28327
|
// "this" is the EngraverController because of the bind(this) when setting the event listener.
|
|
28328
|
+
var _ev = ev;
|
|
28329
|
+
|
|
28330
|
+
if (ev.type === 'touchend') {
|
|
28331
|
+
attachMissingTouchEventAttributes(this.lastTouchMove);
|
|
28332
|
+
_ev = this.lastTouchMove.touches[0];
|
|
28333
|
+
}
|
|
28334
|
+
|
|
27441
28335
|
if (!this.dragTarget) return;
|
|
27442
28336
|
clearSelection.bind(this)();
|
|
27443
28337
|
|
|
@@ -27446,7 +28340,7 @@ function mouseUp(ev) {
|
|
|
27446
28340
|
this.dragTarget.absEl.highlight(undefined, this.selectionColor);
|
|
27447
28341
|
}
|
|
27448
28342
|
|
|
27449
|
-
notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex,
|
|
28343
|
+
notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, _ev);
|
|
27450
28344
|
|
|
27451
28345
|
if (this.dragTarget.svgEl && this.dragTarget.svgEl.focus) {
|
|
27452
28346
|
this.dragTarget.svgEl.focus();
|
|
@@ -28175,7 +29069,7 @@ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, padd
|
|
|
28175
29069
|
font: 'infofont',
|
|
28176
29070
|
klass: 'meta-top rhythm',
|
|
28177
29071
|
absElemType: "rhythm",
|
|
28178
|
-
noMove:
|
|
29072
|
+
noMove: noMove,
|
|
28179
29073
|
info: metaTextInfo.rhythm,
|
|
28180
29074
|
name: "rhythm"
|
|
28181
29075
|
}, getTextSize);
|
|
@@ -28253,7 +29147,7 @@ module.exports = unhighlight;
|
|
|
28253
29147
|
\********************/
|
|
28254
29148
|
/***/ (function(module) {
|
|
28255
29149
|
|
|
28256
|
-
var version = '6.
|
|
29150
|
+
var version = '6.1.1';
|
|
28257
29151
|
module.exports = version;
|
|
28258
29152
|
|
|
28259
29153
|
/***/ })
|