abcjs 6.0.2 → 6.1.0

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.
@@ -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
 
@@ -1294,6 +1297,262 @@ module.exports = renderAbc;
1294
1297
 
1295
1298
  /***/ }),
1296
1299
 
1300
+ /***/ "./src/const/key-accidentals.js":
1301
+ /*!**************************************!*\
1302
+ !*** ./src/const/key-accidentals.js ***!
1303
+ \**************************************/
1304
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
1305
+
1306
+ var _require = __webpack_require__(/*! ./relative-major */ "./src/const/relative-major.js"),
1307
+ relativeMajor = _require.relativeMajor;
1308
+
1309
+ var key1sharp = {
1310
+ acc: 'sharp',
1311
+ note: 'f'
1312
+ };
1313
+ var key2sharp = {
1314
+ acc: 'sharp',
1315
+ note: 'c'
1316
+ };
1317
+ var key3sharp = {
1318
+ acc: 'sharp',
1319
+ note: 'g'
1320
+ };
1321
+ var key4sharp = {
1322
+ acc: 'sharp',
1323
+ note: 'd'
1324
+ };
1325
+ var key5sharp = {
1326
+ acc: 'sharp',
1327
+ note: 'A'
1328
+ };
1329
+ var key6sharp = {
1330
+ acc: 'sharp',
1331
+ note: 'e'
1332
+ };
1333
+ var key7sharp = {
1334
+ acc: 'sharp',
1335
+ note: 'B'
1336
+ };
1337
+ var key1flat = {
1338
+ acc: 'flat',
1339
+ note: 'B'
1340
+ };
1341
+ var key2flat = {
1342
+ acc: 'flat',
1343
+ note: 'e'
1344
+ };
1345
+ var key3flat = {
1346
+ acc: 'flat',
1347
+ note: 'A'
1348
+ };
1349
+ var key4flat = {
1350
+ acc: 'flat',
1351
+ note: 'd'
1352
+ };
1353
+ var key5flat = {
1354
+ acc: 'flat',
1355
+ note: 'G'
1356
+ };
1357
+ var key6flat = {
1358
+ acc: 'flat',
1359
+ note: 'c'
1360
+ };
1361
+ var key7flat = {
1362
+ acc: 'flat',
1363
+ note: 'F'
1364
+ };
1365
+ var keys = {
1366
+ 'C#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
1367
+ 'F#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
1368
+ 'B': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
1369
+ 'E': [key1sharp, key2sharp, key3sharp, key4sharp],
1370
+ 'A': [key1sharp, key2sharp, key3sharp],
1371
+ 'D': [key1sharp, key2sharp],
1372
+ 'G': [key1sharp],
1373
+ 'C': [],
1374
+ 'F': [key1flat],
1375
+ 'Bb': [key1flat, key2flat],
1376
+ 'Eb': [key1flat, key2flat, key3flat],
1377
+ 'Cm': [key1flat, key2flat, key3flat],
1378
+ 'Ab': [key1flat, key2flat, key3flat, key4flat],
1379
+ 'Db': [key1flat, key2flat, key3flat, key4flat, key5flat],
1380
+ 'Gb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
1381
+ 'Cb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
1382
+ // The following are not in the 2.0 spec, but seem normal enough.
1383
+ // TODO-PER: These SOUND the same as what's written, but they aren't right
1384
+ 'A#': [key1flat, key2flat],
1385
+ 'B#': [],
1386
+ 'D#': [key1flat, key2flat, key3flat],
1387
+ 'E#': [key1flat],
1388
+ 'G#': [key1flat, key2flat, key3flat, key4flat],
1389
+ 'none': []
1390
+ };
1391
+
1392
+ function keyAccidentals(key) {
1393
+ var newKey = keys[relativeMajor(key)];
1394
+ if (!newKey) // If we don't recognize the key then there is no change
1395
+ return null;
1396
+ return JSON.parse(JSON.stringify(newKey));
1397
+ }
1398
+
1399
+ ;
1400
+ module.exports = keyAccidentals;
1401
+
1402
+ /***/ }),
1403
+
1404
+ /***/ "./src/const/relative-major.js":
1405
+ /*!*************************************!*\
1406
+ !*** ./src/const/relative-major.js ***!
1407
+ \*************************************/
1408
+ /***/ (function(module) {
1409
+
1410
+ // All these keys have the same number of accidentals
1411
+ var keys = {
1412
+ 'C': {
1413
+ modes: ['CMaj', 'Amin', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'],
1414
+ stepsFromC: 0
1415
+ },
1416
+ 'Db': {
1417
+ modes: ['DbMaj', 'Bbmin', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'],
1418
+ stepsFromC: 1
1419
+ },
1420
+ 'D': {
1421
+ modes: ['DMaj', 'Bmin', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'],
1422
+ stepsFromC: 2
1423
+ },
1424
+ 'Eb': {
1425
+ modes: ['EbMaj', 'Cmin', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'],
1426
+ stepsFromC: 3
1427
+ },
1428
+ 'E': {
1429
+ modes: ['EMaj', 'C#min', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'],
1430
+ stepsFromC: 4
1431
+ },
1432
+ 'F': {
1433
+ modes: ['FMaj', 'Dmin', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'],
1434
+ stepsFromC: 5
1435
+ },
1436
+ 'Gb': {
1437
+ modes: ['GbMaj', 'Ebmin', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'],
1438
+ stepsFromC: 6
1439
+ },
1440
+ 'G': {
1441
+ modes: ['GMaj', 'Emin', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'],
1442
+ stepsFromC: 7
1443
+ },
1444
+ 'Ab': {
1445
+ modes: ['AbMaj', 'Fmin', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'],
1446
+ stepsFromC: 8
1447
+ },
1448
+ 'A': {
1449
+ modes: ['AMaj', 'F#min', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'],
1450
+ stepsFromC: 9
1451
+ },
1452
+ 'Bb': {
1453
+ modes: ['BbMaj', 'Gmin', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'],
1454
+ stepsFromC: 10
1455
+ },
1456
+ 'B': {
1457
+ modes: ['BMaj', 'G#min', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'],
1458
+ stepsFromC: 11
1459
+ },
1460
+ // Enharmonic keys
1461
+ 'C#': {
1462
+ modes: ['C#Maj', 'A#min', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'],
1463
+ stepsFromC: 1
1464
+ },
1465
+ 'F#': {
1466
+ modes: ['F#Maj', 'D#min', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'],
1467
+ stepsFromC: 6
1468
+ },
1469
+ 'Cb': {
1470
+ modes: ['CbMaj', 'Abmin', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'],
1471
+ stepsFromC: 11
1472
+ }
1473
+ };
1474
+ var keyReverse = null;
1475
+
1476
+ function createKeyReverse() {
1477
+ keyReverse = {};
1478
+ var allKeys = Object.keys(keys);
1479
+
1480
+ for (var i = 0; i < allKeys.length; i++) {
1481
+ var keyObj = keys[allKeys[i]];
1482
+ keyReverse[allKeys[i].toLowerCase()] = allKeys[i];
1483
+
1484
+ for (var j = 0; j < keyObj.modes.length; j++) {
1485
+ var mode = keyObj.modes[j].toLowerCase();
1486
+ keyReverse[mode] = allKeys[i];
1487
+ }
1488
+ }
1489
+ }
1490
+
1491
+ function relativeMajor(key) {
1492
+ // Translate a key to its relative major. If it doesn't exist, do the best we can
1493
+ // by just returning the original key.
1494
+ // There are alternate spellings of these - so the search needs to be case insensitive.
1495
+ // To make this efficient, the first time this is called the "keys" object is reversed so this search is fast in the future
1496
+ if (!keyReverse) {
1497
+ createKeyReverse();
1498
+ } // get the key portion itself - there might be other stuff, like extra sharps and flats, or the mode written out.
1499
+
1500
+
1501
+ var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|min|mix|dor|phr|lyd|loc|m)?/);
1502
+ if (!mode || !mode[2]) return key;
1503
+ mode = mode[1] + mode[2];
1504
+ var maj = keyReverse[mode];
1505
+ if (maj) return maj;
1506
+ return key;
1507
+ }
1508
+
1509
+ function relativeMode(majorKey, mode) {
1510
+ // The reverse of the relativeMajor. Translate it back to the original mode.
1511
+ // If it isn't a recognized mode or it is already major, then just return the major key.
1512
+ var group = keys[majorKey];
1513
+ if (!group) return majorKey;
1514
+ if (mode === '') return majorKey;
1515
+ var match = mode.toLowerCase().match(/^(maj|min|mix|dor|phr|lyd|loc|m)/);
1516
+ if (!match) return majorKey;
1517
+ var regMode = match[1];
1518
+
1519
+ for (var i = 0; i < group.modes.length; i++) {
1520
+ var thisMode = group.modes[i];
1521
+ var ind = thisMode.toLowerCase().indexOf(regMode);
1522
+ if (ind !== -1 && ind === thisMode.length - regMode.length) return thisMode.substring(0, thisMode.length - regMode.length);
1523
+ }
1524
+
1525
+ return majorKey;
1526
+ }
1527
+
1528
+ function transposeKey(key, steps) {
1529
+ // This takes a major key and adds the desired steps.
1530
+ // It assigns each key a number that is the number of steps from C so that there can just be arithmetic.
1531
+ var match = keys[key];
1532
+ if (!match) return key;
1533
+
1534
+ while (steps < 0) {
1535
+ steps += 12;
1536
+ }
1537
+
1538
+ var fromC = (match.stepsFromC + steps) % 12;
1539
+
1540
+ for (var i = 0; i < Object.keys(keys).length; i++) {
1541
+ var k = Object.keys(keys)[i];
1542
+ if (keys[k].stepsFromC === fromC) return k;
1543
+ }
1544
+
1545
+ return key;
1546
+ }
1547
+
1548
+ module.exports = {
1549
+ relativeMajor: relativeMajor,
1550
+ relativeMode: relativeMode,
1551
+ transposeKey: transposeKey
1552
+ };
1553
+
1554
+ /***/ }),
1555
+
1297
1556
  /***/ "./src/data/abc_tune.js":
1298
1557
  /*!******************************!*\
1299
1558
  !*** ./src/data/abc_tune.js ***!
@@ -3232,6 +3491,7 @@ var Parse = function Parse() {
3232
3491
 
3233
3492
  var addWord = function addWord(i) {
3234
3493
  var word = parseCommon.strip(words.substring(last_divider, i));
3494
+ word = word.replace(/\\([-_*|~])/g, '$1');
3235
3495
  last_divider = i + 1;
3236
3496
 
3237
3497
  if (word.length > 0) {
@@ -3249,15 +3509,17 @@ var Parse = function Parse() {
3249
3509
  return false;
3250
3510
  };
3251
3511
 
3512
+ var escNext = false;
3513
+
3252
3514
  for (var i = 0; i < words.length; i++) {
3253
- switch (words.charAt(i)) {
3515
+ switch (words[i]) {
3254
3516
  case ' ':
3255
3517
  case '\x12':
3256
3518
  addWord(i);
3257
3519
  break;
3258
3520
 
3259
3521
  case '-':
3260
- if (!addWord(i) && word_list.length > 0) {
3522
+ if (!escNext && !addWord(i) && word_list.length > 0) {
3261
3523
  parseCommon.last(word_list).divider = '-';
3262
3524
  word_list.push({
3263
3525
  skip: true,
@@ -3268,33 +3530,47 @@ var Parse = function Parse() {
3268
3530
  break;
3269
3531
 
3270
3532
  case '_':
3271
- addWord(i);
3272
- word_list.push({
3273
- skip: true,
3274
- to: 'slur'
3275
- });
3533
+ if (!escNext) {
3534
+ addWord(i);
3535
+ word_list.push({
3536
+ skip: true,
3537
+ to: 'slur'
3538
+ });
3539
+ }
3540
+
3276
3541
  break;
3277
3542
 
3278
3543
  case '*':
3279
- addWord(i);
3280
- word_list.push({
3281
- skip: true,
3282
- to: 'next'
3283
- });
3544
+ if (!escNext) {
3545
+ addWord(i);
3546
+ word_list.push({
3547
+ skip: true,
3548
+ to: 'next'
3549
+ });
3550
+ }
3551
+
3284
3552
  break;
3285
3553
 
3286
3554
  case '|':
3287
- addWord(i);
3288
- word_list.push({
3289
- skip: true,
3290
- to: 'bar'
3291
- });
3555
+ if (!escNext) {
3556
+ addWord(i);
3557
+ word_list.push({
3558
+ skip: true,
3559
+ to: 'bar'
3560
+ });
3561
+ }
3562
+
3292
3563
  break;
3293
3564
 
3294
3565
  case '~':
3295
- replace = true;
3566
+ if (!escNext) {
3567
+ replace = true;
3568
+ }
3569
+
3296
3570
  break;
3297
3571
  }
3572
+
3573
+ escNext = words[i] === '\\';
3298
3574
  }
3299
3575
 
3300
3576
  var inSlur = false;
@@ -3678,15 +3954,16 @@ var bookParser = function bookParser(book) {
3678
3954
  "use strict";
3679
3955
 
3680
3956
  var directives = "";
3957
+ var initialWhiteSpace = book.match(/(\s*)/);
3681
3958
  book = parseCommon.strip(book);
3682
3959
  var tuneStrings = book.split("\nX:"); // Put back the X: that we lost when splitting the tunes.
3683
3960
 
3684
3961
  for (var i = 1; i < tuneStrings.length; i++) {
3685
3962
  tuneStrings[i] = "X:" + tuneStrings[i];
3686
- } // Keep track of the character position each tune starts with.
3963
+ } // Keep track of the character position each tune starts with. If the string starts with white space, count that, too.
3687
3964
 
3688
3965
 
3689
- var pos = 0;
3966
+ var pos = initialWhiteSpace ? initialWhiteSpace[0].length : 0;
3690
3967
  var tunes = [];
3691
3968
  parseCommon.each(tuneStrings, function (tune) {
3692
3969
  tunes.push({
@@ -5709,7 +5986,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5709
5986
  return [e - i + 1 + ws];
5710
5987
 
5711
5988
  case "[K:":
5712
- var result = parseKeyVoice.parseKey(line.substring(i + 3, e));
5989
+ var result = parseKeyVoice.parseKey(line.substring(i + 3, e), true);
5713
5990
  if (result.foundClef && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('clef', startChar, endChar, multilineVars.clef);
5714
5991
  if (result.foundKey && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('key', startChar, endChar, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
5715
5992
  return [e - i + 1 + ws];
@@ -5774,7 +6051,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5774
6051
  return [line.length];
5775
6052
 
5776
6053
  case "K:":
5777
- var result = parseKeyVoice.parseKey(line.substring(i + 2));
6054
+ var result = parseKeyVoice.parseKey(line.substring(i + 2), tuneBuilder.hasBeginMusic());
5778
6055
  if (result.foundClef && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('clef', multilineVars.iChar + i, multilineVars.iChar + line.length, multilineVars.clef);
5779
6056
  if (result.foundKey && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('key', multilineVars.iChar + i, multilineVars.iChar + line.length, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
5780
6057
  return [line.length];
@@ -5863,7 +6140,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5863
6140
  case 'K':
5864
6141
  // since the key is the last thing that can happen in the header, we can resolve the tempo now
5865
6142
  this.resolveTempo();
5866
- var result = parseKeyVoice.parseKey(line.substring(2));
6143
+ var result = parseKeyVoice.parseKey(line.substring(2), false);
5867
6144
 
5868
6145
  if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic()) {
5869
6146
  if (result.foundClef) tuneBuilder.appendStartingElement('clef', startChar, endChar, multilineVars.clef);
@@ -5979,179 +6256,7 @@ var parseKeyVoice = {};
5979
6256
  };
5980
6257
 
5981
6258
  parseKeyVoice.standardKey = function (keyName, root, acc, localTranspose) {
5982
- var key1sharp = {
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);
6259
+ return transpose.keySignature(multilineVars, keyName, root, acc, localTranspose);
6155
6260
  };
6156
6261
 
6157
6262
  var clefLines = {
@@ -6487,7 +6592,7 @@ var parseKeyVoice = {};
6487
6592
  }
6488
6593
  };
6489
6594
 
6490
- parseKeyVoice.parseKey = function (str) // (and clef)
6595
+ parseKeyVoice.parseKey = function (str, isInline) // (and clef)
6491
6596
  {
6492
6597
  // returns:
6493
6598
  // { foundClef: true, foundKey: true }
@@ -6605,8 +6710,12 @@ var parseKeyVoice = {};
6605
6710
 
6606
6711
  var oldKey = parseKeyVoice.deepCopyKey(multilineVars.key); //TODO-PER: HACK! To get the local transpose to work, the transposition is done for each line. This caused the global transposition variable to be factored in twice, so, instead of rewriting that right now, I'm just subtracting one of them here.
6607
6712
 
6608
- var keyCompensate = multilineVars.globalTranspose ? -multilineVars.globalTranspose : 0;
6713
+ var keyCompensate = !isInline && multilineVars.globalTranspose ? -multilineVars.globalTranspose : 0; //console.log("parse", JSON.stringify(multilineVars), isInline)
6714
+
6715
+ var savedOrigKey;
6716
+ if (isInline) savedOrigKey = multilineVars.globalTransposeOrigKeySig;
6609
6717
  multilineVars.key = parseKeyVoice.deepCopyKey(parseKeyVoice.standardKey(key, retPitch.token, acc, keyCompensate));
6718
+ if (isInline) multilineVars.globalTransposeOrigKeySig = savedOrigKey;
6610
6719
  multilineVars.key.mode = mode;
6611
6720
 
6612
6721
  if (oldKey) {
@@ -7542,7 +7651,7 @@ MusicParser.prototype.parseMusic = function (line) {
7542
7651
  }
7543
7652
 
7544
7653
  multilineVars.addFormattingOptions(el, tune.formatting, 'bar');
7545
- tuneBuilder.appendElement('bar', startOfLine + i, startOfLine + i + ret[0], bar);
7654
+ tuneBuilder.appendElement('bar', startOfLine + startI, startOfLine + i + ret[0], bar);
7546
7655
  multilineVars.measureNotEmpty = false;
7547
7656
  el = {};
7548
7657
  }
@@ -7981,7 +8090,7 @@ function durationOfMeasure(multilineVars) {
7981
8090
 
7982
8091
  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"];
7983
8092
  var volumeDecorations = ["p", "pp", "f", "ff", "mf", "mp", "ppp", "pppp", "fff", "ffff", "sfz"];
7984
- var dynamicDecorations = ["crescendo(", "crescendo)", "diminuendo(", "diminuendo)"];
8093
+ var dynamicDecorations = ["crescendo(", "crescendo)", "diminuendo(", "diminuendo)", "glissando(", "glissando)"];
7985
8094
  var accentPseudonyms = [["<", "accent"], [">", "accent"], ["tr", "trill"], ["plus", "+"], ["emphasis", "accent"], ["^", "umarcato"], ["marcato", "umarcato"]];
7986
8095
  var accentDynamicPseudonyms = [["<(", "crescendo("], ["<)", "crescendo)"], [">(", "diminuendo("], [">)", "diminuendo)"]];
7987
8096
 
@@ -10039,6 +10148,12 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
10039
10148
  value: parseFloat(num)
10040
10149
  };
10041
10150
 
10151
+ case 'px':
10152
+ return {
10153
+ used: used + 1,
10154
+ value: parseFloat(num)
10155
+ };
10156
+
10042
10157
  case 'cm':
10043
10158
  return {
10044
10159
  used: used + 1,
@@ -10058,17 +10173,11 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
10058
10173
  value: parseFloat(num)
10059
10174
  };
10060
10175
  }
10061
-
10062
- return {
10063
- used: 0
10064
- };
10065
10176
  };
10066
10177
 
10067
10178
  var substInChord = function substInChord(str) {
10068
- while (str.indexOf("\\n") !== -1) {
10069
- str = str.replace("\\n", "\n");
10070
- }
10071
-
10179
+ str = str.replace(/\\n/g, "\n");
10180
+ str = str.replace(/\\"/g, '"');
10072
10181
  return str;
10073
10182
  };
10074
10183
 
@@ -10084,8 +10193,10 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
10084
10193
  var matchChar = _matchChar || line.charAt(i);
10085
10194
 
10086
10195
  var pos = i + 1;
10196
+ var esc = false;
10087
10197
 
10088
- while (pos < line.length && line.charAt(pos) !== matchChar) {
10198
+ while (pos < line.length && (esc || line[pos] !== matchChar)) {
10199
+ esc = line[pos] === '\\';
10089
10200
  ++pos;
10090
10201
  }
10091
10202
 
@@ -10124,9 +10235,15 @@ module.exports = Tokenizer;
10124
10235
  /*!************************************!*\
10125
10236
  !*** ./src/parse/abc_transpose.js ***!
10126
10237
  \************************************/
10127
- /***/ (function(module) {
10238
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
10128
10239
 
10129
10240
  // abc_transpose.js: Handles the automatic transposition of key signatures, chord symbols, and notes.
10241
+ var allNotes = __webpack_require__(/*! ./all-notes */ "./src/parse/all-notes.js");
10242
+
10243
+ var transposeChordName = __webpack_require__(/*! ../parse/transpose-chord */ "./src/parse/transpose-chord.js");
10244
+
10245
+ var keyAccidentals = __webpack_require__(/*! ../const/key-accidentals */ "./src/const/key-accidentals.js");
10246
+
10130
10247
  var transpose = {};
10131
10248
  var keyIndex = {
10132
10249
  'C': 0,
@@ -10150,16 +10267,16 @@ var keyIndex = {
10150
10267
  var newKey = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B'];
10151
10268
  var newKeyMinor = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'Bb', 'B'];
10152
10269
 
10153
- transpose.keySignature = function (multilineVars, keys, keyName, root, acc, localTranspose) {
10154
- if (multilineVars.clef.type === "perc") return {
10155
- accidentals: keys[keyName],
10270
+ transpose.keySignature = function (multilineVars, keyName, root, acc, localTranspose) {
10271
+ if (multilineVars.clef.type === "perc" || multilineVars.clef.type === "none") return {
10272
+ accidentals: keyAccidentals(keyName),
10156
10273
  root: root,
10157
10274
  acc: acc
10158
10275
  };
10159
10276
  if (!localTranspose) localTranspose = 0;
10160
10277
  multilineVars.localTransposeVerticalMovement = 0;
10161
10278
  multilineVars.localTransposePreferFlats = false;
10162
- var k = keys[keyName];
10279
+ var k = keyAccidentals(keyName);
10163
10280
  if (!k) return multilineVars.key; // If the key isn't in the list, it is non-standard. We won't attempt to transpose it.
10164
10281
 
10165
10282
  multilineVars.localTranspose = (multilineVars.globalTranspose ? multilineVars.globalTranspose : 0) + localTranspose;
@@ -10195,7 +10312,7 @@ transpose.keySignature = function (multilineVars, keys, keyName, root, acc, loca
10195
10312
  if (index > 11) index = index % 12;
10196
10313
  var newKeyName = keyName[0] === 'm' ? newKeyMinor[index] : newKey[index];
10197
10314
  var transposedKey = newKeyName + keyName;
10198
- var newKeySig = keys[transposedKey];
10315
+ var newKeySig = keyAccidentals(transposedKey);
10199
10316
  if (newKeySig.length > 0 && newKeySig[0].acc === 'flat') multilineVars.localTransposePreferFlats = true;
10200
10317
  var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
10201
10318
 
@@ -10224,76 +10341,8 @@ transpose.keySignature = function (multilineVars, keys, keyName, root, acc, loca
10224
10341
  };
10225
10342
  };
10226
10343
 
10227
- var sharpChords = ['C', 'C♯', 'D', "D♯", 'E', 'F', "F♯", 'G', 'G♯', 'A', 'A♯', 'B'];
10228
- var flatChords = ['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B'];
10229
- var sharpChordsFree = ['C', 'C#', 'D', "D#", 'E', 'F', "F#", 'G', 'G#', 'A', 'A#', 'B'];
10230
- var flatChordsFree = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
10231
-
10232
10344
  transpose.chordName = function (multilineVars, chord) {
10233
- if (multilineVars.localTranspose && multilineVars.localTranspose % 12 !== 0) {
10234
- // The chords are the same if it is an exact octave change.
10235
- var transposeFactor = multilineVars.localTranspose;
10236
-
10237
- while (transposeFactor < 0) {
10238
- transposeFactor += 12;
10239
- }
10240
-
10241
- if (transposeFactor > 11) transposeFactor = transposeFactor % 12;
10242
-
10243
- if (multilineVars.freegchord) {
10244
- chord = chord.replace(/Cb/g, "`~11`");
10245
- chord = chord.replace(/Db/g, "`~1`");
10246
- chord = chord.replace(/Eb/g, "`~3`");
10247
- chord = chord.replace(/Fb/g, "`~4`");
10248
- chord = chord.replace(/Gb/g, "`~6`");
10249
- chord = chord.replace(/Ab/g, "`~8`");
10250
- chord = chord.replace(/Bb/g, "`~10`");
10251
- chord = chord.replace(/C#/g, "`~1`");
10252
- chord = chord.replace(/D#/g, "`~3`");
10253
- chord = chord.replace(/E#/g, "`~5`");
10254
- chord = chord.replace(/F#/g, "`~6`");
10255
- chord = chord.replace(/G#/g, "`~8`");
10256
- chord = chord.replace(/A#/g, "`~10`");
10257
- chord = chord.replace(/B#/g, "`~0`");
10258
- } else {
10259
- chord = chord.replace(/C♭/g, "`~11`");
10260
- chord = chord.replace(/D♭/g, "`~1`");
10261
- chord = chord.replace(/E♭/g, "`~3`");
10262
- chord = chord.replace(/F♭/g, "`~4`");
10263
- chord = chord.replace(/G♭/g, "`~6`");
10264
- chord = chord.replace(/A♭/g, "`~8`");
10265
- chord = chord.replace(/B♭/g, "`~10`");
10266
- chord = chord.replace(/C♯/g, "`~1`");
10267
- chord = chord.replace(/D♯/g, "`~3`");
10268
- chord = chord.replace(/E♯/g, "`~5`");
10269
- chord = chord.replace(/F♯/g, "`~6`");
10270
- chord = chord.replace(/G♯/g, "`~8`");
10271
- chord = chord.replace(/A♯/g, "`~10`");
10272
- chord = chord.replace(/B♯/g, "`~0`");
10273
- }
10274
-
10275
- chord = chord.replace(/C/g, "`~0`");
10276
- chord = chord.replace(/D/g, "`~2`");
10277
- chord = chord.replace(/E/g, "`~4`");
10278
- chord = chord.replace(/F/g, "`~5`");
10279
- chord = chord.replace(/G/g, "`~7`");
10280
- chord = chord.replace(/A/g, "`~9`");
10281
- chord = chord.replace(/B/g, "`~11`");
10282
- var arr = chord.split("`");
10283
-
10284
- for (var i = 0; i < arr.length; i++) {
10285
- if (arr[i][0] === '~') {
10286
- var chordNum = parseInt(arr[i].substr(1), 10);
10287
- chordNum += transposeFactor;
10288
- if (chordNum > 11) chordNum -= 12;
10289
- if (multilineVars.freegchord) arr[i] = multilineVars.localTransposePreferFlats ? flatChordsFree[chordNum] : sharpChordsFree[chordNum];else arr[i] = multilineVars.localTransposePreferFlats ? flatChords[chordNum] : sharpChords[chordNum];
10290
- }
10291
- }
10292
-
10293
- chord = arr.join("");
10294
- }
10295
-
10296
- return chord;
10345
+ return transposeChordName(chord, multilineVars.localTranspose, multilineVars.localTransposePreferFlats, multilineVars.freegchord);
10297
10346
  };
10298
10347
 
10299
10348
  var pitchToLetter = ['c', 'd', 'e', 'f', 'g', 'a', 'b'];
@@ -10346,19 +10395,42 @@ var accidentals2 = {
10346
10395
  "1": "sharp",
10347
10396
  "2": "dblsharp"
10348
10397
  };
10398
+ var accidentals3 = {
10399
+ "-2": "__",
10400
+ "-1": "_",
10401
+ "0": "=",
10402
+ "1": "^",
10403
+ "2": "^^"
10404
+ };
10405
+ var count = 0;
10349
10406
 
10350
10407
  transpose.note = function (multilineVars, el) {
10351
- // the "el" that is passed in has el.accidental, and el.pitch. "pitch" is the vertical position (0=middle C)
10408
+ // the "el" that is passed in has el.name, el.accidental, and el.pitch. "pitch" is the vertical position (0=middle C)
10352
10409
  // localTranspose is the number of half steps
10353
10410
  // localTransposeVerticalMovement is the vertical distance to move.
10411
+ //console.log(count++,multilineVars.localTranspose, el)
10354
10412
  if (!multilineVars.localTranspose || multilineVars.clef.type === "perc") return;
10355
10413
  var origPitch = el.pitch;
10356
- el.pitch = el.pitch + multilineVars.localTransposeVerticalMovement;
10414
+
10415
+ if (multilineVars.localTransposeVerticalMovement) {
10416
+ el.pitch = el.pitch + multilineVars.localTransposeVerticalMovement;
10417
+
10418
+ if (el.name) {
10419
+ var actual = el.accidental ? el.name.substring(1) : el.name;
10420
+ var acc = el.accidental ? el.name[0] : '';
10421
+ var p = allNotes.pitchIndex(actual);
10422
+ el.name = acc + allNotes.noteName(p + multilineVars.localTransposeVerticalMovement);
10423
+ }
10424
+ }
10357
10425
 
10358
10426
  if (el.accidental) {
10359
10427
  var ret = accidentalChange(origPitch, el.pitch, el.accidental, multilineVars.globalTransposeOrigKeySig, multilineVars.targetKey);
10360
10428
  el.pitch = ret[0];
10361
10429
  el.accidental = accidentals2[ret[1]];
10430
+
10431
+ if (el.name) {
10432
+ el.name = accidentals3[ret[1]] + el.name.replace(/[_^=]/g, '');
10433
+ }
10362
10434
  }
10363
10435
  };
10364
10436
 
@@ -10366,6 +10438,104 @@ module.exports = transpose;
10366
10438
 
10367
10439
  /***/ }),
10368
10440
 
10441
+ /***/ "./src/parse/all-notes.js":
10442
+ /*!********************************!*\
10443
+ !*** ./src/parse/all-notes.js ***!
10444
+ \********************************/
10445
+ /***/ (function(module) {
10446
+
10447
+ var allNotes = {};
10448
+ var allPitches = ['C,,,', 'D,,,', 'E,,,', 'F,,,', 'G,,,', 'A,,,', 'B,,,', 'C,,', 'D,,', 'E,,', 'F,,', 'G,,', 'A,,', 'B,,', 'C,', 'D,', 'E,', 'F,', 'G,', 'A,', 'B,', 'C', 'D', 'E', 'F', 'G', 'A', 'B', 'c', 'd', 'e', 'f', 'g', 'a', 'b', "c'", "d'", "e'", "f'", "g'", "a'", "b'", "c''", "d''", "e''", "f''", "g''", "a''", "b''", "c'''", "d'''", "e'''", "f'''", "g'''", "a'''", "b'''"];
10449
+
10450
+ allNotes.pitchIndex = function (noteName) {
10451
+ return allPitches.indexOf(noteName);
10452
+ };
10453
+
10454
+ allNotes.noteName = function (pitchIndex) {
10455
+ return allPitches[pitchIndex];
10456
+ };
10457
+
10458
+ module.exports = allNotes;
10459
+
10460
+ /***/ }),
10461
+
10462
+ /***/ "./src/parse/transpose-chord.js":
10463
+ /*!**************************************!*\
10464
+ !*** ./src/parse/transpose-chord.js ***!
10465
+ \**************************************/
10466
+ /***/ (function(module) {
10467
+
10468
+ var sharpChords = ['C', 'C♯', 'D', "D♯", 'E', 'F', "F♯", 'G', 'G♯', 'A', 'A♯', 'B'];
10469
+ var flatChords = ['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B'];
10470
+ var sharpChordsFree = ['C', 'C#', 'D', "D#", 'E', 'F', "F#", 'G', 'G#', 'A', 'A#', 'B'];
10471
+ var flatChordsFree = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
10472
+
10473
+ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
10474
+ if (!steps || steps % 12 === 0) // The chords are the same if it is an exact octave change.
10475
+ return chord; // There are two things in the chord that might need to be transposed:
10476
+ // The chord will start with a letter from A-G, and might have one accidental after it.
10477
+ // That accidental might be an actual sharp or flat char, or it might be a pound sign or lower case "b".
10478
+ // 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.
10479
+ // 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.
10480
+
10481
+ while (steps < 0) {
10482
+ steps += 12;
10483
+ }
10484
+
10485
+ if (steps > 11) steps = steps % 12; // (chord name w/accidental) (a bunch of stuff) (/) (bass note) (anything else)
10486
+
10487
+ var match = chord.match(/^([A-G][b#♭♯]?)([^\/]+)?\/?([A-G][b#♭♯]?)?(.+)?/);
10488
+ if (!match) return chord; // We don't recognize the format of the chord, so skip it.
10489
+
10490
+ var name = match[1];
10491
+ var extra1 = match[2];
10492
+ var bass = match[3];
10493
+ var extra2 = match[4];
10494
+ var index = sharpChords.indexOf(name);
10495
+ if (index < 0) index = flatChords.indexOf(name);
10496
+ if (index < 0) index = sharpChordsFree.indexOf(name);
10497
+ if (index < 0) index = flatChordsFree.indexOf(name);
10498
+ if (index < 0) return chord; // This should never happen, but if we can't find the chord just bail.
10499
+
10500
+ index += steps;
10501
+ index = index % 12;
10502
+
10503
+ if (preferFlats) {
10504
+ if (freeGCchord) chord = flatChordsFree[index];else chord = flatChords[index];
10505
+ } else {
10506
+ if (freeGCchord) chord = sharpChordsFree[index];else chord = sharpChords[index];
10507
+ }
10508
+
10509
+ if (extra1) chord += extra1;
10510
+
10511
+ if (bass) {
10512
+ var index = sharpChords.indexOf(bass);
10513
+ if (index < 0) index = flatChords.indexOf(bass);
10514
+ if (index < 0) index = sharpChordsFree.indexOf(bass);
10515
+ if (index < 0) index = flatChordsFree.indexOf(bass);
10516
+ chord += '/';
10517
+
10518
+ if (index >= 0) {
10519
+ index += steps;
10520
+ index = index % 12;
10521
+
10522
+ if (preferFlats) {
10523
+ if (freeGCchord) chord += flatChordsFree[index];else chord += flatChords[index];
10524
+ } else {
10525
+ if (freeGCchord) chord += sharpChordsFree[index];else chord += sharpChords[index];
10526
+ }
10527
+ } else chord += bass; // Don't know what to do so do nothing
10528
+
10529
+ }
10530
+
10531
+ if (extra2) chord += extra2;
10532
+ return chord;
10533
+ }
10534
+
10535
+ module.exports = transposeChordName;
10536
+
10537
+ /***/ }),
10538
+
10369
10539
  /***/ "./src/parse/tune-builder.js":
10370
10540
  /*!***********************************!*\
10371
10541
  !*** ./src/parse/tune-builder.js ***!
@@ -11804,141 +11974,710 @@ function fixedMeasureLineBreaks(widths, lineBreakPoint, preferredMeasuresPerLine
11804
11974
  var thisWidth = 0;
11805
11975
  var failed = false;
11806
11976
 
11807
- for (var i = 0; i < widths.length; i++) {
11808
- thisWidth += widths[i];
11977
+ for (var i = 0; i < widths.length; i++) {
11978
+ thisWidth += widths[i];
11979
+
11980
+ if (thisWidth > lineBreakPoint) {
11981
+ failed = true;
11982
+ }
11983
+
11984
+ if (i % preferredMeasuresPerLine === preferredMeasuresPerLine - 1) {
11985
+ if (i !== widths.length - 1) // Don't bother putting a line break for the last line - it's already a break.
11986
+ lineBreaks.push(i);
11987
+ totals.push(Math.round(thisWidth));
11988
+ thisWidth = 0;
11989
+ }
11990
+ }
11991
+
11992
+ return {
11993
+ failed: failed,
11994
+ totals: totals,
11995
+ lineBreaks: lineBreaks
11996
+ };
11997
+ }
11998
+
11999
+ function getRevisedTuneParams(lineBreaks, staffWidth, params) {
12000
+ var revisedParams = {
12001
+ lineBreaks: lineBreaks,
12002
+ staffwidth: staffWidth
12003
+ };
12004
+
12005
+ for (var key in params) {
12006
+ if (params.hasOwnProperty(key) && key !== 'wrap' && key !== 'staffwidth') {
12007
+ revisedParams[key] = params[key];
12008
+ }
12009
+ }
12010
+
12011
+ return {
12012
+ revisedParams: revisedParams
12013
+ };
12014
+ }
12015
+
12016
+ function calcLineWraps(tune, widths, params) {
12017
+ // 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
12018
+ // by the minimum spacing instead of multiplying the min spacing later.
12019
+ // The scaling works differently: this is done by changing the scaling of the outer SVG, so the scaling needs to be compensated
12020
+ // for here, because the actual width will be different from the calculated numbers.
12021
+ // If the desired width is less than the margin, just punt and return the original tune
12022
+ //console.log(widths)
12023
+ if (widths.length === 0 || params.staffwidth < widths[0].left) {
12024
+ return {
12025
+ reParse: false,
12026
+ explanation: "Staff width is narrower than the margin",
12027
+ revisedParams: params
12028
+ };
12029
+ }
12030
+
12031
+ var scale = params.scale ? Math.max(params.scale, 0.1) : 1;
12032
+ var minSpacing = params.wrap.minSpacing ? Math.max(parseFloat(params.wrap.minSpacing), 1) : 1;
12033
+ var minSpacingLimit = params.wrap.minSpacingLimit ? Math.max(parseFloat(params.wrap.minSpacingLimit), 1) : minSpacing - 0.1;
12034
+ var maxSpacing = params.wrap.maxSpacing ? Math.max(parseFloat(params.wrap.maxSpacing), 1) : undefined;
12035
+ 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;
12036
+
12037
+ var preferredMeasuresPerLine = params.wrap.preferredMeasuresPerLine ? Math.max(parseInt(params.wrap.preferredMeasuresPerLine, 10), 0) : undefined;
12038
+ var accumulatedLineBreaks = [];
12039
+ var explanations = [];
12040
+
12041
+ for (var s = 0; s < widths.length; s++) {
12042
+ var section = widths[s];
12043
+ var usableWidth = params.staffwidth - section.left;
12044
+ var lineBreakPoint = usableWidth / minSpacing / scale;
12045
+ var minLineSize = usableWidth / maxSpacing / scale;
12046
+ var allowableVariance = usableWidth / minSpacingLimit / scale;
12047
+ var explanation = {
12048
+ widths: section,
12049
+ lineBreakPoint: lineBreakPoint,
12050
+ minLineSize: minLineSize,
12051
+ attempts: [],
12052
+ staffWidth: params.staffwidth,
12053
+ minWidth: Math.round(allowableVariance)
12054
+ }; // 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.
12055
+
12056
+ var lineBreaks = null;
12057
+
12058
+ if (preferredMeasuresPerLine) {
12059
+ var f = fixedMeasureLineBreaks(section.measureWidths, lineBreakPoint, preferredMeasuresPerLine);
12060
+ explanation.attempts.push({
12061
+ type: "Fixed Measures Per Line",
12062
+ preferredMeasuresPerLine: preferredMeasuresPerLine,
12063
+ lineBreaks: f.lineBreaks,
12064
+ failed: f.failed,
12065
+ totals: f.totals
12066
+ });
12067
+ if (!f.failed) lineBreaks = f.lineBreaks;
12068
+ } // If we don't have lineBreaks yet, use the free form method of line breaks.
12069
+ // 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.
12070
+
12071
+
12072
+ if (!lineBreaks) {
12073
+ var ff = freeFormLineBreaks(section.measureWidths, lineBreakPoint);
12074
+ explanation.attempts.push({
12075
+ type: "Free Form",
12076
+ lineBreaks: ff.lineBreaks,
12077
+ totals: ff.totals
12078
+ });
12079
+ 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.
12080
+
12081
+ if (lineBreaks.length > 0 && section.measureWidths.length < 25) {
12082
+ // Only do this if everything doesn't fit on one line.
12083
+ // This is an intensive operation and it is optional so just do it for shorter music.
12084
+ ff = optimizeLineWidths(section, lineBreakPoint, lineBreaks, explanation);
12085
+ explanation.attempts.push({
12086
+ type: "Optimize",
12087
+ failed: ff.failed,
12088
+ reason: ff.reason,
12089
+ lineBreaks: ff.lineBreaks,
12090
+ totals: ff.totals
12091
+ });
12092
+ if (!ff.failed) lineBreaks = ff.lineBreaks;
12093
+ }
12094
+ }
12095
+
12096
+ accumulatedLineBreaks.push(lineBreaks);
12097
+ explanations.push(explanation);
12098
+ } // If the vertical space exceeds targetHeight, remove a line and try again. If that is too crowded, then don't use it.
12099
+
12100
+
12101
+ var staffWidth = params.staffwidth;
12102
+ var ret = getRevisedTuneParams(accumulatedLineBreaks, staffWidth, params);
12103
+ ret.explanation = explanations;
12104
+ ret.reParse = true;
12105
+ return ret;
12106
+ }
12107
+
12108
+ module.exports = {
12109
+ wrapLines: wrapLines,
12110
+ calcLineWraps: calcLineWraps
12111
+ };
12112
+
12113
+ /***/ }),
12114
+
12115
+ /***/ "./src/str/output.js":
12116
+ /*!***************************!*\
12117
+ !*** ./src/str/output.js ***!
12118
+ \***************************/
12119
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
12120
+
12121
+ var keyAccidentals = __webpack_require__(/*! ../const/key-accidentals */ "./src/const/key-accidentals.js");
12122
+
12123
+ var _require = __webpack_require__(/*! ../const/relative-major */ "./src/const/relative-major.js"),
12124
+ relativeMajor = _require.relativeMajor,
12125
+ transposeKey = _require.transposeKey,
12126
+ relativeMode = _require.relativeMode;
12127
+
12128
+ var transposeChordName = __webpack_require__(/*! ../parse/transpose-chord */ "./src/parse/transpose-chord.js");
12129
+
12130
+ var strTranspose;
12131
+
12132
+ (function () {
12133
+ "use strict";
12134
+
12135
+ strTranspose = function strTranspose(abc, abcTune, steps) {
12136
+ if (abcTune === "TEST") // Backdoor way to get entry points for unit tests
12137
+ return {
12138
+ keyAccidentals: keyAccidentals,
12139
+ relativeMajor: relativeMajor,
12140
+ transposeKey: transposeKey,
12141
+ relativeMode: relativeMode,
12142
+ transposeChordName: transposeChordName
12143
+ };
12144
+ steps = parseInt(steps, 10);
12145
+ var changes = [];
12146
+ var i;
12147
+
12148
+ for (i = 0; i < abcTune.length; i++) {
12149
+ changes = changes.concat(transposeOneTune(abc, abcTune[i], steps));
12150
+ } // Reverse sort so that we are replacing strings from the end to the beginning so that the indexes aren't invalidated as we go.
12151
+ // (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.)
12152
+
12153
+
12154
+ changes = changes.sort(function (a, b) {
12155
+ return b.start - a.start;
12156
+ });
12157
+ var output = abc.split('');
12158
+
12159
+ for (i = 0; i < changes.length; i++) {
12160
+ var ch = changes[i];
12161
+ output.splice(ch.start, ch.end - ch.start, ch.note);
12162
+ }
12163
+
12164
+ return output.join('');
12165
+ };
12166
+
12167
+ function transposeOneTune(abc, abcTune, steps) {
12168
+ var changes = []; // Don't transpose bagpipe music - that is a special case and is always a particular key
12169
+
12170
+ var key = abcTune.getKeySignature();
12171
+ if (key.root === 'Hp' || key.root === "HP") return changes;
12172
+ changes = changes.concat(changeAllKeySigs(abc, steps));
12173
+
12174
+ for (var i = 0; i < abcTune.lines.length; i++) {
12175
+ var staves = abcTune.lines[i].staff;
12176
+
12177
+ if (staves) {
12178
+ for (var j = 0; j < staves.length; j++) {
12179
+ var staff = staves[j];
12180
+ if (staff.clef.type !== "perc") changes = changes.concat(transposeVoices(abc, staff.voices, staff.key, steps));
12181
+ }
12182
+ }
12183
+ }
12184
+
12185
+ return changes;
12186
+ }
12187
+
12188
+ function changeAllKeySigs(abc, steps) {
12189
+ var changes = [];
12190
+ var arr = abc.split("K:"); // now each line except the first one will start with whatever is right after "K:"
12191
+
12192
+ var count = arr[0].length;
12193
+
12194
+ for (var i = 1; i < arr.length; i++) {
12195
+ var segment = arr[i];
12196
+ var match = segment.match(/^( *)([A-G])([#b]?)(\w*)/);
12197
+
12198
+ if (match) {
12199
+ var start = count + 2 + match[1].length; // move past the 'K:' and optional white space
12200
+
12201
+ var key = match[2] + match[3] + match[4]; // key name, accidental, and mode
12202
+
12203
+ var destinationKey = newKey({
12204
+ root: match[2],
12205
+ acc: match[3],
12206
+ mode: match[4]
12207
+ }, steps);
12208
+ var dest = destinationKey.root + destinationKey.acc + destinationKey.mode;
12209
+ changes.push({
12210
+ start: start,
12211
+ end: start + key.length,
12212
+ note: dest
12213
+ });
12214
+ }
12215
+
12216
+ count += segment.length + 2;
12217
+ }
12218
+
12219
+ return changes;
12220
+ }
12221
+
12222
+ function transposeVoices(abc, voices, key, steps) {
12223
+ var changes = [];
12224
+ var destinationKey = newKey(key, steps);
12225
+
12226
+ for (var i = 0; i < voices.length; i++) {
12227
+ changes = changes.concat(transposeVoice(abc, voices[i], key.root, createKeyAccidentals(key), destinationKey, steps));
12228
+ }
12229
+
12230
+ return changes;
12231
+ }
12232
+
12233
+ function createKeyAccidentals(key) {
12234
+ var ret = {};
12235
+
12236
+ for (var i = 0; i < key.accidentals.length; i++) {
12237
+ var acc = key.accidentals[i];
12238
+ if (acc.acc === 'flat') ret[acc.note.toUpperCase()] = '_';else if (acc.acc === 'sharp') ret[acc.note.toUpperCase()] = '^';
12239
+ }
12240
+
12241
+ return ret;
12242
+ }
12243
+
12244
+ function setLetterDistance(destinationKey, keyRoot, steps) {
12245
+ var letterDistance = letters.indexOf(destinationKey.root) - letters.indexOf(keyRoot);
12246
+ if (keyRoot === "none") letterDistance = letters.indexOf(destinationKey.root);
12247
+
12248
+ if (letterDistance === 0) {
12249
+ // This could either be a half step (like Eb => E) or almost an octave (like E => Eb)
12250
+ if (steps > 2) // If it is a large leap, then we are going up an octave
12251
+ letterDistance += 7;else if (steps === -12) // If it is a large leap, then we are going down an octave
12252
+ letterDistance -= 7;
12253
+ } else if (steps > 0 && letterDistance < 0) letterDistance += 7;else if (steps < 0 && letterDistance > 0) letterDistance -= 7;
12254
+
12255
+ if (steps > 12) letterDistance += 7;else if (steps < -12) letterDistance -= 7;
12256
+ return letterDistance;
12257
+ }
12258
+
12259
+ function transposeVoice(abc, voice, keyRoot, keyAccidentals, destinationKey, steps) {
12260
+ var changes = [];
12261
+ var letterDistance = setLetterDistance(destinationKey, keyRoot, steps);
12262
+ var measureAccidentals = {};
12263
+ var transposedMeasureAccidentals = {};
12264
+
12265
+ for (var i = 0; i < voice.length; i++) {
12266
+ var el = voice[i];
12267
+
12268
+ if (el.chord) {
12269
+ for (var c = 0; c < el.chord.length; c++) {
12270
+ var ch = el.chord[c];
12271
+
12272
+ if (ch.position === 'default') {
12273
+ var prefersFlats = destinationKey.accidentals.length && destinationKey.accidentals[0].acc === 'flat';
12274
+ var newChord = transposeChordName(ch.name, steps, prefersFlats, true);
12275
+ newChord = newChord.replace(/♭/g, "b").replace(/♯/g, "#");
12276
+ if (newChord !== ch.name) // If we didn't recognize the chord the input is returned unchanged and there is nothing to replace
12277
+ changes.push(replaceChord(abc, el.startChar, el.endChar, newChord));
12278
+ }
12279
+ }
12280
+ }
12281
+
12282
+ if (el.el_type === 'note' && el.pitches) {
12283
+ for (var j = 0; j < el.pitches.length; j++) {
12284
+ var note = parseNote(el.pitches[j].name, keyRoot, keyAccidentals, measureAccidentals);
12285
+ if (note.acc) measureAccidentals[note.name.toUpperCase()] = note.acc;
12286
+ var newPitch = transposePitch(note, destinationKey, letterDistance, transposedMeasureAccidentals);
12287
+ if (newPitch.acc) transposedMeasureAccidentals[newPitch.upper] = newPitch.acc;
12288
+ changes.push(replaceNote(abc, el.startChar, el.endChar, newPitch.acc + newPitch.name, j));
12289
+ }
12290
+
12291
+ if (el.gracenotes) {
12292
+ for (var g = 0; g < el.gracenotes.length; g++) {
12293
+ var grace = parseNote(el.gracenotes[g].name, keyRoot, keyAccidentals, measureAccidentals);
12294
+ if (grace.acc) measureAccidentals[grace.name.toUpperCase()] = grace.acc;
12295
+ var newGrace = transposePitch(grace, destinationKey, letterDistance, measureAccidentals);
12296
+ if (newGrace.acc) transposedMeasureAccidentals[newGrace.upper] = newGrace.acc;
12297
+ changes.push(replaceGrace(abc, el.startChar, el.endChar, newGrace.acc + newGrace.name, g));
12298
+ }
12299
+ }
12300
+ } else if (el.el_type === "bar") {
12301
+ measureAccidentals = {};
12302
+ transposedMeasureAccidentals = {};
12303
+ } else if (el.el_type === "keySignature") {
12304
+ keyRoot = el.root;
12305
+ keyAccidentals = createKeyAccidentals(el);
12306
+ destinationKey = newKey(el, steps);
12307
+ letterDistance = setLetterDistance(destinationKey, keyRoot, steps);
12308
+ }
12309
+ }
12310
+
12311
+ return changes;
12312
+ }
12313
+
12314
+ var letters = "CDEFGAB";
12315
+ var octaves = [",,,,", ",,,", ",,", ",", "", "'", "''", "'''", "''''"];
12316
+
12317
+ function newKey(key, steps) {
12318
+ if (key.root === "none") {
12319
+ return {
12320
+ root: transposeKey("C", steps),
12321
+ mode: "",
12322
+ acc: "",
12323
+ accidentals: []
12324
+ };
12325
+ }
12326
+
12327
+ var major = relativeMajor(key.root + key.acc + key.mode);
12328
+ var newMajor = transposeKey(major, steps);
12329
+ var newMode = relativeMode(newMajor, key.mode);
12330
+ var acc = keyAccidentals(newMajor);
12331
+ return {
12332
+ root: newMode[0],
12333
+ mode: key.mode,
12334
+ acc: newMode.length > 1 ? newMode[1] : '',
12335
+ accidentals: acc
12336
+ };
12337
+ }
12338
+
12339
+ function transposePitch(note, key, letterDistance, measureAccidentals) {
12340
+ // Depending on what the current note and new note are, the octave might have changed
12341
+ // The letterDistance is how far the change is to see if we passed "C" when transposing.
12342
+ var pitch = note.pitch;
12343
+ var origDistFromC = letters.indexOf(note.name);
12344
+ var root = letters.indexOf(key.root);
12345
+ 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.
12346
+
12347
+ var newDistFromC = origDistFromC + letterDistance;
12348
+ var oct = note.oct;
12349
+
12350
+ while (newDistFromC > 6) {
12351
+ oct++;
12352
+ newDistFromC -= 7;
12353
+ }
12354
+
12355
+ while (newDistFromC < 0) {
12356
+ oct--;
12357
+ newDistFromC += 7;
12358
+ }
12359
+
12360
+ var name = letters[index];
12361
+ var acc = '';
12362
+ 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.
12363
+
12364
+ var keyAcc = '=';
12365
+
12366
+ for (var i = 0; i < key.accidentals.length; i++) {
12367
+ if (key.accidentals[i].note.toLowerCase() === name.toLowerCase()) {
12368
+ adj = adj + (key.accidentals[i].acc === 'flat' ? -1 : 1);
12369
+ keyAcc = key.accidentals[i].acc === 'flat' ? '_' : '^';
12370
+ break;
12371
+ }
12372
+ }
12373
+
12374
+ switch (adj) {
12375
+ case -2:
12376
+ acc = "__";
12377
+ break;
12378
+
12379
+ case -1:
12380
+ acc = "_";
12381
+ break;
12382
+
12383
+ case 0:
12384
+ acc = "=";
12385
+ break;
12386
+
12387
+ case 1:
12388
+ acc = "^";
12389
+ break;
12390
+
12391
+ case 2:
12392
+ acc = "^^";
12393
+ break;
12394
+
12395
+ case -3:
12396
+ // This requires a triple flat, so bump down the pitch and try again
12397
+ var newNote = {};
12398
+ newNote.pitch = note.pitch - 1;
12399
+ newNote.oct = note.oct;
12400
+ newNote.name = letters[letters.indexOf(note.name) - 1];
12401
+
12402
+ if (!newNote.name) {
12403
+ newNote.name = "B";
12404
+ newNote.oct--;
12405
+ }
12406
+
12407
+ if (newNote.name === "B" || newNote.name === "E") newNote.adj = note.adj + 1;else newNote.adj = note.adj + 2;
12408
+ return transposePitch(newNote, key, letterDistance + 1, measureAccidentals);
12409
+
12410
+ case 3:
12411
+ // This requires a triple sharp, so bump up the pitch and try again
12412
+ var newNote = {};
12413
+ newNote.pitch = note.pitch + 1;
12414
+ newNote.oct = note.oct;
12415
+ newNote.name = letters[letters.indexOf(note.name) + 1];
12416
+
12417
+ if (!newNote.name) {
12418
+ newNote.name = "C";
12419
+ newNote.oct++;
12420
+ }
12421
+
12422
+ if (newNote.name === "C" || newNote.name === "F") newNote.adj = note.adj - 1;else newNote.adj = note.adj - 2;
12423
+ return transposePitch(newNote, key, letterDistance + 1, measureAccidentals);
12424
+ }
12425
+
12426
+ if ((measureAccidentals[name] === acc || !measureAccidentals[name] && acc === keyAcc) && !note.courtesy) acc = "";
12427
+
12428
+ switch (oct) {
12429
+ case 0:
12430
+ name = name + ",,,";
12431
+ break;
12432
+
12433
+ case 1:
12434
+ name = name + ",,";
12435
+ break;
12436
+
12437
+ case 2:
12438
+ name = name + ",";
12439
+ break;
12440
+ // case 3: it is already correct
12441
+
12442
+ case 4:
12443
+ name = name.toLowerCase();
12444
+ break;
12445
+
12446
+ case 5:
12447
+ name = name.toLowerCase() + "'";
12448
+ break;
12449
+
12450
+ case 6:
12451
+ name = name.toLowerCase() + "''";
12452
+ break;
12453
+
12454
+ case 7:
12455
+ name = name.toLowerCase() + "'''";
12456
+ break;
12457
+
12458
+ case 8:
12459
+ name = name.toLowerCase() + "''''";
12460
+ break;
12461
+ }
12462
+
12463
+ if (oct > 4) name = name.toLowerCase();
12464
+ return {
12465
+ acc: acc,
12466
+ name: name,
12467
+ upper: name.toUpperCase()
12468
+ };
12469
+ }
12470
+
12471
+ var regPitch = /([_^=]*)([A-Ga-g])([,']*)/;
12472
+ var regNote = /([_^=]*[A-Ga-g][,']*)(\d*\/*\d*)([\>\<\-\)\.\s\\]*)/;
12473
+ var regOptionalNote = /([_^=]*[A-Ga-g][,']*)?(\d*\/*\d*)?([\>\<\-\)]*)?/;
12474
+ 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
12475
+ // 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.
12476
+ // I don't think there is any adjustment needed for minor keys since the adjustment is based on the key signature and the accidentals.
12477
+
12478
+ function parseNote(note, keyRoot, keyAccidentals, measureAccidentals) {
12479
+ var root = keyRoot === "none" ? 0 : letters.indexOf(keyRoot);
12480
+ var reg = note.match(regPitch); // reg[1] : "__", "_", "", "=", "^", or "^^"
12481
+ // reg[2] : A-G a-g
12482
+ // reg[3] : commas or apostrophes
12483
+
12484
+ var name = reg[2].toUpperCase();
12485
+ var pos = letters.indexOf(name) - root;
12486
+ if (pos < 0) pos += 7;
12487
+ var oct = octaves.indexOf(reg[3]);
12488
+ if (name === reg[2]) // See if it is a capital letter and subtract an octave if so.
12489
+ oct--;
12490
+ 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.
12491
+
12492
+ return {
12493
+ acc: reg[1],
12494
+ name: name,
12495
+ pitch: pos,
12496
+ oct: oct,
12497
+ adj: calcAdjustment(reg[1], keyAccidentals[name], measureAccidentals[name]),
12498
+ courtesy: reg[1] === currentAcc
12499
+ };
12500
+ }
12501
+
12502
+ function replaceNote(abc, start, end, newPitch, index) {
12503
+ // 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.
12504
+ // This could also be a part of a chord. If so, then the particular note needs to be teased out.
12505
+ var note = abc.substring(start, end);
12506
+ var match = note.match(new RegExp(regNote.source + regSpace.source), '');
12507
+
12508
+ if (match) {
12509
+ // This will match a single note
12510
+ var noteLen = match[1].length;
12511
+ var trailingLen = match[2].length + match[3].length + match[4].length;
12512
+ var leadingLen = end - start - noteLen - trailingLen;
12513
+ start += leadingLen;
12514
+ end -= trailingLen;
12515
+ } else {
12516
+ // 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.
12517
+ var regPreBracket = /([^\[]*)/;
12518
+ var regOpenBracket = /\[/;
12519
+ var regCloseBracket = /\-?](\d*\/*\d*)?([\>\<\-\)]*)/;
12520
+ 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));
12521
+
12522
+ if (match) {
12523
+ // This will match a chord
12524
+ // Get the number of chars used by the previous notes in this chord
12525
+ var count = 1 + match[1].length; // one character for the open bracket
12526
+
12527
+ for (var i = 0; i < index; i++) {
12528
+ // index is the iteration through the chord. This function gets called for each one.
12529
+ if (match[i * 3 + 2]) count += match[i * 3 + 2].length;
12530
+ if (match[i * 3 + 3]) count += match[i * 3 + 3].length;
12531
+ if (match[i * 3 + 4]) count += match[i * 3 + 4].length;
12532
+ }
12533
+
12534
+ start += count;
12535
+ var endLen = match[index * 3 + 2] ? match[index * 3 + 2].length : 0; // endLen += match[index * 3 + 3] ? match[index * 3 + 3].length : 0
12536
+ // endLen += match[index * 3 + 4] ? match[index * 3 + 4].length : 0
11809
12537
 
11810
- if (thisWidth > lineBreakPoint) {
11811
- failed = true;
12538
+ end = start + endLen;
12539
+ }
11812
12540
  }
11813
12541
 
11814
- if (i % preferredMeasuresPerLine === preferredMeasuresPerLine - 1) {
11815
- if (i !== widths.length - 1) // Don't bother putting a line break for the last line - it's already a break.
11816
- lineBreaks.push(i);
11817
- totals.push(Math.round(thisWidth));
11818
- thisWidth = 0;
11819
- }
12542
+ return {
12543
+ start: start,
12544
+ end: end,
12545
+ note: newPitch
12546
+ };
11820
12547
  }
11821
12548
 
11822
- return {
11823
- failed: failed,
11824
- totals: totals,
11825
- lineBreaks: lineBreaks
11826
- };
11827
- }
12549
+ function replaceGrace(abc, start, end, newGrace, index) {
12550
+ 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.
11828
12551
 
11829
- function getRevisedTuneParams(lineBreaks, staffWidth, params) {
11830
- var revisedParams = {
11831
- lineBreaks: lineBreaks,
11832
- staffwidth: staffWidth
11833
- };
12552
+ var regOpenBrace = /\{/;
12553
+ var regCloseBrace = /\}/;
12554
+ var regPreBrace = /([^\{]*)/;
12555
+ var regPreNote = /(\/*)/;
12556
+ 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));
11834
12557
 
11835
- for (var key in params) {
11836
- if (params.hasOwnProperty(key) && key !== 'wrap' && key !== 'staffwidth') {
11837
- revisedParams[key] = params[key];
12558
+ if (match) {
12559
+ // This will match all notes inside a grace symbol
12560
+ // Get the number of chars used by the previous graces
12561
+ var count = 1 + match[1].length; // one character for the open brace, and whatever comes before the brace
12562
+
12563
+ for (var i = 0; i < index; i++) {
12564
+ // index is the iteration through the chord. This function gets called for each one.
12565
+ if (match[i * 3 + 2]) count += match[i * 3 + 2].length;
12566
+ if (match[i * 3 + 3]) count += match[i * 3 + 3].length;
12567
+ if (match[i * 3 + 4]) count += match[i * 3 + 4].length;
12568
+ if (match[i * 3 + 5]) count += match[i * 3 + 5].length;
12569
+ }
12570
+
12571
+ if (match[index * 3 + 2]) count += match[i * 3 + 2].length;
12572
+ start += count;
12573
+ var endLen = match[index * 3 + 3] ? match[index * 3 + 3].length : 0;
12574
+ endLen += match[index * 3 + 4] ? match[index * 3 + 4].length : 0;
12575
+ endLen += match[index * 3 + 5] ? match[index * 3 + 5].length : 0;
12576
+ end = start + endLen;
11838
12577
  }
12578
+
12579
+ return {
12580
+ start: start,
12581
+ end: end,
12582
+ note: newGrace
12583
+ };
11839
12584
  }
11840
12585
 
11841
- return {
11842
- revisedParams: revisedParams
11843
- };
11844
- }
12586
+ function replaceChord(abc, start, end, newChord) {
12587
+ // Isolate the chord and just replace that
12588
+ var match = abc.substring(start, end).match(/([^"]+)?(".+")+/);
12589
+ if (match[1]) start += match[1].length;
12590
+ end = start + match[2].length; // leave the quote in, so skip one more
11845
12591
 
11846
- function calcLineWraps(tune, widths, params) {
11847
- // 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
11848
- // by the minimum spacing instead of multiplying the min spacing later.
11849
- // The scaling works differently: this is done by changing the scaling of the outer SVG, so the scaling needs to be compensated
11850
- // for here, because the actual width will be different from the calculated numbers.
11851
- // If the desired width is less than the margin, just punt and return the original tune
11852
- //console.log(widths)
11853
- if (widths.length === 0 || params.staffwidth < widths[0].left) {
11854
12592
  return {
11855
- reParse: false,
11856
- explanation: "Staff width is narrower than the margin",
11857
- revisedParams: params
12593
+ start: start + 1,
12594
+ end: end - 1,
12595
+ note: newChord
11858
12596
  };
11859
12597
  }
11860
12598
 
11861
- var scale = params.scale ? Math.max(params.scale, 0.1) : 1;
11862
- var minSpacing = params.wrap.minSpacing ? Math.max(parseFloat(params.wrap.minSpacing), 1) : 1;
11863
- var minSpacingLimit = params.wrap.minSpacingLimit ? Math.max(parseFloat(params.wrap.minSpacingLimit), 1) : minSpacing - 0.1;
11864
- var maxSpacing = params.wrap.maxSpacing ? Math.max(parseFloat(params.wrap.maxSpacing), 1) : undefined;
11865
- 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;
12599
+ function calcAdjustment(thisAccidental, keyAccidental, measureAccidental) {
12600
+ if (!thisAccidental && measureAccidental) {
12601
+ // There was no accidental on this note, but there was earlier in the measure, so we'll use that
12602
+ thisAccidental = measureAccidental;
12603
+ }
11866
12604
 
11867
- var preferredMeasuresPerLine = params.wrap.preferredMeasuresPerLine ? Math.max(parseInt(params.wrap.preferredMeasuresPerLine, 10), 0) : undefined;
11868
- var accumulatedLineBreaks = [];
11869
- var explanations = [];
12605
+ if (!thisAccidental) return 0; // there is no deviation from the key.
11870
12606
 
11871
- for (var s = 0; s < widths.length; s++) {
11872
- var section = widths[s];
11873
- var usableWidth = params.staffwidth - section.left;
11874
- var lineBreakPoint = usableWidth / minSpacing / scale;
11875
- var minLineSize = usableWidth / maxSpacing / scale;
11876
- var allowableVariance = usableWidth / minSpacingLimit / scale;
11877
- var explanation = {
11878
- widths: section,
11879
- lineBreakPoint: lineBreakPoint,
11880
- minLineSize: minLineSize,
11881
- attempts: [],
11882
- staffWidth: params.staffwidth,
11883
- minWidth: Math.round(allowableVariance)
11884
- }; // 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.
12607
+ switch (keyAccidental) {
12608
+ case undefined:
12609
+ switch (thisAccidental) {
12610
+ case '__':
12611
+ return -2;
11885
12612
 
11886
- var lineBreaks = null;
12613
+ case '_':
12614
+ return -1;
11887
12615
 
11888
- if (preferredMeasuresPerLine) {
11889
- var f = fixedMeasureLineBreaks(section.measureWidths, lineBreakPoint, preferredMeasuresPerLine);
11890
- explanation.attempts.push({
11891
- type: "Fixed Measures Per Line",
11892
- preferredMeasuresPerLine: preferredMeasuresPerLine,
11893
- lineBreaks: f.lineBreaks,
11894
- failed: f.failed,
11895
- totals: f.totals
11896
- });
11897
- if (!f.failed) lineBreaks = f.lineBreaks;
11898
- } // If we don't have lineBreaks yet, use the free form method of line breaks.
11899
- // 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.
12616
+ case '=':
12617
+ return 0;
11900
12618
 
12619
+ case '^':
12620
+ return 1;
11901
12621
 
11902
- if (!lineBreaks) {
11903
- var ff = freeFormLineBreaks(section.measureWidths, lineBreakPoint);
11904
- explanation.attempts.push({
11905
- type: "Free Form",
11906
- lineBreaks: ff.lineBreaks,
11907
- totals: ff.totals
11908
- });
11909
- 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.
12622
+ case '^^':
12623
+ return 2;
11910
12624
 
11911
- if (lineBreaks.length > 0 && section.measureWidths.length < 25) {
11912
- // Only do this if everything doesn't fit on one line.
11913
- // This is an intensive operation and it is optional so just do it for shorter music.
11914
- ff = optimizeLineWidths(section, lineBreakPoint, lineBreaks, explanation);
11915
- explanation.attempts.push({
11916
- type: "Optimize",
11917
- failed: ff.failed,
11918
- reason: ff.reason,
11919
- lineBreaks: ff.lineBreaks,
11920
- totals: ff.totals
11921
- });
11922
- if (!ff.failed) lineBreaks = ff.lineBreaks;
11923
- }
11924
- }
12625
+ default:
12626
+ return 0;
12627
+ // this should never happen
12628
+ }
11925
12629
 
11926
- accumulatedLineBreaks.push(lineBreaks);
11927
- explanations.push(explanation);
11928
- } // If the vertical space exceeds targetHeight, remove a line and try again. If that is too crowded, then don't use it.
12630
+ case '_':
12631
+ switch (thisAccidental) {
12632
+ case '__':
12633
+ return -1;
11929
12634
 
12635
+ case '_':
12636
+ return 0;
11930
12637
 
11931
- var staffWidth = params.staffwidth;
11932
- var ret = getRevisedTuneParams(accumulatedLineBreaks, staffWidth, params);
11933
- ret.explanation = explanations;
11934
- ret.reParse = true;
11935
- return ret;
11936
- }
12638
+ case '=':
12639
+ return 1;
11937
12640
 
11938
- module.exports = {
11939
- wrapLines: wrapLines,
11940
- calcLineWraps: calcLineWraps
11941
- };
12641
+ case '^':
12642
+ return 2;
12643
+
12644
+ case '^^':
12645
+ return 3;
12646
+
12647
+ default:
12648
+ return 0;
12649
+ // this should never happen
12650
+ }
12651
+
12652
+ case '^':
12653
+ switch (thisAccidental) {
12654
+ case '__':
12655
+ return -3;
12656
+
12657
+ case '_':
12658
+ return -2;
12659
+
12660
+ case '=':
12661
+ return -1;
12662
+
12663
+ case '^':
12664
+ return 0;
12665
+
12666
+ case '^^':
12667
+ return 1;
12668
+
12669
+ default:
12670
+ return 0;
12671
+ // this should never happen
12672
+ }
12673
+
12674
+ }
12675
+
12676
+ return 0; // this should never happen
12677
+ }
12678
+ })();
12679
+
12680
+ module.exports = strTranspose;
11942
12681
 
11943
12682
  /***/ }),
11944
12683
 
@@ -12719,7 +13458,9 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
12719
13458
  volume: velocity,
12720
13459
  start: timeToRealTime(elem.time),
12721
13460
  duration: durationRounded(note.duration),
12722
- instrument: currentInstrument
13461
+ instrument: currentInstrument,
13462
+ startChar: elem.elem.startChar,
13463
+ endChar: elem.elem.endChar
12723
13464
  };
12724
13465
  p = adjustForMicroTone(p);
12725
13466
 
@@ -14792,6 +15533,8 @@ var createNoteMap = function createNoteMap(sequence) {
14792
15533
  end: Math.round((ev.start + len - gap) * 1000000) / 1000000,
14793
15534
  volume: ev.volume
14794
15535
  };
15536
+ if (ev.startChar) obj.startChar = ev.startChar;
15537
+ if (ev.endChar) obj.endChar = ev.endChar;
14795
15538
  if (ev.style) obj.style = ev.style;
14796
15539
  if (ev.cents) obj.cents = ev.cents;
14797
15540
  map[i].push(obj);
@@ -16138,8 +16881,8 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
16138
16881
  var fnResolve;
16139
16882
 
16140
16883
  offlineCtx.oncomplete = function (e) {
16141
- if (e.renderedBuffer) {
16142
- // If the system gets overloaded then this can start failing. Just drop the note if so.
16884
+ if (e.renderedBuffer && e.renderedBuffer.getChannelData) {
16885
+ // If the system gets overloaded or there are network problems then this can start failing. Just drop the note if so.
16143
16886
  for (var i = 0; i < startArray.length; i++) {
16144
16887
  //Math.floor(startArray[i] * sound.tempoMultiplier * sampleRate)
16145
16888
  var start = startArray[i] * sound.tempoMultiplier;
@@ -19302,6 +20045,15 @@ AbstractEngraver.prototype.createBeam = function (isSingleLineStaff, voice, elem
19302
20045
  if (hint) beamelem.setHint();
19303
20046
 
19304
20047
  for (var i = 0; i < elems.length; i++) {
20048
+ // Do a first pass to figure out the stem direction before creating the notes, so that staccatos and other decorations can be placed correctly.
20049
+ beamelem.runningDirection(elems[i]);
20050
+ }
20051
+
20052
+ beamelem.setStemDirection();
20053
+ var tempStemDir = this.stemdir;
20054
+ this.stemdir = beamelem.stemsUp ? 'up' : 'down';
20055
+
20056
+ for (i = 0; i < elems.length; i++) {
19305
20057
  var elem = elems[i];
19306
20058
  var abselem = this.createNote(elem, true, isSingleLineStaff, voice);
19307
20059
  abselemset.push(abselem);
@@ -19316,6 +20068,7 @@ AbstractEngraver.prototype.createBeam = function (isSingleLineStaff, voice, elem
19316
20068
 
19317
20069
  beamelem.calcDir();
19318
20070
  voice.addBeam(beamelem);
20071
+ this.stemdir = tempStemDir;
19319
20072
  return abselemset;
19320
20073
  };
19321
20074
 
@@ -19861,6 +20614,7 @@ AbstractEngraver.prototype.addSlursAndTies = function (abselem, pitchelem, noteh
19861
20614
  for (var j = 0; j < this.ties.length; j++) {
19862
20615
  if (this.ties[j].anchor1 && this.ties[j].anchor1.pitch === notehead.pitch) {
19863
20616
  this.ties[j].setEndAnchor(notehead);
20617
+ voice.setRange(this.ties[j]);
19864
20618
  this.ties.splice(j, 1);
19865
20619
  found = true;
19866
20620
  break;
@@ -19869,6 +20623,7 @@ AbstractEngraver.prototype.addSlursAndTies = function (abselem, pitchelem, noteh
19869
20623
 
19870
20624
  if (!found) {
19871
20625
  this.ties[0].setEndAnchor(notehead);
20626
+ voice.setRange(this.ties[0]);
19872
20627
  this.ties.splice(0, 1);
19873
20628
  }
19874
20629
  }
@@ -19904,6 +20659,7 @@ AbstractEngraver.prototype.addSlursAndTies = function (abselem, pitchelem, noteh
19904
20659
  if (this.slurs[slurid]) {
19905
20660
  slur = this.slurs[slurid];
19906
20661
  slur.setEndAnchor(notehead);
20662
+ voice.setRange(slur);
19907
20663
  delete this.slurs[slurid];
19908
20664
  } else {
19909
20665
  slur = new TieElem({
@@ -20058,6 +20814,11 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
20058
20814
 
20059
20815
 
20060
20816
  abselem.extraw -= 5;
20817
+
20818
+ if (elem.chord !== undefined) {
20819
+ var ret3 = addChord(this.getTextSize, abselem, elem, 0, 0, 0, false);
20820
+ }
20821
+
20061
20822
  return abselem;
20062
20823
  };
20063
20824
 
@@ -20116,6 +20877,15 @@ BeamElem.prototype.setHint = function () {
20116
20877
  this.hint = true;
20117
20878
  };
20118
20879
 
20880
+ BeamElem.prototype.runningDirection = function (abcelem) {
20881
+ var pitch = abcelem.averagepitch;
20882
+ if (pitch === undefined) return; // don't include elements like spacers in beams
20883
+
20884
+ this.total = Math.round(this.total + pitch);
20885
+ if (!this.count) this.count = 0;
20886
+ this.count++;
20887
+ };
20888
+
20119
20889
  BeamElem.prototype.add = function (abselem) {
20120
20890
  var pitch = abselem.abcelem.averagepitch;
20121
20891
  if (pitch === undefined) return; // don't include elements like spacers in beams
@@ -20138,6 +20908,24 @@ BeamElem.prototype.addBeam = function (beam) {
20138
20908
  this.beams.push(beam);
20139
20909
  };
20140
20910
 
20911
+ BeamElem.prototype.setStemDirection = function () {
20912
+ // Have to figure this out before the notes are placed because placing the notes also places the decorations.
20913
+ this.average = calcAverage(this.total, this.count);
20914
+
20915
+ if (this.forceup) {
20916
+ this.stemsUp = true;
20917
+ } else if (this.forcedown) {
20918
+ this.stemsUp = false;
20919
+ } else {
20920
+ var middleLine = 6; // hardcoded 6 is B
20921
+
20922
+ this.stemsUp = this.average < middleLine; // true is up, false is down;
20923
+ }
20924
+
20925
+ delete this.count;
20926
+ this.total = 0;
20927
+ };
20928
+
20141
20929
  BeamElem.prototype.calcDir = function () {
20142
20930
  this.average = calcAverage(this.total, this.elems.length);
20143
20931
 
@@ -20706,6 +21494,8 @@ var DynamicDecoration = __webpack_require__(/*! ./abc_dynamic_decoration */ "./s
20706
21494
 
20707
21495
  var CrescendoElem = __webpack_require__(/*! ./abc_crescendo_element */ "./src/write/abc_crescendo_element.js");
20708
21496
 
21497
+ var GlissandoElem = __webpack_require__(/*! ./abc_glissando_element */ "./src/write/abc_glissando_element.js");
21498
+
20709
21499
  var glyphs = __webpack_require__(/*! ./abc_glyphs */ "./src/write/abc_glyphs.js");
20710
21500
 
20711
21501
  var RelativeElement = __webpack_require__(/*! ./abc_relative_element */ "./src/write/abc_relative_element.js");
@@ -21022,6 +21812,7 @@ function leftDecoration(decoration, abselem, roomtaken) {
21022
21812
  Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, positioning) {
21023
21813
  var diminuendo;
21024
21814
  var crescendo;
21815
+ var glissando;
21025
21816
 
21026
21817
  for (var i = 0; i < decoration.length; i++) {
21027
21818
  switch (decoration[i]) {
@@ -21050,6 +21841,19 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
21050
21841
  };
21051
21842
  this.startCrescendoX = undefined;
21052
21843
  break;
21844
+
21845
+ case "glissando(":
21846
+ this.startGlissandoX = abselem;
21847
+ glissando = undefined;
21848
+ break;
21849
+
21850
+ case "glissando)":
21851
+ glissando = {
21852
+ start: this.startGlissandoX,
21853
+ stop: abselem
21854
+ };
21855
+ this.startGlissandoX = undefined;
21856
+ break;
21053
21857
  }
21054
21858
  }
21055
21859
 
@@ -21060,6 +21864,10 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
21060
21864
  if (crescendo) {
21061
21865
  voice.addOther(new CrescendoElem(crescendo.start, crescendo.stop, "<", positioning));
21062
21866
  }
21867
+
21868
+ if (glissando) {
21869
+ voice.addOther(new GlissandoElem(glissando.start, glissando.stop));
21870
+ }
21063
21871
  };
21064
21872
 
21065
21873
  Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals) {
@@ -21426,6 +22234,23 @@ module.exports = EngraverController;
21426
22234
 
21427
22235
  /***/ }),
21428
22236
 
22237
+ /***/ "./src/write/abc_glissando_element.js":
22238
+ /*!********************************************!*\
22239
+ !*** ./src/write/abc_glissando_element.js ***!
22240
+ \********************************************/
22241
+ /***/ (function(module) {
22242
+
22243
+ var GlissandoElem = function GlissandoElem(anchor1, anchor2) {
22244
+ this.type = "GlissandoElem";
22245
+ this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
22246
+
22247
+ this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
22248
+ };
22249
+
22250
+ module.exports = GlissandoElem;
22251
+
22252
+ /***/ }),
22253
+
21429
22254
  /***/ "./src/write/abc_glyphs.js":
21430
22255
  /*!*********************************!*\
21431
22256
  !*** ./src/write/abc_glyphs.js ***!
@@ -22663,6 +23488,17 @@ TieElem.prototype.addInternalNote = function (note) {
22663
23488
  TieElem.prototype.setEndAnchor = function (anchor2) {
22664
23489
  // console.log("end", this.anchor1 ? this.anchor1.pitch : "N/A", anchor2 ? anchor2.pitch : "N/A", this.isTie, this.isGrace);
22665
23490
  this.anchor2 = anchor2; // must have a .x and a .pitch property or be null (means ends at the end of the line)
23491
+ // we don't really have enough info to know what the vertical extent is yet and we won't until drawing. This will just give it enough
23492
+ // room on either side (we don't even know if the slur will be above yet). We need to set this so that we can make sure the voice has
23493
+ // at least enough room that the line doesn't get cut off if the tie or slur is the lowest thing.
23494
+
23495
+ if (this.anchor1) {
23496
+ this.top = Math.max(this.anchor1.pitch, this.anchor2.pitch) + 4;
23497
+ this.bottom = Math.min(this.anchor1.pitch, this.anchor2.pitch) - 4;
23498
+ } else {
23499
+ this.top = this.anchor2.pitch + 4;
23500
+ this.bottom = this.anchor2.pitch - 4;
23501
+ }
22666
23502
  }; // If we encounter a repeat sign, then we don't want to extend either a tie or a slur past it, so these are called to be a limit.
22667
23503
 
22668
23504
 
@@ -23899,6 +24735,107 @@ module.exports = drawEnding;
23899
24735
 
23900
24736
  /***/ }),
23901
24737
 
24738
+ /***/ "./src/write/draw/glissando.js":
24739
+ /*!*************************************!*\
24740
+ !*** ./src/write/draw/glissando.js ***!
24741
+ \*************************************/
24742
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
24743
+
24744
+ var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
24745
+
24746
+ var printPath = __webpack_require__(/*! ./print-path */ "./src/write/draw/print-path.js");
24747
+
24748
+ var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
24749
+
24750
+ function drawGlissando(renderer, params, selectables) {
24751
+ 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.");
24752
+ var margin = 4;
24753
+ var leftY = renderer.calcY(params.anchor1.heads[0].pitch);
24754
+ var rightY = renderer.calcY(params.anchor2.heads[0].pitch);
24755
+ var leftX = params.anchor1.x + params.anchor1.w / 2;
24756
+ var rightX = params.anchor2.x + params.anchor2.w / 2;
24757
+ var len = lineLength(leftX, leftY, rightX, rightY);
24758
+ var marginLeft = params.anchor1.w / 2 + margin;
24759
+ var marginRight = params.anchor2.w / 2 + margin;
24760
+ var s = slope(leftX, leftY, rightX, rightY);
24761
+ var leftYAdj = getY(leftY, s, marginLeft);
24762
+ var rightYAdj = getY(rightY, s, -marginRight);
24763
+ var num = numSquigglies(len - marginLeft - marginRight);
24764
+ var el = drawSquiggly(renderer, leftX + marginLeft, leftYAdj, num, s);
24765
+ selectables.wrapSvgEl({
24766
+ el_type: "glissando",
24767
+ startChar: -1,
24768
+ endChar: -1
24769
+ }, el);
24770
+ return [el];
24771
+ }
24772
+
24773
+ function lineLength(leftX, leftY, rightX, rightY) {
24774
+ // The length from notehead center to notehead center.
24775
+ var w = rightX - leftX;
24776
+ var h = rightY - leftY;
24777
+ return Math.sqrt(w * w + h * h);
24778
+ }
24779
+
24780
+ function slope(leftX, leftY, rightX, rightY) {
24781
+ return (rightY - leftY) / (rightX - leftX);
24782
+ }
24783
+
24784
+ function getY(y, slope, xOfs) {
24785
+ return roundNumber(y + xOfs * slope);
24786
+ }
24787
+
24788
+ function numSquigglies(length) {
24789
+ var endLen = 5; // The width of the end - that is, the non repeating part
24790
+
24791
+ return Math.max(2, Math.floor((length - endLen * 2) / 6));
24792
+ }
24793
+
24794
+ var leftStart = [[3.5, -4.8]];
24795
+ var right = [[1.5, -1], [.3, -.3], [-3.5, 3.8]];
24796
+ var leftEnd = [[-1.5, 2]];
24797
+ var top = [[3, 4], [3, -4]];
24798
+ var bottom = [[-3, 4], [-3, -4]];
24799
+
24800
+ function segment(arr, slope) {
24801
+ var ret = "";
24802
+
24803
+ for (var i = 0; i < arr.length; i++) {
24804
+ ret += 'l' + arr[i][0] + ' ' + getY(arr[i][1], slope, arr[i][0]);
24805
+ }
24806
+
24807
+ return ret;
24808
+ }
24809
+
24810
+ var drawSquiggly = function drawSquiggly(renderer, x, y, num, slope) {
24811
+ var p = sprintf("M %f %f", x, y);
24812
+ p += segment(leftStart, slope);
24813
+ var i;
24814
+
24815
+ for (i = 0; i < num; i++) {
24816
+ p += segment(top, slope);
24817
+ }
24818
+
24819
+ p += segment(right, slope);
24820
+
24821
+ for (i = 0; i < num; i++) {
24822
+ p += segment(bottom, slope);
24823
+ }
24824
+
24825
+ p += segment(leftEnd, slope) + 'z';
24826
+ return printPath(renderer, {
24827
+ path: p,
24828
+ highlight: "stroke",
24829
+ stroke: renderer.foregroundColor,
24830
+ 'class': renderer.controller.classes.generate('decoration'),
24831
+ "data-name": "glissando"
24832
+ });
24833
+ };
24834
+
24835
+ module.exports = drawGlissando;
24836
+
24837
+ /***/ }),
24838
+
23902
24839
  /***/ "./src/write/draw/group-elements.js":
23903
24840
  /*!******************************************!*\
23904
24841
  !*** ./src/write/draw/group-elements.js ***!
@@ -25436,6 +26373,8 @@ module.exports = drawTriplet;
25436
26373
  \*********************************/
25437
26374
  /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
25438
26375
 
26376
+ var drawGlissando = __webpack_require__(/*! ./glissando */ "./src/write/draw/glissando.js");
26377
+
25439
26378
  var drawCrescendo = __webpack_require__(/*! ./crescendo */ "./src/write/draw/crescendo.js");
25440
26379
 
25441
26380
  var drawDynamics = __webpack_require__(/*! ./dynamics */ "./src/write/draw/dynamics.js");
@@ -25535,6 +26474,10 @@ function drawVoice(renderer, params, bartop, selectables, staffPos) {
25535
26474
  renderer.controller.classes.incrMeasure();
25536
26475
  } else {
25537
26476
  switch (child.type) {
26477
+ case "GlissandoElem":
26478
+ child.elemset = drawGlissando(renderer, child, selectables);
26479
+ break;
26480
+
25538
26481
  case "CrescendoElem":
25539
26482
  child.elemset = drawCrescendo(renderer, child, selectables);
25540
26483
  break;
@@ -25587,7 +26530,7 @@ function formatJazzChord(chordString) {
25587
26530
  for (var i = 0; i < lines.length; i++) {
25588
26531
  var chord = lines[i]; // If the chord isn't in a recognizable format then just skip the formatting.
25589
26532
 
25590
- var reg = chord.match(/^([ABCDEFG][♯♭]?)?([^\/]+)?(\/[ABCDEFG][#b]?)?/);
26533
+ var reg = chord.match(/^([ABCDEFG][♯♭]?)?([^\/]+)?(\/[ABCDEFG][#b♯♭]?)?/);
25591
26534
  if (reg) lines[i] = (reg[1] ? reg[1] : '') + "\x03" + (reg[2] ? reg[2] : '') + "\x03" + (reg[3] ? reg[3] : '');
25592
26535
  }
25593
26536
 
@@ -28109,7 +29052,7 @@ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, padd
28109
29052
  font: 'infofont',
28110
29053
  klass: 'meta-top rhythm',
28111
29054
  absElemType: "rhythm",
28112
- noMove: true,
29055
+ noMove: noMove,
28113
29056
  info: metaTextInfo.rhythm,
28114
29057
  name: "rhythm"
28115
29058
  }, getTextSize);
@@ -28187,7 +29130,7 @@ module.exports = unhighlight;
28187
29130
  \********************/
28188
29131
  /***/ (function(module) {
28189
29132
 
28190
- var version = '6.0.2';
29133
+ var version = '6.1.0';
28191
29134
  module.exports = version;
28192
29135
 
28193
29136
  /***/ })