abcjs 6.6.0 → 6.6.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.
@@ -9465,12 +9465,14 @@ function flattenVoices(staves) {
9465
9465
  });
9466
9466
  }
9467
9467
  if (!element.rest || element.rest.type !== 'spacer') {
9468
- var thisDuration = Math.floor(element.duration * 4);
9468
+ // if the duration is zero and it is a note, then it is stemless and should count as a quarter note
9469
+ var dur = element.duration === 0 && !element.rest ? 0.25 : element.duration;
9470
+ var thisDuration = Math.floor(dur * 4);
9469
9471
  if (thisDuration > 4) {
9470
9472
  measureNum += Math.floor(thisDuration / 4);
9471
9473
  beatNum = 0;
9472
9474
  } else {
9473
- var thisBeat = element.duration * 4;
9475
+ var thisBeat = dur * 4;
9474
9476
  if (element.tripletMultiplier) thisBeat *= element.tripletMultiplier;
9475
9477
  beatNum += thisBeat;
9476
9478
  }
@@ -9481,6 +9483,14 @@ function flattenVoices(staves) {
9481
9483
  nextBarEnding = "";
9482
9484
  }
9483
9485
  addDecoration(element, currentBar);
9486
+ if (element.chord) {
9487
+ element.chord.forEach(function (ch) {
9488
+ if (ch.position !== 'default') {
9489
+ if (!currentBar.annotations) currentBar.annotations = [];
9490
+ currentBar.annotations.push(ch.name);
9491
+ }
9492
+ });
9493
+ }
9484
9494
  if (element.type === 'bar_dbl_repeat' || element.type === 'bar_left_repeat') currentBar.hasStartRepeat = true;
9485
9495
  if (element.type === 'bar_dbl_repeat' || element.type === 'bar_right_repeat') currentBar.hasEndRepeat = true;
9486
9496
  if (element.startEnding) nextBarEnding = element.startEnding;
@@ -9864,9 +9874,21 @@ var TuneBuilder = function TuneBuilder(tune) {
9864
9874
  }
9865
9875
 
9866
9876
  // If there are overlays, create new voices for them.
9877
+ var hadOverlays = false;
9867
9878
  while (resolveOverlays(tune)) {
9879
+ hadOverlays = true;
9868
9880
  // keep resolving overlays as long as any are found.
9869
9881
  }
9882
+
9883
+ if (hadOverlays) {
9884
+ // remove any blank lines that got inserted - not sure how that happened.
9885
+ var voiceNum = 0;
9886
+ var isUseful = voiceUseful(tune.lines, voiceNum);
9887
+ while (isUseful !== 'not-found') {
9888
+ isUseful = voiceUseful(tune.lines, voiceNum);
9889
+ if (!isUseful) deleteVoice(tune.lines, voiceNum);else voiceNum++;
9890
+ }
9891
+ }
9870
9892
  for (var i = 0; i < tune.lines.length; i++) {
9871
9893
  var staff = tune.lines[i].staff;
9872
9894
  if (staff) {
@@ -10247,8 +10269,13 @@ function simplifyMetaText(tune) {
10247
10269
  // }
10248
10270
 
10249
10271
  function resolveOverlays(tune) {
10272
+ // TODO-PER: maybe a better algorithm than the following:
10273
+ // do a pass to find all the overlays - return a count
10274
+ // (this first pass also removes the overlays and returns them)
10275
+ // do a pass of creating voices with all the measures with invisible rests for all lines
10276
+ // do a pass of inserting the overlays
10277
+
10250
10278
  var madeChanges = false;
10251
- var durationsPerLines = [];
10252
10279
  for (var i = 0; i < tune.lines.length; i++) {
10253
10280
  var line = tune.lines[i];
10254
10281
  if (line.staff) {
@@ -10262,10 +10289,8 @@ function resolveOverlays(tune) {
10262
10289
  voice: [],
10263
10290
  snip: []
10264
10291
  });
10265
- durationsPerLines[i] = 0;
10266
10292
  var durationThisBar = 0;
10267
10293
  var inOverlay = false;
10268
- var overlayDuration = 0;
10269
10294
  var snipStart = -1;
10270
10295
  for (var kk = 0; kk < voice.length; kk++) {
10271
10296
  var event = voice[kk];
@@ -10274,20 +10299,33 @@ function resolveOverlays(tune) {
10274
10299
  inOverlay = true;
10275
10300
  snipStart = kk;
10276
10301
  overlayVoice[k].hasOverlay = true;
10277
- if (overlayDuration === 0) overlayDuration = durationsPerLines[i];
10278
- // If this isn't the first line, we also need invisible rests on the previous lines.
10279
- // So, if the next voice doesn't appear in a previous line, create it
10302
+
10303
+ // TODO-PER: This looks like it can create a completely blank voice but I'm not sure how. That doesn't seem to hurt anything, though, because I filter that out immediately afterward.
10280
10304
  for (var ii = 0; ii < i; ii++) {
10281
- if (durationsPerLines[ii] && tune.lines[ii].staff && staff.voices.length >= tune.lines[ii].staff[0].voices.length) {
10282
- tune.lines[ii].staff[0].voices.push([{
10283
- el_type: "note",
10284
- duration: durationsPerLines[ii],
10285
- rest: {
10286
- type: "invisible"
10287
- },
10288
- startChar: event.startChar,
10289
- endChar: event.endChar
10290
- }]);
10305
+ if (tune.lines[ii].staff) {
10306
+ tune.lines[ii].staff.forEach(function (s) {
10307
+ if (staff.voices.length >= s.voices.length) {
10308
+ s.voices.forEach(function (v) {
10309
+ var nv = [];
10310
+ v.forEach(function (ev) {
10311
+ if (ev.el_type === "bar") {
10312
+ nv.push(ev);
10313
+ } else if (ev.el_type === "note") {
10314
+ nv.push({
10315
+ el_type: "note",
10316
+ duration: ev.duration,
10317
+ rest: {
10318
+ type: "invisible"
10319
+ },
10320
+ startChar: ev.startChar,
10321
+ endChar: ev.endChar
10322
+ });
10323
+ }
10324
+ });
10325
+ s.voices.push(nv);
10326
+ });
10327
+ }
10328
+ });
10291
10329
  }
10292
10330
  }
10293
10331
  } else if (event.el_type === "bar") {
@@ -10318,7 +10356,6 @@ function resolveOverlays(tune) {
10318
10356
  overlayVoice[k].voice.push(event);
10319
10357
  } else if (!event.rest || event.rest.type !== 'spacer') {
10320
10358
  durationThisBar += event.duration;
10321
- durationsPerLines[i] += event.duration;
10322
10359
  }
10323
10360
  } else if (event.el_type === "scale" || event.el_type === "stem" || event.el_type === "overlay" || event.el_type === "style" || event.el_type === "transpose" || event.el_type === "color") {
10324
10361
  // These types of events are duplicated on the overlay layer.
@@ -10370,7 +10407,6 @@ function resolveOverlays(tune) {
10370
10407
  }
10371
10408
  return madeChanges;
10372
10409
  }
10373
- ;
10374
10410
  function findLastBar(voice, start) {
10375
10411
  for (var i = start - 1; i > 0 && voice[i].el_type !== "bar"; i--) {}
10376
10412
  return i;
@@ -10773,6 +10809,42 @@ function createLine(self, tune, params) {
10773
10809
  };
10774
10810
  createStaff(self, tune, params);
10775
10811
  }
10812
+ function voiceUseful(lines, voiceNum) {
10813
+ var isUseful = false;
10814
+ var voiceExists = false;
10815
+ for (var line = 0; line < lines.length; line++) {
10816
+ var staves = lines[line].staff;
10817
+ if (staves) {
10818
+ for (var s = 0; s < staves.length; s++) {
10819
+ var staff = staves[s];
10820
+ if (voiceNum < staff.voices.length) {
10821
+ voiceExists = true;
10822
+ var voice = staff.voices[voiceNum];
10823
+ var output = [];
10824
+ for (var e = 0; e < voice.length; e++) {
10825
+ var el = voice[e];
10826
+ if (el.el_type === 'note' && (!el.rest || el.chord)) isUseful = true;
10827
+ }
10828
+ }
10829
+ }
10830
+ }
10831
+ }
10832
+ if (!voiceExists) return 'not-found';
10833
+ return isUseful;
10834
+ }
10835
+ function deleteVoice(lines, voiceNum) {
10836
+ for (var line = 0; line < lines.length; line++) {
10837
+ var staves = lines[line].staff;
10838
+ if (staves) {
10839
+ for (var s = 0; s < staves.length; s++) {
10840
+ var staff = staves[s];
10841
+ if (voiceNum < staff.voices.length) {
10842
+ staff.voices.splice(voiceNum, 1);
10843
+ }
10844
+ }
10845
+ }
10846
+ }
10847
+ }
10776
10848
  module.exports = TuneBuilder;
10777
10849
 
10778
10850
  /***/ }),
@@ -12011,7 +12083,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
12011
12083
  break;
12012
12084
  default:
12013
12085
  // This should never happen
12014
- console.log("MIDI creation. Unknown el_type: " + element.el_type + "\n"); // jshint ignore:line
12086
+ console.log("MIDI creation. Unknown el_type: " + element.el_type + "\n");
12015
12087
  break;
12016
12088
  }
12017
12089
  }
@@ -16096,10 +16168,17 @@ function Repeats(voice) {
16096
16168
  var isStartRepeat = elem.type === "bar_left_repeat" || elem.type === "bar_dbl_repeat";
16097
16169
  var isEndRepeat = elem.type === "bar_right_repeat" || elem.type === "bar_dbl_repeat";
16098
16170
  var startEnding = elem.startEnding ? startEndingNumbers(elem.startEnding) : undefined;
16099
- if (isEndRepeat) this.sections.push({
16100
- type: "endRepeat",
16101
- index: thisIndex
16102
- });
16171
+ if (isEndRepeat) {
16172
+ // If there are two endRepeats in a row, that is a notation error, but we'll recover by pretending there was a startRepeat right before it.
16173
+ if (this.sections.length > 0 && this.sections[this.sections.length - 1].type === 'endRepeat') this.sections.push({
16174
+ type: "startRepeat",
16175
+ index: this.sections[this.sections.length - 1].index
16176
+ });
16177
+ this.sections.push({
16178
+ type: "endRepeat",
16179
+ index: thisIndex
16180
+ });
16181
+ }
16103
16182
  if (isStartRepeat) this.sections.push({
16104
16183
  type: "startRepeat",
16105
16184
  index: thisIndex
@@ -16167,7 +16246,7 @@ function Repeats(voice) {
16167
16246
  }
16168
16247
  }
16169
16248
  if (lastUsed < section.index - 1) {
16170
- console.log("gap", voice.slice(lastUsed + 1, section.index));
16249
+ //console.log("gap", voice.slice(lastUsed+1, section.index))
16171
16250
  repeatInstructions.push({
16172
16251
  common: {
16173
16252
  start: lastUsed + 1,
@@ -16252,8 +16331,31 @@ function Repeats(voice) {
16252
16331
  }
16253
16332
  function duplicateSpan(input, output, start, end) {
16254
16333
  //console.log("dup", {start, end})
16334
+ if (start < 0) start = 0;
16335
+ // If there is a bar at the end of a line and a bar to start the next line, it would be duplicated.
16336
+ if (output.length > 0 && input[start].el_type === 'bar' && output[output.length - 1].el_type === 'bar') start++;
16255
16337
  for (var i = start; i <= end; i++) {
16256
- output.push(duplicateItem(input[i]));
16338
+ // If there is a beginning element, it might be duplicated.
16339
+ var index;
16340
+ var skip = false;
16341
+ if (input[i].el_type === 'key' || input[i].el_type === 'meter' || input[i].el_type === 'tempo' || input[i].el_type === 'instrument') {
16342
+ index = output.length - 1;
16343
+ while (index >= 0 && output[index].el_type !== input[i].el_type) {
16344
+ index--;
16345
+ }
16346
+ if (index >= 0) {
16347
+ if (input[i].el_type === 'key' && areKeysEqual(input[i], output[index])) {
16348
+ skip = true;
16349
+ } else if (input[i].el_type === 'meter' && input[i].num === output[index].num && input[i].den === output[index].den) {
16350
+ skip = true;
16351
+ } else if (input[i].el_type === 'instrument' && input[i].program === output[index].program) {
16352
+ skip = true;
16353
+ } else if (input[i].el_type === 'tempo' && input[i].qpm === output[index].qpm) {
16354
+ skip = true;
16355
+ }
16356
+ }
16357
+ }
16358
+ if (!skip) output.push(duplicateItem(input[i]));
16257
16359
  }
16258
16360
  }
16259
16361
  function duplicateItem(src) {
@@ -16261,6 +16363,11 @@ function duplicateItem(src) {
16261
16363
  if (item.pitches) item.pitches = parseCommon.cloneArray(item.pitches);
16262
16364
  return item;
16263
16365
  }
16366
+ function areKeysEqual(el1, el2) {
16367
+ if (!el1.accidentals || !el2.accidentals) return false; // this shouldn't happen, but if so, we don't want to skip the element
16368
+
16369
+ return JSON.stringify(el1.accidentals) === JSON.stringify(el2.accidentals);
16370
+ }
16264
16371
  function startEndingNumbers(startEnding) {
16265
16372
  // The ending can be in four different types: "random-string", "number", "number-number", "number,number"
16266
16373
  // If we don't get a number out of it then we will just skip the ending - we don't know what to do with it.
@@ -18450,6 +18557,7 @@ AbstractEngraver.prototype.createABCVoice = function (abcline, tempo, s, v, isSi
18450
18557
  }
18451
18558
  pos += ret.count;
18452
18559
  }
18560
+ this.decoration.endLine(voice);
18453
18561
  this.pushCrossLineElems(s, v);
18454
18562
  };
18455
18563
  AbstractEngraver.prototype.saveState = function () {
@@ -20168,6 +20276,16 @@ function leftDecoration(decoration, abselem, roomtaken) {
20168
20276
  }
20169
20277
  }
20170
20278
  }
20279
+ Decoration.prototype.endLine = function (voice) {
20280
+ if (this.startDiminuendoX) {
20281
+ voice.addOther(new CrescendoElem(this.startDiminuendoX, lastNote(voice.children), ">", this.dynamicPositioning));
20282
+ this.startDiminuendoX = undefined;
20283
+ }
20284
+ if (this.startCrescendoX) {
20285
+ voice.addOther(new CrescendoElem(this.startCrescendoX, lastNote(voice.children), "<", this.dynamicPositioning));
20286
+ this.startCrescendoX = undefined;
20287
+ }
20288
+ };
20171
20289
  Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, positioning) {
20172
20290
  var diminuendo;
20173
20291
  var crescendo;
@@ -20176,9 +20294,11 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
20176
20294
  switch (decoration[i]) {
20177
20295
  case "diminuendo(":
20178
20296
  this.startDiminuendoX = abselem;
20297
+ this.dynamicPositioning = positioning;
20179
20298
  diminuendo = undefined;
20180
20299
  break;
20181
20300
  case "diminuendo)":
20301
+ if (!this.startDiminuendoX) this.startDiminuendoX = firstNote(voice.children);
20182
20302
  diminuendo = {
20183
20303
  start: this.startDiminuendoX,
20184
20304
  stop: abselem
@@ -20187,9 +20307,11 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
20187
20307
  break;
20188
20308
  case "crescendo(":
20189
20309
  this.startCrescendoX = abselem;
20310
+ this.dynamicPositioning = positioning;
20190
20311
  crescendo = undefined;
20191
20312
  break;
20192
20313
  case "crescendo)":
20314
+ if (!this.startCrescendoX) this.startCrescendoX = firstNote(voice.children);
20193
20315
  crescendo = {
20194
20316
  start: this.startCrescendoX,
20195
20317
  stop: abselem
@@ -20221,6 +20343,16 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
20221
20343
  voice.addOther(new GlissandoElem(glissando.start, glissando.stop));
20222
20344
  }
20223
20345
  };
20346
+ function firstNote(els) {
20347
+ for (var i = 0; i < els.length; i++) {
20348
+ if (els[i].abcelem.pitches) return els[i];
20349
+ }
20350
+ return null;
20351
+ }
20352
+ function lastNote(els) {
20353
+ // The end point doesn't need to be a note - we end at the end of the line
20354
+ return els[els.length - 1];
20355
+ }
20224
20356
  Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals, accentAbove) {
20225
20357
  if (!positioning) positioning = {
20226
20358
  ornamentPosition: 'above',
@@ -22785,6 +22917,9 @@ function drawChordGrid(renderer, parts, leftMargin, pageWidth, fonts) {
22785
22917
  var PART_MARGIN_TOP = 10;
22786
22918
  var PART_MARGIN_BOTTOM = 20;
22787
22919
  var TEXT_MARGIN = 16;
22920
+ renderer.paper.openGroup({
22921
+ klass: 'abcjs-chord-grid'
22922
+ });
22788
22923
  parts.forEach(function (part) {
22789
22924
  switch (part.type) {
22790
22925
  case "text":
@@ -22868,6 +23003,7 @@ function drawChordGrid(renderer, parts, leftMargin, pageWidth, fonts) {
22868
23003
  break;
22869
23004
  }
22870
23005
  });
23006
+ renderer.paper.closeGroup();
22871
23007
  }
22872
23008
  function drawPercent(renderer, x, y, offset) {
22873
23009
  var lineX1 = x - 10;
@@ -27815,7 +27951,7 @@ module.exports = Svg;
27815
27951
  \********************/
27816
27952
  /***/ (function(module) {
27817
27953
 
27818
- var version = '6.6.0';
27954
+ var version = '6.6.1';
27819
27955
  module.exports = version;
27820
27956
 
27821
27957
  /***/ })