abcjs 6.2.3 → 6.4.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.
Files changed (75) hide show
  1. package/README.md +8 -0
  2. package/RELEASE.md +84 -1
  3. package/dist/abcjs-basic-min.js +2 -2
  4. package/dist/abcjs-basic.js +1775 -1034
  5. package/dist/abcjs-basic.js.map +1 -1
  6. package/dist/abcjs-plugin-min.js +2 -2
  7. package/index.js +3 -0
  8. package/package.json +1 -1
  9. package/plugin.js +1 -1
  10. package/src/api/abc_tablatures.js +48 -13
  11. package/src/api/tune-metrics.js +18 -0
  12. package/src/data/abc_tune.js +13 -2
  13. package/src/edit/abc_editarea.js +4 -1
  14. package/src/parse/abc_parse.js +2 -0
  15. package/src/parse/abc_parse_directive.js +23 -16
  16. package/src/parse/abc_parse_header.js +22 -19
  17. package/src/parse/abc_tokenizer.js +72 -7
  18. package/src/parse/tune-builder.js +60 -1
  19. package/src/synth/abc_midi_flattener.js +40 -462
  20. package/src/synth/abc_midi_sequencer.js +25 -10
  21. package/src/synth/chord-track.js +562 -0
  22. package/src/synth/create-note-map.js +2 -1
  23. package/src/synth/create-synth.js +91 -42
  24. package/src/synth/synth-controller.js +6 -2
  25. package/src/tablatures/instruments/string-patterns.js +11 -0
  26. package/src/tablatures/instruments/{violin/violin-patterns.js → tab-string-patterns.js} +6 -6
  27. package/src/tablatures/instruments/{violin/tab-violin.js → tab-string.js} +13 -11
  28. package/src/tablatures/tab-absolute-elements.js +16 -7
  29. package/src/tablatures/tab-renderer.js +22 -9
  30. package/src/test/abc_parser_lint.js +14 -12
  31. package/src/write/creation/abstract-engraver.js +6 -2
  32. package/src/write/creation/add-chord.js +102 -82
  33. package/src/write/creation/add-text-if.js +2 -2
  34. package/src/write/creation/decoration.js +16 -9
  35. package/src/write/creation/elements/bottom-text.js +62 -47
  36. package/src/write/creation/elements/rich-text.js +51 -0
  37. package/src/write/creation/elements/tie-element.js +23 -0
  38. package/src/write/creation/elements/top-text.js +37 -11
  39. package/src/write/creation/glyphs.js +1 -1
  40. package/src/write/draw/absolute.js +4 -1
  41. package/src/write/draw/draw.js +13 -4
  42. package/src/write/draw/non-music.js +3 -1
  43. package/src/write/draw/relative.js +1 -1
  44. package/src/write/draw/tempo.js +1 -1
  45. package/src/write/draw/text.js +10 -0
  46. package/src/write/engraver-controller.js +62 -17
  47. package/src/write/helpers/classes.js +1 -1
  48. package/src/write/helpers/get-font-and-attr.js +8 -1
  49. package/src/write/helpers/get-text-size.js +8 -1
  50. package/src/write/interactive/create-analysis.js +50 -0
  51. package/src/write/interactive/find-selectable-element.js +24 -0
  52. package/src/write/interactive/selection.js +5 -45
  53. package/src/write/layout/layout-in-grid.js +83 -0
  54. package/src/write/layout/layout.js +29 -24
  55. package/src/write/layout/set-upper-and-lower-elements.js +2 -0
  56. package/src/write/layout/staff-group.js +2 -2
  57. package/src/write/layout/voice-elements.js +1 -1
  58. package/src/write/layout/voice.js +1 -1
  59. package/src/write/renderer.js +3 -0
  60. package/src/write/svg.js +30 -0
  61. package/temp.txt +3 -0
  62. package/types/index.d.ts +142 -38
  63. package/version.js +1 -1
  64. package/.github/workflows/tests.yml +0 -29
  65. package/abc2xml_239/abc2xml.html +0 -769
  66. package/abc2xml_239/abc2xml.py +0 -2248
  67. package/abc2xml_239/abc2xml_changelog.html +0 -124
  68. package/abc2xml_239/lazy-river.abc +0 -26
  69. package/abc2xml_239/lazy-river.xml +0 -3698
  70. package/abc2xml_239/mean-to-me.abc +0 -22
  71. package/abc2xml_239/mean-to-me.xml +0 -2954
  72. package/abc2xml_239/pyparsing.py +0 -3672
  73. package/abc2xml_239/pyparsing.pyc +0 -0
  74. package/src/tablatures/instruments/guitar/guitar-patterns.js +0 -23
  75. package/src/tablatures/instruments/guitar/tab-guitar.js +0 -48
@@ -26,13 +26,15 @@ function CreateSynth() {
26
26
  self.audioBuffers = []; // cache of the buffers so starting play can be fast.
27
27
  self.duration = undefined; // the duration of the tune in seconds.
28
28
  self.isRunning = false; // whether there is currently a sound buffer running.
29
- // self.options = undefined
29
+ self.options = undefined
30
+ self.pickupLength = 0
30
31
 
31
32
  // Load and cache all needed sounds
32
33
  self.init = function(options) {
33
34
  if (!options)
34
35
  options = {};
35
- // self.options = options
36
+ if (options.options)
37
+ self.options = options.options
36
38
  registerAudioContext(options.audioContext); // This works no matter what - if there is already an ac it is a nop; if the context is not passed in, then it creates one.
37
39
  var startTime = activeAudioContext().currentTime;
38
40
  self.debugCallback = options.debugCallback;
@@ -115,6 +117,7 @@ function CreateSynth() {
115
117
  var meter = options.visualObj.getMeterFraction();
116
118
  if (meter.den)
117
119
  self.meterSize = options.visualObj.getMeterFraction().num / options.visualObj.getMeterFraction().den;
120
+ self.pickupLength = options.visualObj.getPickupLength()
118
121
  } else if (options.sequence)
119
122
  self.flattened = options.sequence;
120
123
  else
@@ -124,6 +127,7 @@ function CreateSynth() {
124
127
  self.sequenceCallback = params.sequenceCallback;
125
128
  self.callbackContext = params.callbackContext;
126
129
  self.onEnded = params.onEnded;
130
+ self.meterFraction = options.visualObj.getMeterFraction();
127
131
 
128
132
  var allNotes = {};
129
133
  var cached = [];
@@ -136,18 +140,19 @@ function CreateSynth() {
136
140
  if (event.pitch !== undefined) {
137
141
  var pitchNumber = event.pitch;
138
142
  var noteName = pitchToNoteName[pitchNumber];
143
+ var inst = event.instrument !== undefined ? instrumentIndexToName[event.instrument] : currentInstrument
139
144
  if (noteName) {
140
- if (!allNotes[currentInstrument])
141
- allNotes[currentInstrument] = {};
142
- if (!soundsCache[currentInstrument] || !soundsCache[currentInstrument][noteName])
143
- allNotes[currentInstrument][noteName] = true;
145
+ if (!allNotes[inst])
146
+ allNotes[inst] = {};
147
+ if (!soundsCache[inst] || !soundsCache[inst][noteName])
148
+ allNotes[inst][noteName] = true;
144
149
  else {
145
- var label2 = currentInstrument+":"+noteName
150
+ var label2 = inst+":"+noteName
146
151
  if (cached.indexOf(label2) < 0)
147
152
  cached.push(label2);
148
153
  }
149
154
  } else {
150
- var label = currentInstrument+":"+noteName
155
+ var label = inst+":"+noteName
151
156
  console.log("Can't find note: ", pitchNumber, label);
152
157
  if (errorNotes.indexOf(label) < 0)
153
158
  errorNotes.push(label)
@@ -309,8 +314,10 @@ function CreateSynth() {
309
314
  self.stop();
310
315
 
311
316
  var noteMapTracks = createNoteMap(self.flattened);
312
- // if (self.options.swing)
313
- // addSwing(noteMapTracks, self.options.swing, self.beatsPerMeasure)
317
+
318
+ if (self.options.swing)
319
+ addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength)
320
+
314
321
  if (self.sequenceCallback)
315
322
  self.sequenceCallback(noteMapTracks, self.callbackContext);
316
323
 
@@ -545,38 +552,80 @@ function CreateSynth() {
545
552
  }
546
553
  };
547
554
 
548
- // // this is a first attempt at adding a little bit of swing to the output, but the algorithm isn't correct.
549
- // function addSwing(noteMapTracks, swing, beatsPerMeasure) {
550
- // console.log("addSwing", noteMapTracks, swing, beatsPerMeasure)
551
- // // Swing should be between -0.9 and 0.9. Make sure the input is between them.
552
- // // Then that is the percentage to add to the first beat, so a negative number moves the second beat earlier.
553
- // // A value of zero is the same as no swing at all.
554
- // // This only works when there are an even number of beats in a measure.
555
- // if (beatsPerMeasure % 2 !== 0)
556
- // return;
557
- // swing = parseFloat(swing)
558
- // if (isNaN(swing))
559
- // return
560
- // if (swing < -0.9)
561
- // swing = -0.9
562
- // if (swing > 0.9)
563
- // swing = 0.9
564
- // var beatLength = (1 / beatsPerMeasure)*2
565
- // swing = beatLength * swing
566
- // for (var t = 0; t < noteMapTracks.length; t++) {
567
- // var track = noteMapTracks[t];
568
- // for (var i = 0; i < track.length; i++) {
569
- // var event = track[i];
570
- // if (event.start % beatLength) {
571
- // // This is the off beat
572
- // event.start += swing;
573
- // } else {
574
- // // This is the beat
575
- // event.end += swing;
576
- // }
577
- // }
578
- // }
579
- // }
555
+ function addSwing(noteMapTracks, swing, meterFraction, pickupLength) {
556
+
557
+ // we can only swing in X/4 and X/8 meters.
558
+ if (meterFraction.den != 4 && meterFraction.den != 8)
559
+ return;
560
+
561
+ swing = parseFloat(swing);
562
+
563
+ // 50 (or less) is no swing,
564
+ if (isNaN(swing) || swing <= 50)
565
+ return;
566
+
567
+ // 66 is triplet swing 2:1, and
568
+ // 60 is swing with a ratio of 3:2.
569
+ // 75 is the maximum swing where the first eight is played as a dotted eight and the second as a sixteenth.
570
+ if (swing > 75)
571
+ swing = 75;
572
+
573
+ // convert the swing percentage to a percentage of increase for the first half of the beat
574
+ swing = swing/50 - 1;
575
+
576
+ // The volume of the swung notes is increased by this factor
577
+ // could be also in the settings. Try out values such 0.1, 0.2
578
+ var volumeIncrease = 0.0;
579
+
580
+ // the beatLength in X/8 meters
581
+ var beatLength = 0.25;
582
+
583
+ // in X/8 meters the 16s swing so the beatLength is halved
584
+ if (meterFraction.den === 8)
585
+ beatLength = beatLength/2;
586
+
587
+ // duration of a half beat
588
+ var halfbeatLength = beatLength/2;
589
+
590
+ // the extra duration of the first swung notes and the delay of the second notes
591
+ var swingDuration = halfbeatLength * swing;
592
+
593
+ for (var t = 0; t < noteMapTracks.length; t++) {
594
+ var track = noteMapTracks[t];
595
+ for (var i = 0; i < track.length; i++) {
596
+ var event = track[i];
597
+ if (
598
+ // is halfbeat
599
+ (event.start-pickupLength) % halfbeatLength == 0 && (event.start-pickupLength) % beatLength != 0
600
+ && (
601
+ // the previous note is on the beat or before OR there is no previous note
602
+ i == 0
603
+ || track[i-1].start <= track[i].start - halfbeatLength
604
+ )
605
+ && (
606
+ // the next note is on the beat or after OR there is no next note
607
+ i == track.length - 1
608
+ || track[i+1].start >= track[i].start + halfbeatLength
609
+ )
610
+ ) {
611
+ var oldEventStart = event.start;
612
+
613
+ event.start += swingDuration;
614
+
615
+ // Increase volume of swung notes
616
+ event.volume *= 1 + volumeIncrease;
617
+
618
+ // if there is a previous note ending at the start of this note, extend its end
619
+ // and decrease its volume
620
+ if (i > 0 && track[i-1].end == oldEventStart) {
621
+ track[i-1].end = event.start;
622
+ track[i-1].volume *= 1 - volumeIncrease;
623
+ }
624
+ }
625
+ }
626
+ }
627
+ }
628
+
580
629
  }
581
630
 
582
631
  module.exports = CreateSynth;
@@ -21,6 +21,10 @@ function SynthController() {
21
21
  self.load = function (selector, cursorControl, visualOptions) {
22
22
  if (!visualOptions)
23
23
  visualOptions = {};
24
+ if (visualOptions.displayPlay === undefined)
25
+ visualOptions.displayPlay = true
26
+ if (visualOptions.displayProgress === undefined)
27
+ visualOptions.displayProgress = true
24
28
  self.control = new CreateSynthControl(selector, {
25
29
  loopHandler: visualOptions.displayLoop ? self.toggleLoop : undefined,
26
30
  restartHandler: visualOptions.displayRestart ? self.restart : undefined,
@@ -41,7 +45,7 @@ function SynthController() {
41
45
  self.setTune = function(visualObj, userAction, audioParams) {
42
46
  self.visualObj = visualObj;
43
47
  self.disable(false);
44
- self.options = audioParams;
48
+ self.options = audioParams ? audioParams : {};
45
49
 
46
50
  if (self.control) {
47
51
  self.pause();
@@ -195,7 +199,7 @@ function SynthController() {
195
199
 
196
200
  self._randomAccess = function (ev) {
197
201
  var background = (ev.target.classList.contains('abcjs-midi-progress-indicator')) ? ev.target.parentNode : ev.target;
198
- var percent = (ev.x - background.offsetLeft) / background.offsetWidth;
202
+ var percent = (ev.x - background.getBoundingClientRect().left) / background.offsetWidth;
199
203
  if (percent < 0)
200
204
  percent = 0;
201
205
  if (percent > 1)
@@ -265,6 +265,17 @@ StringPatterns.prototype.tabInfos = function (plugin) {
265
265
  return '';
266
266
  };
267
267
 
268
+ // MAE 27 Nov 2023
269
+ StringPatterns.prototype.suppress = function (plugin) {
270
+ var _super = plugin._super;
271
+ var suppress = _super.params.suppress;
272
+ if (suppress){
273
+ return true;
274
+ }
275
+ return false;
276
+ };
277
+ // MAE 27 Nov 2023 End
278
+
268
279
  /**
269
280
  * Common patterns for all string instruments
270
281
  * @param {} plugin
@@ -1,23 +1,23 @@
1
- var StringPatterns = require('../string-patterns');
1
+ var StringPatterns = require('./string-patterns');
2
2
 
3
- function ViolinPatterns(plugin) {
3
+ function TabStringPatterns(plugin, defaultTuning) {
4
4
  this.tuning = plugin._super.params.tuning;
5
5
  if (!this.tuning) {
6
- this.tuning = ['G,', 'D', 'A', 'e'];
6
+ this.tuning = defaultTuning;
7
7
  }
8
8
  plugin.tuning = this.tuning;
9
9
  this.strings = new StringPatterns(plugin);
10
10
  }
11
11
 
12
- ViolinPatterns.prototype.notesToNumber = function (notes, graces) {
12
+ TabStringPatterns.prototype.notesToNumber = function (notes, graces) {
13
13
  var converter = this.strings;
14
14
  return converter.notesToNumber(notes, graces);
15
15
  };
16
16
 
17
- ViolinPatterns.prototype.stringToPitch = function (stringNumber) {
17
+ TabStringPatterns.prototype.stringToPitch = function (stringNumber) {
18
18
  var converter = this.strings;
19
19
  return converter.stringToPitch(stringNumber);
20
20
  };
21
21
 
22
22
 
23
- module.exports = ViolinPatterns;
23
+ module.exports = TabStringPatterns;
@@ -1,8 +1,8 @@
1
1
 
2
- var StringTablature = require('../string-tablature');
3
- var TabCommon = require('../../tab-common');
4
- var TabRenderer = require('../../tab-renderer');
5
- var ViolinPatterns = require('./violin-patterns');
2
+ var StringTablature = require('./string-tablature');
3
+ var TabCommon = require('../tab-common');
4
+ var TabRenderer = require('../tab-renderer');
5
+ var TabStringPatterns = require('./tab-string-patterns');
6
6
 
7
7
 
8
8
  /**
@@ -11,18 +11,20 @@ var ViolinPatterns = require('./violin-patterns');
11
11
  * @param {*} tuneNumber the parsed tune AST tree
12
12
  * @param {*} params complementary args provided to Tablature Plugin
13
13
  */
14
- Plugin.prototype.init = function (abcTune, tuneNumber, params) {
14
+ Plugin.prototype.init = function (abcTune, tuneNumber, params, staffNumber, tabSettings) {
15
15
  var _super = new TabCommon(abcTune, tuneNumber, params);
16
16
  this.abcTune = abcTune;
17
17
  this._super = _super;
18
18
  this.linePitch = 3;
19
- this.nbLines = 4;
20
- this.isTabBig = false;
19
+ this.nbLines = tabSettings.defaultTuning.length;
20
+ this.isTabBig = tabSettings.isTabBig;
21
+ this.tabSymbolOffset = tabSettings.tabSymbolOffset;
21
22
  this.capo = params.capo;
22
23
  this.transpose = params.visualTranspose;
24
+ this.hideTabSymbol = params.hideTabSymbol;
23
25
  this.tablature = new StringTablature(this.nbLines,
24
26
  this.linePitch);
25
- var semantics = new ViolinPatterns(this);
27
+ var semantics = new TabStringPatterns(this, tabSettings.defaultTuning);
26
28
  this.semantics = semantics;
27
29
  };
28
30
 
@@ -38,8 +40,8 @@ function Plugin() {}
38
40
  //
39
41
  // Tablature plugin definition
40
42
  //
41
- var AbcViolinTab = function () {
42
- return { name: 'ViolinTab', tablature: Plugin };
43
+ var AbcStringTab = function () {
44
+ return { name: 'StringTab', tablature: Plugin };
43
45
  };
44
46
 
45
- module.exports = AbcViolinTab;
47
+ module.exports = AbcStringTab;
@@ -60,13 +60,22 @@ function buildTabAbsolute(plugin, absX, relX) {
60
60
  icon: tabIcon,
61
61
  Ypos: tabYPos
62
62
  };
63
- var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
64
- tabAbsolute.x = absX;
65
- var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
66
- tabRelative.x = relX;
67
- tabAbsolute.children.push(tabRelative);
68
- if (tabAbsolute.abcelem.el_type == 'tab') {
69
- tabRelative.pitch = tabYPos;
63
+
64
+ // Offset the TAB symbol position if specified in the tab description
65
+ tabYPos += plugin.tabSymbolOffset;
66
+
67
+ // For tablature like whistle tab where you want the TAB symbol hidden
68
+ if (!plugin.hideTabSymbol){
69
+
70
+ var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
71
+ tabAbsolute.x = absX;
72
+ var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
73
+ tabRelative.x = relX;
74
+ tabAbsolute.children.push(tabRelative);
75
+ if (tabAbsolute.abcelem.el_type == 'tab') {
76
+ tabRelative.pitch = tabYPos;
77
+ }
78
+
70
79
  }
71
80
  return tabAbsolute;
72
81
  }
@@ -37,12 +37,24 @@ function buildTabName(self, dest) {
37
37
  var controller = self.renderer.controller;
38
38
  var textSize = controller.getTextSize;
39
39
  var tabName = stringSemantics.tabInfos(self.plugin);
40
- var size = textSize.calc(tabName, 'tablabelfont', 'text instrumentname');
41
- dest.tabNameInfos = {
42
- textSize: size,
43
- name: tabName
44
- };
45
- return size.height;
40
+ var suppress = stringSemantics.suppress(self.plugin);
41
+ var doDraw = true;
42
+
43
+ if (suppress){
44
+ doDraw = false
45
+ }
46
+
47
+
48
+ if (doDraw){
49
+ var size = textSize.calc(tabName, 'tablabelfont', 'text instrumentname');
50
+ dest.tabNameInfos = {
51
+ textSize: {height:size.height,width:size.width},
52
+ name: tabName
53
+ };
54
+ return size.height;
55
+ }
56
+ return 0
57
+
46
58
  }
47
59
 
48
60
  /**
@@ -230,8 +242,10 @@ TabRenderer.prototype.doLayout = function () {
230
242
  if (ii > 0) tabVoice.duplicate = true;
231
243
  var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
232
244
  nameHeight = Math.max(nameHeight, 1) // If there is no label for the tab line, then there needs to be a little padding
233
- staffGroup.staffs[this.staffIndex].top += nameHeight;
234
- staffGroup.height += nameHeight * spacing.STEP;
245
+ // This was pushing down the top staff by the tab label height
246
+ //staffGroup.staffs[this.staffIndex].top += nameHeight;
247
+ staffGroup.staffs[this.staffIndex].top += 1;
248
+ staffGroup.height += nameHeight;
235
249
  tabVoice.staff = staffGroupInfos;
236
250
  var tabVoiceIndex = voices.length
237
251
  voices.splice(voices.length, 0, tabVoice);
@@ -242,5 +256,4 @@ TabRenderer.prototype.doLayout = function () {
242
256
  linkStaffAndTabs(staffGroup.staffs); // crossreference tabs and staff
243
257
  };
244
258
 
245
-
246
259
  module.exports = TabRenderer;
@@ -51,18 +51,18 @@
51
51
  var parseCommon = require('../parse/abc_common');
52
52
  var JSONSchema = require('./jsonschema-b4');
53
53
 
54
- var { legalAccents } = require('../parse/abc_parse_settings');
55
-
56
- var ParserLint = function () {
57
- 'use strict';
58
- var decorationList = {
59
- type: 'array',
60
- optional: true,
61
- items: {
62
- type: 'string',
63
- Enum: legalAccents
64
- }
65
- };
54
+ var ParserLint = function() {
55
+ "use strict";
56
+ var decorationList = { type: 'array', optional: true, items: { type: 'string', Enum: [
57
+ "trill", "lowermordent", "uppermordent", "mordent", "pralltriller", "accent",
58
+ "fermata", "invertedfermata", "tenuto", "0", "1", "2", "3", "4", "5", "+", "wedge",
59
+ "open", "thumb", "snap", "turn", "roll", "irishroll", "breath", "shortphrase", "mediumphrase", "longphrase",
60
+ "segno", "coda", "D.S.", "D.C.", "fine", "crescendo(", "crescendo)", "diminuendo(", "diminuendo)", "glissando(", "glissando)",
61
+ "p", "pp", "f", "ff", "mf", "mp", "ppp", "pppp", "fff", "ffff", "sfz", "repeatbar", "repeatbar2", "slide",
62
+ "upbow", "downbow", "staccato", "trem1", "trem2", "trem3", "trem4",
63
+ "/", "//", "///", "////", "turnx", "invertedturn", "invertedturnx", "arpeggio", "trill(", "trill)", "xstem",
64
+ "mark", "marcato", "umarcato", "D.C.alcoda", "D.C.alfine", "D.S.alcoda", "D.S.alfine", "editorial", "courtesy"
65
+ ] } };
66
66
 
67
67
  var tempoProperties = {
68
68
  duration: { type: "array", optional: true, output: "join", requires: [ 'bpm'], items: { type: "number"} },
@@ -427,6 +427,7 @@ var ParserLint = function () {
427
427
  var formattingProperties = {
428
428
  type:"object",
429
429
  properties: {
430
+ accentAbove: { type: "boolean", optional: true },
430
431
  alignbars: { type: "number", optional: true },
431
432
  aligncomposer: { type: "string", Enum: [ 'left', 'center','right' ], optional: true },
432
433
  annotationfont: fontType,
@@ -555,6 +556,7 @@ var ParserLint = function () {
555
556
  subtitlespace: { type: "number", optional: true },
556
557
  sysstaffsep: { type: "number", optional: true },
557
558
  systemsep: { type: "number", optional: true },
559
+ stafftopmargin: { type: "number", optional: true },
558
560
  tabgracefont: fontType,
559
561
  tablabelfont: fontType,
560
562
  tabnumberfont: fontType,
@@ -53,6 +53,7 @@ var AbstractEngraver = function (getTextSize, tuneNumber, options) {
53
53
  this.percmap = options.percmap;
54
54
  this.initialClef = options.initialClef
55
55
  this.jazzchords = !!options.jazzchords
56
+ this.accentAbove = !!options.accentAbove
56
57
  this.germanAlphabet = !!options.germanAlphabet
57
58
  this.reset();
58
59
  };
@@ -830,7 +831,10 @@ AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaf
830
831
  }
831
832
 
832
833
  if (elem.decoration) {
833
- this.decoration.createDecoration(voice, elem.decoration, abselem.top, (notehead) ? notehead.w : 0, abselem, roomtaken, dir, abselem.bottom, elem.positioning, this.hasVocals);
834
+ // TODO-PER: nostem is true if this is beamed. In that case we don't know where to place the decoration yet so just make a guess. This should be refactored to not place decorations until after the beams are determined.
835
+ // This should probably be combined with moveDecorations()
836
+ var bottom = nostem ? Math.min(-3, abselem.bottom - 6) : abselem.bottom
837
+ this.decoration.createDecoration(voice, elem.decoration, abselem.top, (notehead) ? notehead.w : 0, abselem, roomtaken, dir, bottom, elem.positioning, this.hasVocals, this.accentAbove);
834
838
  }
835
839
 
836
840
  if (elem.barNumber) {
@@ -989,7 +993,7 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
989
993
  }
990
994
 
991
995
  if (elem.decoration) {
992
- this.decoration.createDecoration(voice, elem.decoration, 12, (thick) ? 3 : 1, abselem, 0, "down", 2, elem.positioning, this.hasVocals);
996
+ this.decoration.createDecoration(voice, elem.decoration, 12, (thick) ? 3 : 1, abselem, 0, "down", 2, elem.positioning, this.hasVocals, this.accentAbove);
993
997
  }
994
998
 
995
999
  if (thick) {