abcjs 6.4.1 → 6.4.3

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 (48) hide show
  1. package/LICENSE.md +1 -1
  2. package/RELEASE.md +46 -0
  3. package/dist/abcjs-basic-min.js +2 -2
  4. package/dist/abcjs-basic-min.js.LICENSE +1 -1
  5. package/dist/abcjs-basic.js +1271 -1179
  6. package/dist/abcjs-basic.js.map +1 -1
  7. package/dist/abcjs-plugin-min.js +2 -2
  8. package/dist/abcjs-plugin-min.js.LICENSE +1 -1
  9. package/index.js +1 -1
  10. package/license.js +1 -1
  11. package/package.json +1 -1
  12. package/plugin.js +1 -1
  13. package/src/api/abc_tunebook.js +1 -2
  14. package/src/api/abc_tunebook_svg.js +0 -1
  15. package/src/data/abc_tune.js +2 -0
  16. package/src/midi/abc_midi_create.js +22 -7
  17. package/src/parse/abc_common.js +3 -11
  18. package/src/parse/abc_parse.js +1 -1
  19. package/src/parse/abc_parse_directive.js +44 -3
  20. package/src/parse/abc_parse_header.js +6 -4
  21. package/src/parse/abc_parse_key_voice.js +10 -6
  22. package/src/parse/abc_parse_music.js +54 -22
  23. package/src/parse/tune-builder.js +675 -643
  24. package/src/synth/abc_midi_flattener.js +3 -1
  25. package/src/synth/abc_midi_sequencer.js +18 -3
  26. package/src/synth/chord-track.js +90 -18
  27. package/src/synth/create-synth-control.js +1 -2
  28. package/src/tablatures/abc_tablatures.js +184 -0
  29. package/src/tablatures/instruments/string-patterns.js +266 -268
  30. package/src/tablatures/instruments/string-tablature.js +38 -35
  31. package/src/tablatures/instruments/tab-note.js +186 -181
  32. package/src/tablatures/instruments/tab-notes.js +30 -35
  33. package/src/tablatures/instruments/tab-string.js +43 -25
  34. package/src/tablatures/render/tab-absolute-elements.js +303 -0
  35. package/src/tablatures/render/tab-renderer.js +244 -0
  36. package/src/test/abc_parser_lint.js +2 -3
  37. package/src/write/creation/abstract-engraver.js +1 -1
  38. package/src/write/creation/elements/tie-element.js +26 -0
  39. package/src/write/engraver-controller.js +1 -1
  40. package/src/write/layout/set-upper-and-lower-elements.js +8 -0
  41. package/test.js +1 -1
  42. package/types/index.d.ts +2 -2
  43. package/version.js +1 -1
  44. package/src/api/abc_tablatures.js +0 -184
  45. package/src/tablatures/instruments/tab-string-patterns.js +0 -23
  46. package/src/tablatures/tab-absolute-elements.js +0 -301
  47. package/src/tablatures/tab-common.js +0 -29
  48. package/src/tablatures/tab-renderer.js +0 -259
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Tablature Absolute elements factory
3
+ */
4
+ var AbsoluteElement = require('../../write/creation/elements/absolute-element');
5
+ var RelativeElement = require('../../write/creation/elements/relative-element');
6
+
7
+ function isObject(a) { return a != null && a.constructor === Object; }
8
+ function cloneObject(dest, src) {
9
+ for (var prop in src) {
10
+ if (src.hasOwnProperty(prop)) {
11
+ if (!(Array.isArray(src[prop]) || isObject(src[prop]))) {
12
+ dest[prop] = src[prop];
13
+ }
14
+ }
15
+ }
16
+ }
17
+
18
+ function cloneAbsolute(absSrc) {
19
+ var returned = new AbsoluteElement('', 0, 0, '', 0);
20
+ cloneObject(returned, absSrc);
21
+ returned.top = 0;
22
+ returned.bottom = -1;
23
+ if (absSrc.abcelem) {
24
+ returned.abcelem = {};
25
+ cloneObject(returned.abcelem, absSrc.abcelem);
26
+ if (returned.abcelem.el_type === "note")
27
+ returned.abcelem.el_type = 'tabNumber';
28
+ }
29
+ // TODO-PER: This fixes the classes because the element isn't created at the right time.
30
+ absSrc.cloned = returned
31
+ return returned;
32
+ }
33
+
34
+ function cloneAbsoluteAndRelatives(absSrc, plugin) {
35
+ var returned = cloneAbsolute(absSrc);
36
+ if (plugin) {
37
+ var children = absSrc.children;
38
+ // proceed with relative as well
39
+ var first = true;
40
+ for (var ii = 0; ii < children.length; ii++) {
41
+ var child = children[ii];
42
+ var relative = new RelativeElement('', 0, 0, 0, '');
43
+ cloneObject(relative, child);
44
+ first = plugin.tablature.setRelative(child, relative, first);
45
+ returned.children.push(relative);
46
+ }
47
+ }
48
+ return returned;
49
+ }
50
+
51
+ function buildTabAbsolute(plugin, absX, relX) {
52
+ var tabIcon = 'tab.tiny';
53
+ var tabYPos = 7.5;
54
+ if (plugin.isTabBig) {
55
+ tabIcon = 'tab.big';
56
+ tabYPos = 10;
57
+ }
58
+ var element = {
59
+ el_type: "tab",
60
+ icon: tabIcon,
61
+ Ypos: tabYPos
62
+ };
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
+
79
+ }
80
+ return tabAbsolute;
81
+ }
82
+
83
+ function lyricsDim(abs) {
84
+ if (abs.extra) {
85
+ for (var ii = 0; ii < abs.extra.length; ii++) {
86
+ var extra = abs.extra[ii];
87
+ if (extra.type == 'lyric') {
88
+ return {
89
+ bottom: extra.bottom,
90
+ height: extra.height
91
+ };
92
+ }
93
+ }
94
+ }
95
+ return null;
96
+ }
97
+ function TabAbsoluteElements() {
98
+ //console.log("RENDER TabAbsoluteElements constructor")
99
+ this.accidentals = null;
100
+ }
101
+
102
+ function getInitialStaffSize(staffGroup) {
103
+ var returned = 0;
104
+ for (var ii = 0; ii < staffGroup.length; ii++) {
105
+ if (!staffGroup[ii].tabNameInfos) returned++;
106
+ }
107
+ return returned;
108
+ }
109
+
110
+ function buildRelativeTabNote(plugin, relX, def, curNote, isGrace) {
111
+ var strNote = curNote.num;
112
+ if (curNote.note.quarter != null) {
113
+ // add tab quarter => needs to string conversion then
114
+ strNote = strNote.toString();
115
+ strNote += curNote.note.quarter;
116
+ }
117
+ var pitch = plugin.semantics.stringToPitch(curNote.str);
118
+ def.notes.push({ num: strNote, str: curNote.str, pitch: curNote.note.emit() });
119
+ var opt = {
120
+ type: 'tabNumber'
121
+ };
122
+ var tabNoteRelative = new RelativeElement(
123
+ strNote, 0, 0, pitch + 0.3, opt);
124
+ tabNoteRelative.x = relX;
125
+ tabNoteRelative.isGrace = isGrace;
126
+ tabNoteRelative.isAltered = curNote.note.isAltered;
127
+ return tabNoteRelative;
128
+ }
129
+
130
+ function getXGrace(abs, index) {
131
+ var found = 0;
132
+ if (abs.extra) {
133
+ for (var ii = 0; ii < abs.extra.length; ii++) {
134
+ if (abs.extra[ii].c.indexOf('noteheads') >= 0) {
135
+ if (found === index) {
136
+ return abs.extra[ii].x + abs.extra[ii].w / 2;
137
+ } else {
138
+ found++;
139
+ }
140
+ }
141
+ }
142
+ }
143
+ return -1;
144
+ }
145
+
146
+ function graceInRest(absElem) {
147
+ if (absElem.abcelem) {
148
+ var elem = absElem.abcelem;
149
+ if (elem.rest) {
150
+ return elem.gracenotes;
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+
156
+ function convertToNumber(plugin, pitches, graceNotes) {
157
+ var tabPos = plugin.semantics.notesToNumber(pitches, graceNotes);
158
+ if (tabPos.error) {
159
+ plugin.setError(tabPos.error);
160
+ return tabPos; // give up on error here
161
+ }
162
+ if (tabPos.graces && tabPos.notes) {
163
+ // add graces to last note in notes
164
+ var posNote = tabPos.notes.length - 1;
165
+ tabPos.notes[posNote].graces = tabPos.graces;
166
+ }
167
+ return tabPos;
168
+ }
169
+
170
+ function buildGraceRelativesForRest(plugin, abs, absChild, graceNotes, tabVoice) {
171
+ for (var mm = 0; mm < graceNotes.length; mm++) {
172
+ var defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true };
173
+ var graceX = getXGrace(absChild, mm);
174
+ var curGrace = graceNotes[mm];
175
+ var tabGraceRelative = buildRelativeTabNote(plugin, graceX, defGrace, curGrace, true);
176
+ abs.children.push(tabGraceRelative);
177
+ tabVoice.push(defGrace);
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Build tab absolutes by scanning current staff line absolute array
183
+ * @param {*} staffAbsolute
184
+ */
185
+ TabAbsoluteElements.prototype.build = function (plugin,
186
+ staffAbsolute,
187
+ tabVoice,
188
+ voiceIndex,
189
+ staffIndex,
190
+ keySig,
191
+ tabVoiceIndex) {
192
+ //console.log("RENDER TabAbsoluteElements build")
193
+ var staffSize = getInitialStaffSize(staffAbsolute);
194
+ var source = staffAbsolute[staffIndex + voiceIndex];
195
+ var dest = staffAbsolute[tabVoiceIndex];
196
+ var tabPos = null;
197
+ var defNote = null;
198
+ if (source.children[0].abcelem.el_type != 'clef') {
199
+ // keysig missing => provide one for tabs
200
+ if (keySig != 'none') {
201
+ source.children.splice(0, 0, keySig);
202
+ }
203
+ }
204
+ for (var ii = 0; ii < source.children.length; ii++) {
205
+ var absChild = source.children[ii];
206
+ var absX = absChild.x;
207
+ var relX = absX;
208
+ // if (absChild.children.length > 0) {
209
+ // relX = absChild.children[0].x;
210
+ // }
211
+ if ((absChild.isClef)) {
212
+ dest.children.push(buildTabAbsolute(plugin, absX, relX));
213
+ if (absChild.abcelem.type.indexOf('-8') >= 0) plugin.semantics.clefTranspose = -12
214
+ if (absChild.abcelem.type.indexOf('+8') >= 0) plugin.semantics.clefTranspose = 12
215
+ }
216
+ switch (absChild.type) {
217
+ case 'staff-extra key-signature':
218
+ // refresh key accidentals
219
+ this.accidentals = absChild.abcelem.accidentals;
220
+ plugin.semantics.accidentals = this.accidentals;
221
+ break;
222
+ case 'bar':
223
+ plugin.semantics.measureAccidentals = {}
224
+ var lastBar = false;
225
+ if (ii === source.children.length - 1) {
226
+ // used for final line bar drawing
227
+ // for multi tabs / multi staves
228
+ lastBar = true;
229
+ }
230
+ var cloned = cloneAbsoluteAndRelatives(absChild, plugin);
231
+ if (cloned.abcelem.barNumber) {
232
+ delete cloned.abcelem.barNumber;
233
+ for (var bn = 0; bn < cloned.children.length; bn++) {
234
+ if (cloned.children[bn].type === "barNumber") {
235
+ cloned.children.splice(bn, 1);
236
+ break;
237
+ }
238
+ }
239
+ }
240
+ cloned.abcelem.lastBar = lastBar;
241
+ dest.children.push(cloned);
242
+ tabVoice.push({
243
+ el_type: absChild.abcelem.el_type,
244
+ type: absChild.abcelem.type,
245
+ endChar: absChild.abcelem.endChar,
246
+ startChar: absChild.abcelem.startChar,
247
+ abselem: cloned
248
+ });
249
+ break;
250
+ case 'rest':
251
+ var restGraces = graceInRest(absChild);
252
+ if (restGraces) {
253
+ // to number conversion
254
+ tabPos = convertToNumber(plugin, null, restGraces);
255
+ if (tabPos.error) return;
256
+ // build relative for grace
257
+ defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true };
258
+ buildGraceRelativesForRest(plugin, abs, absChild, tabPos.graces, tabVoice);
259
+ }
260
+ break;
261
+ case 'note':
262
+ var abs = cloneAbsolute(absChild);
263
+ abs.x = absChild.heads[0].x + absChild.heads[0].w / 2; // center the number
264
+ abs.lyricDim = lyricsDim(absChild);
265
+ var pitches = absChild.abcelem.pitches;
266
+ var graceNotes = absChild.abcelem.gracenotes;
267
+ abs.type = 'tabNumber';
268
+ // to number conversion
269
+ tabPos = convertToNumber(plugin, pitches, graceNotes);
270
+ if (tabPos.error) return;
271
+ if (tabPos.graces) {
272
+ // add graces to last note in notes
273
+ var posNote = tabPos.notes.length - 1;
274
+ tabPos.notes[posNote].graces = tabPos.graces;
275
+ }
276
+ // build relative
277
+ defNote = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [] };
278
+ for (var ll = 0; ll < tabPos.notes.length; ll++) {
279
+ var curNote = tabPos.notes[ll];
280
+ if (curNote.graces) {
281
+ for (var mm = 0; mm < curNote.graces.length; mm++) {
282
+ var defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true };
283
+ var graceX = getXGrace(absChild, mm);
284
+ var curGrace = curNote.graces[mm];
285
+ var tabGraceRelative = buildRelativeTabNote(plugin, graceX, defGrace, curGrace, true);
286
+ abs.children.push(tabGraceRelative);
287
+ tabVoice.push(defGrace);
288
+ }
289
+ }
290
+ var tabNoteRelative = buildRelativeTabNote(plugin, abs.x + absChild.heads[ll].dx, defNote, curNote, false);
291
+ abs.children.push(tabNoteRelative);
292
+ }
293
+ if (defNote.notes.length > 0) {
294
+ defNote.abselem = abs;
295
+ tabVoice.push(defNote);
296
+ dest.children.push(abs);
297
+ }
298
+ break;
299
+ }
300
+ }
301
+ };
302
+
303
+ module.exports = TabAbsoluteElements;
@@ -0,0 +1,244 @@
1
+ /* eslint-disable no-debugger */
2
+ var VoiceElement = require('../../write/creation/elements/voice-element');
3
+ var TabAbsoluteElements = require('./tab-absolute-elements');
4
+ var spacing = require('../../write/helpers/spacing');
5
+
6
+ function initSpecialY() {
7
+ return {
8
+ tempoHeightAbove: 0,
9
+ partHeightAbove: 0,
10
+ volumeHeightAbove: 0,
11
+ dynamicHeightAbove: 0,
12
+ endingHeightAbove: 0,
13
+ chordHeightAbove: 0,
14
+ lyricHeightAbove: 0,
15
+ lyricHeightBelow: 0,
16
+ chordHeightBelow: 0,
17
+ volumeHeightBelow: 0,
18
+ dynamicHeightBelow: 0
19
+ };
20
+ }
21
+
22
+ function getLyricHeight(voice) {
23
+ var maxLyricHeight = 0;
24
+ for (var ii = 0; ii < voice.children.length; ii++) {
25
+ var curAbs = voice.children[ii];
26
+ if (curAbs.specialY) {
27
+ if (curAbs.specialY.lyricHeightBelow > maxLyricHeight) {
28
+ maxLyricHeight = curAbs.specialY.lyricHeightBelow;
29
+ }
30
+ }
31
+ }
32
+ return maxLyricHeight; // add spacing
33
+ }
34
+
35
+ function buildTabName(plugin, renderer, dest) {
36
+ var stringSemantics = plugin.semantics;
37
+ var textSize = renderer.controller.getTextSize;
38
+ var tabName = stringSemantics.tabInfos(plugin);
39
+ var suppress = stringSemantics.suppress(plugin);
40
+ var doDraw = true;
41
+
42
+ if (suppress) {
43
+ doDraw = false
44
+ }
45
+
46
+
47
+ if (doDraw) {
48
+ var size = textSize.calc(tabName, 'tablabelfont', 'text instrumentname');
49
+ dest.tabNameInfos = {
50
+ textSize: { height: size.height, width: size.width },
51
+ name: tabName
52
+ };
53
+ return size.height;
54
+ }
55
+ return 0
56
+
57
+ }
58
+
59
+ function islastTabInStaff(index, staffGroup) {
60
+ if (staffGroup[index].isTabStaff) {
61
+ if (index === staffGroup.length - 1) return true;
62
+ if (staffGroup[index + 1].isTabStaff) {
63
+ return false;
64
+ } else {
65
+ return true;
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+
71
+ function getStaffNumbers(staffs) {
72
+ var nbStaffs = 0;
73
+ for (var ii = 0; ii < staffs.length; ii++) {
74
+ if (!staffs[ii].isTabStaff) {
75
+ nbStaffs++;
76
+ }
77
+ }
78
+ return nbStaffs;
79
+ }
80
+
81
+ function getParentStaffIndex(staffs, index) {
82
+ for (var ii = index; ii >= 0; ii--) {
83
+ if (!staffs[ii].isTabStaff) {
84
+ return ii;
85
+ }
86
+ }
87
+ return -1;
88
+ }
89
+
90
+
91
+ function linkStaffAndTabs(staffs) {
92
+ for (var ii = 0; ii < staffs.length; ii++) {
93
+ if (staffs[ii].isTabStaff) {
94
+ // link to parent staff
95
+ var parentIndex = getParentStaffIndex(staffs, ii);
96
+ staffs[ii].hasStaff = staffs[parentIndex];
97
+ if (!staffs[parentIndex].hasTab) staffs[parentIndex].hasTab = [];
98
+ staffs[parentIndex].hasTab.push(staffs[ii]);
99
+ }
100
+ }
101
+ }
102
+
103
+ function isMultiVoiceSingleStaff(staffs, parent) {
104
+ if (getStaffNumbers(staffs) === 1) {
105
+ if (parent.voices.length > 1) return true;
106
+ }
107
+ return false;
108
+ }
109
+
110
+
111
+ function getNextTabPos(tabIndex, staffGroup) {
112
+ var startIndex = 0;
113
+ var handledVoices = 0;
114
+ var inProgress = true;
115
+ var nbVoices = 0;
116
+ while (inProgress) {
117
+ //for (var ii = 0; ii < staffGroup.length; ii++) {
118
+ if (!staffGroup[startIndex])
119
+ return -1;
120
+ if (!staffGroup[startIndex].isTabStaff) {
121
+ nbVoices = staffGroup[startIndex].voices.length; // get number of staff voices
122
+ }
123
+ if (staffGroup[startIndex].isTabStaff) {
124
+ handledVoices++;
125
+ if (islastTabInStaff(startIndex, staffGroup)) {
126
+ if (handledVoices < nbVoices) return startIndex + 1;
127
+ }
128
+ } else {
129
+ handledVoices = 0;
130
+ if (startIndex >= tabIndex) {
131
+ if (startIndex + 1 == staffGroup.length) return startIndex + 1;
132
+ if (!staffGroup[startIndex + 1].isTabStaff) return startIndex + 1;
133
+ }
134
+ }
135
+ startIndex++;
136
+ // out of space case
137
+ if (startIndex > staffGroup.length) return -1;
138
+ }
139
+ }
140
+
141
+ function getLastStaff(staffs, lastTab) {
142
+ for (var ii = lastTab; ii >= 0; ii--) {
143
+ if (!staffs[ii].isTabStaff) {
144
+ return staffs[ii];
145
+ }
146
+ }
147
+ return null;
148
+ }
149
+
150
+ function checkVoiceKeySig(voices, ii) {
151
+ var curVoice = voices[ii];
152
+ // on multivoice multistaff only the first voice has key signature
153
+ // folling consecutive do not have one => we should provide the first voice key sig back then
154
+ var elem0 = curVoice.children[0].abcelem;
155
+ if (elem0.el_type === 'clef') return null;
156
+ if (ii == 0) {
157
+ // not found => clef=none case
158
+ return 'none';
159
+ }
160
+ return voices[ii - 1].children[0];
161
+ }
162
+
163
+ function tabRenderer(plugin, renderer, line, staffIndex) {
164
+ //console.log("RENDER tabRenderer")
165
+ var absolutes = new TabAbsoluteElements();
166
+ var tabStaff = {
167
+ clef: {
168
+ type: 'TAB'
169
+ }
170
+ };
171
+ var tabSize = (plugin.linePitch * plugin.nbLines);
172
+ var staffs = line.staff;
173
+ if (staffs) {
174
+ // give up on staffline=0 in key
175
+ var firstStaff = staffs[0];
176
+ if (firstStaff) {
177
+ if (firstStaff.clef) {
178
+ if (firstStaff.clef.stafflines == 0) {
179
+ plugin.setError("No tablatures when stafflines=0");
180
+ return;
181
+ }
182
+ }
183
+ }
184
+ staffs.splice(
185
+ staffs.length, 0,
186
+ tabStaff
187
+ );
188
+ }
189
+ var staffGroup = line.staffGroup;
190
+
191
+ var voices = staffGroup.voices;
192
+ var firstVoice = voices[0];
193
+ // take lyrics into account if any
194
+ var lyricsHeight = getLyricHeight(firstVoice);
195
+ var padd = 3;
196
+ var prevIndex = staffIndex;
197
+ var previousStaff = staffGroup.staffs[prevIndex];
198
+ var tabTop = tabSize + padd - previousStaff.bottom - lyricsHeight;
199
+ if (previousStaff.isTabStaff) {
200
+ tabTop = previousStaff.top;
201
+ }
202
+ var staffGroupInfos = {
203
+ bottom: -1,
204
+ isTabStaff: true,
205
+ specialY: initSpecialY(),
206
+ lines: plugin.nbLines,
207
+ linePitch: plugin.linePitch,
208
+ dy: 0.15,
209
+ top: tabTop,
210
+ };
211
+ var nextTabPos = getNextTabPos(staffIndex, staffGroup.staffs);
212
+ if (nextTabPos === -1)
213
+ return;
214
+ staffGroupInfos.parentIndex = nextTabPos - 1;
215
+ staffGroup.staffs.splice(nextTabPos, 0, staffGroupInfos);
216
+ // staffGroup.staffs.push(staffGroupInfos);
217
+ staffGroup.height += tabSize + padd;
218
+ var parentStaff = getLastStaff(staffGroup.staffs, nextTabPos);
219
+ var nbVoices = 1;
220
+ if (isMultiVoiceSingleStaff(staffGroup.staffs, parentStaff)) {
221
+ nbVoices = parentStaff.voices.length;
222
+ }
223
+ // build from staff
224
+ tabStaff.voices = [];
225
+ for (var ii = 0; ii < nbVoices; ii++) {
226
+ var tabVoice = new VoiceElement(0, 0);
227
+ if (ii > 0) tabVoice.duplicate = true;
228
+ var nameHeight = buildTabName(plugin, renderer, tabVoice) / spacing.STEP;
229
+ nameHeight = Math.max(nameHeight, 1) // If there is no label for the tab line, then there needs to be a little padding
230
+ // This was pushing down the top staff by the tab label height
231
+ //staffGroup.staffs[staffIndex].top += nameHeight;
232
+ staffGroup.staffs[staffIndex].top += 1;
233
+ staffGroup.height += nameHeight;
234
+ tabVoice.staff = staffGroupInfos;
235
+ var tabVoiceIndex = voices.length
236
+ voices.splice(voices.length, 0, tabVoice);
237
+ var keySig = checkVoiceKeySig(voices, ii + staffIndex);
238
+ tabStaff.voices[ii] = [];
239
+ absolutes.build(plugin, voices, tabStaff.voices[ii], ii, staffIndex, keySig, tabVoiceIndex);
240
+ }
241
+ linkStaffAndTabs(staffGroup.staffs); // crossreference tabs and staff
242
+ }
243
+
244
+ module.exports = tabRenderer;
@@ -48,7 +48,6 @@
48
48
  // Expanded:
49
49
  // MIDI is now { cmd, param }
50
50
 
51
- var parseCommon = require('../parse/abc_common');
52
51
  var JSONSchema = require('./jsonschema-b4');
53
52
 
54
53
  var ParserLint = function() {
@@ -76,7 +75,7 @@ var ParserLint = function() {
76
75
  };
77
76
 
78
77
  var appendPositioning = function(properties) {
79
- var ret = parseCommon.clone(properties);
78
+ var ret = Object.assign({},properties);
80
79
  ret.startChar = { type: 'number' }; //, output: 'hidden' };
81
80
  ret.endChar = { type: 'number' }; //, output: 'hidden' };
82
81
  return ret;
@@ -580,7 +579,7 @@ var ParserLint = function() {
580
579
  };
581
580
 
582
581
  var addProhibits = function(obj, arr) {
583
- var ret = parseCommon.clone(obj);
582
+ var ret = Object.assign({},obj);
584
583
  ret.prohibits = arr;
585
584
  return ret;
586
585
  };
@@ -833,7 +833,7 @@ AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaf
833
833
  if (elem.decoration) {
834
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
835
  // This should probably be combined with moveDecorations()
836
- var bottom = nostem ? Math.min(-3, abselem.bottom - 6) : abselem.bottom
836
+ var bottom = nostem && dir !== 'up' ? Math.min(-3, abselem.bottom - 6) : abselem.bottom
837
837
  this.decoration.createDecoration(voice, elem.decoration, abselem.top, (notehead) ? notehead.w : 0, abselem, roomtaken, dir, bottom, elem.positioning, this.hasVocals, this.accentAbove);
838
838
  }
839
839
 
@@ -225,4 +225,30 @@ TieElem.prototype.avoidCollisionAbove = function () {
225
225
  }
226
226
  };
227
227
 
228
+ TieElem.prototype.getYBounds = function () {
229
+ var lineStartX = 10 // TODO-PER: I'm not sure where to get this number from but it probably doesn't matter much
230
+ var lineEndX = 1000 // TODO-PER: I'm not sure where to get this number from but it probably doesn't matter much
231
+ if (this.isTie) {
232
+ this.calcTieDirection();
233
+ this.calcX(lineStartX, lineEndX);
234
+ this.calcTieY();
235
+
236
+ } else {
237
+ this.calcSlurDirection();
238
+ this.calcX(lineStartX, lineEndX);
239
+ this.calcSlurY();
240
+ }
241
+ var top;
242
+ var bottom;
243
+ // TODO-PER: It's hard to tell how far the arc is, so I'm just using 3 as the max
244
+ if (this.above) {
245
+ bottom = Math.min(this.startY, this.endY)
246
+ top = bottom + 3
247
+ } else {
248
+ top = Math.min(this.startY, this.endY)
249
+ bottom = top - 3
250
+ }
251
+ return [ top, bottom ]
252
+ };
253
+
228
254
  module.exports = TieElem;
@@ -16,7 +16,7 @@ var Classes = require('./helpers/classes');
16
16
  var GetFontAndAttr = require('./helpers/get-font-and-attr');
17
17
  var GetTextSize = require('./helpers/get-text-size');
18
18
  var draw = require('./draw/draw');
19
- var tablatures = require('../api/abc_tablatures');
19
+ var tablatures = require('../tablatures/abc_tablatures');
20
20
  var findSelectableElement = require('./interactive/find-selectable-element');
21
21
 
22
22
  /**
@@ -128,6 +128,14 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
128
128
  case 'EndingElem':
129
129
  setUpperAndLowerEndingElements(positionY, abselem);
130
130
  break;
131
+ case 'TieElem':
132
+ // If a tie element is the highest or lowest thing then space might need to make room for it.
133
+ var yBounds = abselem.getYBounds()
134
+ voice.staff.top = Math.max(voice.staff.top, yBounds[0])
135
+ voice.staff.top = Math.max(voice.staff.top, yBounds[1])
136
+ voice.staff.bottom = Math.min(voice.staff.bottom, yBounds[0])
137
+ voice.staff.bottom = Math.min(voice.staff.bottom, yBounds[1])
138
+ break;
131
139
  }
132
140
  }
133
141
  }
package/test.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**!
2
- Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
2
+ Copyright (c) 2009-2024 Paul Rosen and Gregory Dyke
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
package/types/index.d.ts CHANGED
@@ -379,7 +379,7 @@ declare module 'abcjs' {
379
379
  chordprog?: number;
380
380
  chordvol?: number;
381
381
  gchord?: string;
382
- }
382
+ }
383
383
 
384
384
  export interface SynthVisualOptions {
385
385
  displayLoop?: boolean;
@@ -886,7 +886,7 @@ declare module 'abcjs' {
886
886
  getBarLength: NumberFunction;
887
887
  getBeatLength: NumberFunction;
888
888
  getBeatsPerMeasure: NumberFunction;
889
- getBpm: NumberFunction;
889
+ getBpm: (tempo?:TempoProperties) => number;
890
890
  getMeter: () => Meter;
891
891
  getMeterFraction: () => MeterFraction;
892
892
  getPickupLength: NumberFunction;
package/version.js CHANGED
@@ -1,3 +1,3 @@
1
- var version = '6.4.1';
1
+ var version = '6.4.3';
2
2
 
3
3
  module.exports = version;