abcjs 6.0.0-beta.35 → 6.0.0-beta.39

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 (73) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +15 -6
  3. package/RELEASE.md +74 -0
  4. package/dist/abcjs-basic-min.js +2 -2
  5. package/dist/abcjs-basic-min.js.LICENSE +2 -2
  6. package/dist/abcjs-basic.js +2599 -328
  7. package/dist/abcjs-basic.js.map +1 -1
  8. package/dist/abcjs-plugin-min.js +2 -2
  9. package/dist/abcjs-plugin-min.js.LICENSE +2 -2
  10. package/dist/report-basic.html +37 -0
  11. package/dist/report-before-glyph-compress.html +37 -0
  12. package/dist/report-brown-ts-target-es5.html +37 -0
  13. package/dist/report-dev-orig-no-babel.html +37 -0
  14. package/dist/report-synth.html +37 -0
  15. package/docker-build.sh +1 -0
  16. package/glyphs.json +1 -0
  17. package/index.js +23 -1
  18. package/license.js +1 -1
  19. package/package.json +2 -1
  20. package/plugin.js +23 -1
  21. package/src/api/abc_tablatures.js +144 -0
  22. package/src/api/abc_tunebook.js +10 -1
  23. package/src/api/abc_tunebook_svg.js +18 -6
  24. package/src/data/abc_tune.js +26 -24
  25. package/src/edit/abc_editor.js +29 -11
  26. package/src/parse/abc_parse.js +4 -2
  27. package/src/parse/abc_parse_directive.js +12 -6
  28. package/src/parse/tune-builder.js +1 -1
  29. package/src/synth/abc_midi_flattener.js +11 -2
  30. package/src/synth/abc_midi_sequencer.js +4 -1
  31. package/src/synth/create-synth.js +105 -34
  32. package/src/synth/load-note.js +34 -65
  33. package/src/synth/place-note.js +63 -59
  34. package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
  35. package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
  36. package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
  37. package/src/tablatures/instruments/string-patterns.js +277 -0
  38. package/src/tablatures/instruments/string-tablature.js +56 -0
  39. package/src/tablatures/instruments/tab-note.js +282 -0
  40. package/src/tablatures/instruments/tab-notes.js +41 -0
  41. package/src/tablatures/instruments/violin/tab-violin.js +47 -0
  42. package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
  43. package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
  44. package/src/tablatures/tab-absolute-elements.js +310 -0
  45. package/src/tablatures/tab-common.js +29 -0
  46. package/src/tablatures/tab-renderer.js +243 -0
  47. package/src/tablatures/transposer.js +110 -0
  48. package/src/test/abc_parser_lint.js +3 -0
  49. package/src/write/abc_absolute_element.js +2 -2
  50. package/src/write/abc_engraver_controller.js +19 -11
  51. package/src/write/abc_glyphs.js +2 -0
  52. package/src/write/abc_relative_element.js +5 -3
  53. package/src/write/abc_renderer.js +5 -1
  54. package/src/write/draw/absolute.js +5 -1
  55. package/src/write/draw/draw.js +5 -6
  56. package/src/write/draw/non-music.js +3 -1
  57. package/src/write/draw/print-line.js +24 -0
  58. package/src/write/draw/relative.js +14 -2
  59. package/src/write/draw/selectables.js +9 -6
  60. package/src/write/draw/staff-group.js +44 -8
  61. package/src/write/draw/staff-line.js +3 -19
  62. package/src/write/draw/staff.js +15 -2
  63. package/src/write/draw/tab-line.js +40 -0
  64. package/src/write/draw/text.js +3 -0
  65. package/src/write/draw/voice.js +9 -1
  66. package/src/write/format-jazz-chord.js +2 -2
  67. package/src/write/layout/staffGroup.js +23 -1
  68. package/src/write/layout/voice.js +2 -1
  69. package/src/write/svg.js +2 -1
  70. package/temp.txt +0 -0
  71. package/test.js +23 -0
  72. package/types/index.d.ts +73 -25
  73. package/version.js +1 -1
@@ -17,74 +17,78 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
17
17
  len = 0.005; // Have some small audible length no matter how short the note is.
18
18
  var offlineCtx = new OfflineAC(2,Math.floor((len+fadeTimeSec)*sampleRate),sampleRate);
19
19
  var noteName = pitchToNoteName[sound.pitch];
20
- var noteBuffer = soundsCache[sound.instrument][noteName];
21
- if (noteBuffer === "error" || noteBuffer === "pending") { // If the note isn't available, just leave a blank spot
22
- // If the note is still pending by now that means an error happened when loading. There was probably a timeout.
23
- console.log("Didn't load note", sound.instrument, noteName, noteBuffer);
24
- return;
20
+ var noteBufferPromise = soundsCache[sound.instrument][noteName];
21
+
22
+ if (!noteBufferPromise) {
23
+ // if the note isn't present then just skip it - it will leave a blank spot in the audio.
24
+ return Promise.resolve();
25
25
  }
26
26
 
27
- // create audio buffer
28
- var source = offlineCtx.createBufferSource();
29
- source.buffer = noteBuffer;
27
+ return noteBufferPromise
28
+ .then(function (response) {
29
+ // create audio buffer
30
+ var source = offlineCtx.createBufferSource();
31
+ source.buffer = response.audioBuffer;
30
32
 
31
- // add gain
32
- // volume can be between 1 to 127. This translation to gain is just trial and error.
33
- // The smaller the first number, the more dynamic range between the quietest to loudest.
34
- // The larger the second number, the louder it will be in general.
35
- var volume = (sound.volume / 96) * volumeMultiplier;
36
- source.gainNode = offlineCtx.createGain();
33
+ // add gain
34
+ // volume can be between 1 to 127. This translation to gain is just trial and error.
35
+ // The smaller the first number, the more dynamic range between the quietest to loudest.
36
+ // The larger the second number, the louder it will be in general.
37
+ var volume = (sound.volume / 96) * volumeMultiplier;
38
+ source.gainNode = offlineCtx.createGain();
37
39
 
38
- // add pan if supported and present
39
- if (sound.pan && offlineCtx.createStereoPanner) {
40
- source.panNode = offlineCtx.createStereoPanner();
41
- source.panNode.pan.setValueAtTime(sound.pan, 0);
42
- }
43
- source.gainNode.gain.value = volume; // Math.min(2, Math.max(0, volume));
44
- source.gainNode.gain.linearRampToValueAtTime(source.gainNode.gain.value, len);
45
- source.gainNode.gain.linearRampToValueAtTime(0.0, len + fadeTimeSec);
40
+ // add pan if supported and present
41
+ if (sound.pan && offlineCtx.createStereoPanner) {
42
+ source.panNode = offlineCtx.createStereoPanner();
43
+ source.panNode.pan.setValueAtTime(sound.pan, 0);
44
+ }
45
+ source.gainNode.gain.value = volume; // Math.min(2, Math.max(0, volume));
46
+ source.gainNode.gain.linearRampToValueAtTime(source.gainNode.gain.value, len);
47
+ source.gainNode.gain.linearRampToValueAtTime(0.0, len + fadeTimeSec);
46
48
 
47
- if (sound.cents) {
48
- source.playbackRate.value = centsToFactor(sound.cents);
49
- }
49
+ if (sound.cents) {
50
+ source.playbackRate.value = centsToFactor(sound.cents);
51
+ }
50
52
 
51
- // connect all the nodes
52
- if (source.panNode) {
53
- source.panNode.connect(offlineCtx.destination);
54
- source.gainNode.connect(source.panNode);
55
- } else {
56
- source.gainNode.connect(offlineCtx.destination);
57
- }
58
- source.connect(source.gainNode);
53
+ // connect all the nodes
54
+ if (source.panNode) {
55
+ source.panNode.connect(offlineCtx.destination);
56
+ source.gainNode.connect(source.panNode);
57
+ } else {
58
+ source.gainNode.connect(offlineCtx.destination);
59
+ }
60
+ source.connect(source.gainNode);
59
61
 
60
- // Do the process of creating the sound and placing it in the buffer
61
- source.start(0);
62
+ // Do the process of creating the sound and placing it in the buffer
63
+ source.start(0);
62
64
 
63
- if (source.noteOff) {
64
- source.noteOff(len + fadeTimeSec);
65
- } else {
66
- source.stop(len + fadeTimeSec);
67
- }
68
- var fnResolve;
69
- offlineCtx.oncomplete = function(e) {
70
- if (e.renderedBuffer) { // If the system gets overloaded then this can start failing. Just drop the note if so.
71
- for (var i = 0; i < startArray.length; i++) {
72
- //Math.floor(startArray[i] * sound.tempoMultiplier * sampleRate)
73
- var start = startArray[i] * sound.tempoMultiplier;
74
- if (ofsMs)
75
- start -=ofsMs/1000;
76
- if (start < 0)
77
- start = 0; // If the item that is moved back is at the very beginning of the buffer then don't move it back. To do that would be to push everything else forward. TODO-PER: this should probably be done at some point but then it would change timing in existing apps.
78
- start = Math.floor(start*sampleRate);
79
- copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
65
+ if (source.noteOff) {
66
+ source.noteOff(len + fadeTimeSec);
67
+ } else {
68
+ source.stop(len + fadeTimeSec);
80
69
  }
81
- }
82
- fnResolve();
83
- };
84
- offlineCtx.startRendering();
85
- return new Promise(function(resolve, reject) {
86
- fnResolve = resolve;
87
- });
70
+ var fnResolve;
71
+ offlineCtx.oncomplete = function(e) {
72
+ if (e.renderedBuffer) { // If the system gets overloaded then this can start failing. Just drop the note if so.
73
+ for (var i = 0; i < startArray.length; i++) {
74
+ //Math.floor(startArray[i] * sound.tempoMultiplier * sampleRate)
75
+ var start = startArray[i] * sound.tempoMultiplier;
76
+ if (ofsMs)
77
+ start -=ofsMs/1000;
78
+ if (start < 0)
79
+ start = 0; // If the item that is moved back is at the very beginning of the buffer then don't move it back. To do that would be to push everything else forward. TODO-PER: this should probably be done at some point but then it would change timing in existing apps.
80
+ start = Math.floor(start*sampleRate);
81
+ copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
82
+ }
83
+ }
84
+ fnResolve();
85
+ };
86
+ offlineCtx.startRendering();
87
+ return new Promise(function(resolve) {
88
+ fnResolve = resolve;
89
+ });
90
+ })
91
+ .catch(function () {});
88
92
  }
89
93
 
90
94
  var copyToChannel = function(toBuffer, fromBuffer, start) {
@@ -0,0 +1,19 @@
1
+
2
+ /**
3
+ * Dedicated fonts for violin tabs
4
+ */
5
+
6
+ /**
7
+ * Set here the fonts used by renderer/drawer
8
+ * for the violin plugin
9
+ * @param {} tune
10
+ */
11
+ // eslint-disable-next-line no-unused-vars
12
+ function setGuitarFonts(tune) {
13
+ /* enhance or change instrument fonts here */
14
+ // tune.formatting.tabnumberfont = { face: "\"Times New Roman\"", size: 9, weight: "normal", style: "normal", decoration: "none" };
15
+ // tune.formatting.tabgracefont = { face: "\"Times New Roman\"", size: 7, weight: "normal", style: "normal", decoration: "none" };
16
+ }
17
+
18
+ module.exports = setGuitarFonts;
19
+
@@ -0,0 +1,23 @@
1
+ var StringPatterns = require('../string-patterns');
2
+
3
+ function GuitarPatterns(plugin) {
4
+ this.tuning = plugin._super.params.tuning;
5
+ if (!this.tuning) {
6
+ this.tuning = ['E,', 'A', 'D', 'G' , 'B' , 'e'];
7
+ }
8
+ plugin.tuning = this.tuning;
9
+ this.strings = new StringPatterns(plugin);
10
+ }
11
+
12
+ GuitarPatterns.prototype.notesToNumber = function (notes, graces) {
13
+ var converter = this.strings;
14
+ return converter.notesToNumber(notes, graces);
15
+ };
16
+
17
+ GuitarPatterns.prototype.stringToPitch = function (stringNumber) {
18
+ var converter = this.strings;
19
+ return converter.stringToPitch(stringNumber);
20
+ };
21
+
22
+
23
+ module.exports = GuitarPatterns;
@@ -0,0 +1,50 @@
1
+ /*
2
+ Emit tab for Guitar staff
3
+ */
4
+ var StringTablature = require('../string-tablature');
5
+ var TabCommon = require('../../tab-common');
6
+ var TabRenderer = require('../../tab-renderer');
7
+ var GuitarPatterns = require('./guitar-patterns');
8
+ var setGuitarFonts = require('./guitar-fonts');
9
+
10
+ /**
11
+ * upon init mainly store provided instances for later usage
12
+ * @param {*} abcTune the parsed tune AST tree
13
+ * @param {*} tuneNumber the parsed tune AST tree
14
+ * @param {*} params complementary args provided to Tablature Plugin
15
+ */
16
+ Plugin.prototype.init = function (abcTune, tuneNumber, params) {
17
+ var _super = new TabCommon(abcTune, tuneNumber, params);
18
+ this._super = _super;
19
+ this.abcTune = abcTune;
20
+ this.linePitch = 3;
21
+ this.nbLines = 6;
22
+ this.isTabBig = true;
23
+ this.capo = params.capo;
24
+ this.transpose = params.visualTranspose;
25
+ this.tablature = new StringTablature(this.nbLines,
26
+ this.linePitch);
27
+
28
+ var semantics = new GuitarPatterns(this);
29
+ this.semantics = semantics;
30
+ };
31
+
32
+ Plugin.prototype.render = function (renderer, line, staffIndex) {
33
+ if (this._super.inError) return;
34
+ if (this.tablature.bypass(line)) return;
35
+ setGuitarFonts(this.abcTune);
36
+ var rndrer = new TabRenderer(this, renderer, line, staffIndex);
37
+ rndrer.doLayout();
38
+ };
39
+
40
+ function Plugin() {}
41
+
42
+ //
43
+ // Tablature plugin definition
44
+ //
45
+ var AbcGuitarTab = function () {
46
+ return { name: 'GuitarTab', tablature: Plugin };
47
+ };
48
+
49
+
50
+ module.exports = AbcGuitarTab;
@@ -0,0 +1,277 @@
1
+ var TabNote = require('./tab-note');
2
+ var TabNotes = require('./tab-notes');
3
+
4
+
5
+ function buildCapo(self) {
6
+ var capoTuning = null;
7
+ var tuning = self.tuning;
8
+ if (self.capo > 0) {
9
+ capoTuning = [];
10
+ for (var iii = 0; iii < tuning.length; iii++) {
11
+ var curNote = new TabNote.TabNote(tuning[iii]);
12
+ for (var jjj = 0; jjj < self.capo; jjj++) {
13
+ curNote = curNote.nextNote();
14
+ }
15
+ capoTuning[iii] = curNote.emit();
16
+ }
17
+ }
18
+ return capoTuning;
19
+ }
20
+
21
+ function buildPatterns(self) {
22
+ var strings = [];
23
+ var tuning = self.tuning;
24
+ if (self.capo > 0) {
25
+ tuning = self.capoTuning;
26
+ }
27
+ var pos = tuning.length - 1;
28
+ for (var iii = 0; iii < tuning.length; iii++) {
29
+ var nextNote = self.highestNote; // highest handled note
30
+ if (iii != tuning.length - 1) {
31
+ nextNote = tuning[iii + 1];
32
+ }
33
+ var tabNotes = new TabNotes(tuning[iii],nextNote);
34
+ var stringNotes = tabNotes.build();
35
+ if (stringNotes.error) {
36
+ return stringNotes;
37
+ }
38
+ strings[pos--] = stringNotes;
39
+ }
40
+ return strings;
41
+ }
42
+
43
+
44
+ function buildSecond(first) {
45
+ var seconds = [];
46
+ seconds[0] = [];
47
+ var strings = first.strings;
48
+ for (var iii = 1; iii < strings.length; iii++) {
49
+ seconds[iii] = strings[iii - 1];
50
+ }
51
+ return seconds;
52
+ }
53
+
54
+ function sameString(self, chord) {
55
+ for (var jjjj = 0; jjjj < chord.length - 1; jjjj++) {
56
+ var curPos = chord[jjjj];
57
+ var nextPos = chord[jjjj + 1];
58
+ if (curPos.str == nextPos.str) {
59
+ // same String
60
+ // => change lower pos
61
+ if (curPos.str == self.strings.length - 1) {
62
+ // Invalid tab Chord position for instrument
63
+ curPos.num = "?";
64
+ nextPos.num = "?";
65
+ return;
66
+ }
67
+ // change lower pitch on lowest string
68
+ if (nextPos.num < curPos.num) {
69
+ nextPos.str++;
70
+ nextPos = noteToNumber(self,
71
+ nextPos.note,
72
+ nextPos.str,
73
+ self.secondPos,
74
+ self.strings[nextPos.str].length
75
+ );
76
+ } else {
77
+ curPos.str++;
78
+ curPos = noteToNumber(self,
79
+ curPos.note,
80
+ curPos.str,
81
+ self.secondPos,
82
+ self.strings[curPos.str].length
83
+ );
84
+ }
85
+ // update table
86
+ chord[jjjj] = curPos;
87
+ chord[jjjj + 1] = nextPos;
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+
93
+ function handleChordNotes(self, notes) {
94
+ var retNotes = [];
95
+ for (var iiii = 0; iiii < notes.length; iiii++) {
96
+ var note = new TabNote.TabNote(notes[iiii].name);
97
+ var curPos = toNumber(self, note);
98
+ retNotes.push(curPos);
99
+ }
100
+ sameString(self, retNotes);
101
+ return retNotes;
102
+ }
103
+
104
+ function noteToNumber(self, note, stringNumber, secondPosition , firstSize) {
105
+ var strings = self.strings;
106
+ note.checkKeyAccidentals(self.accidentals) ;
107
+ if (secondPosition) {
108
+ strings = secondPosition;
109
+ }
110
+ var noteName = note.emitNoAccidentals();
111
+ var num = strings[stringNumber].indexOf(noteName);
112
+ var acc = note.acc;
113
+ if (num != -1) {
114
+ if (secondPosition) {
115
+ num += firstSize;
116
+ }
117
+ if ( (note.isFlat || note.acc == -1) && (num == 0)) {
118
+ // flat on 0 pos => previous string 7th position
119
+ var noteEquiv = note.getAccidentalEquiv();
120
+ stringNumber++;
121
+ num = strings[stringNumber].indexOf(noteEquiv.emit());
122
+ acc = 0;
123
+ }
124
+ return {
125
+ num: (num + acc),
126
+ str: stringNumber,
127
+ note: note
128
+ };
129
+ }
130
+ return null;
131
+ }
132
+
133
+ function toNumber(self, note) {
134
+ var num = null;
135
+ var str = 0;
136
+ var lowestString = self.strings[self.strings.length - 1];
137
+ var lowestNote = new TabNote.TabNote(lowestString[0]);
138
+ if (note.isLowerThan(lowestNote) ) {
139
+ return {
140
+ num: "?",
141
+ str: self.strings.length - 1,
142
+ note: note,
143
+ error: note.emit() + ': unexpected note for instrument'
144
+ };
145
+ }
146
+ while (str < self.strings.length) {
147
+ num = noteToNumber(self, note, str);
148
+ if (num) {
149
+ return num;
150
+ }
151
+ str++;
152
+ }
153
+ return null; // not found
154
+ }
155
+
156
+ StringPatterns.prototype.stringToPitch = function (stringNumber) {
157
+ var startingPitch = 5.3;
158
+ var bottom = this.strings.length - 1;
159
+ return startingPitch + ((bottom - stringNumber) * this.linePitch);
160
+ };
161
+
162
+ function invalidNumber( retNotes , note ) {
163
+ var number = {
164
+ num: "?",
165
+ str: 0,
166
+ note: note
167
+ };
168
+ retNotes.push(number);
169
+ retNotes.error = note.emit() + ': unexpected note for instrument' ;
170
+ }
171
+
172
+ StringPatterns.prototype.notesToNumber = function (notes, graces) {
173
+ var note;
174
+ var number;
175
+ var error = null;
176
+ var retNotes = null;
177
+ if (notes) {
178
+ retNotes = [];
179
+ if (notes.length > 1) {
180
+ retNotes = handleChordNotes(this, notes);
181
+ if (retNotes.error) {
182
+ error = retNotes.error;
183
+ }
184
+ } else {
185
+ note = new TabNote.TabNote(notes[0].name);
186
+ number = toNumber(this, note);
187
+ if (number) {
188
+ retNotes.push(number);
189
+ } else {
190
+ invalidNumber(retNotes, note);
191
+ error = retNotes.error;
192
+ }
193
+ }
194
+ }
195
+ if (error) return retNotes;
196
+ var retGraces = null;
197
+ if (graces) {
198
+ retGraces = [];
199
+ for (var iiii = 0; iiii < graces.length; iiii++) {
200
+ note = new TabNote.TabNote(graces[iiii].name);
201
+ number = toNumber(this, note);
202
+ if (number) {
203
+ retGraces.push(number);
204
+ } else {
205
+ invalidNumber(retGraces, note);
206
+ error = retNotes.error;
207
+ }
208
+ }
209
+ }
210
+
211
+ return {
212
+ notes: retNotes,
213
+ graces: retGraces,
214
+ error: error
215
+ };
216
+ };
217
+
218
+ StringPatterns.prototype.toString = function () {
219
+ return this.tuning.join('').replaceAll(',', '').toUpperCase();
220
+ };
221
+
222
+ StringPatterns.prototype.tabInfos = function (plugin) {
223
+ var _super = plugin._super;
224
+ var name = _super.params.label;
225
+ if (name) {
226
+ var tunePos = name.indexOf('%T');
227
+ var tuning = "";
228
+ if (tunePos != -1) {
229
+ tuning = this.toString();
230
+ if (plugin.capo > 0) {
231
+ tuning += ' capo:' + plugin.capo;
232
+ }
233
+ name = name.replace('%T', tuning);
234
+ }
235
+ return name;
236
+ }
237
+ return '';
238
+ };
239
+
240
+ /**
241
+ * Common patterns for all string instruments
242
+ * @param {} plugin
243
+ * @param {} tuning
244
+ * @param {*} capo
245
+ * @param {*} highestNote
246
+ */
247
+ function StringPatterns(plugin) {
248
+ var tuning = plugin.tuning;
249
+ var capo = plugin.capo;
250
+ var highestNote = plugin._super.params.highestNote;
251
+ this.linePitch = plugin.linePitch;
252
+ this.highestNote = "a'";
253
+ if (highestNote) {
254
+ // override default
255
+ this.highestNote = highestNote;
256
+ }
257
+ this.capo = 0;
258
+ if (capo) {
259
+ this.capo = capo;
260
+ }
261
+ this.tuning = tuning;
262
+ if (this.capo > 0) {
263
+ this.capoTuning = buildCapo(this);
264
+ }
265
+ this.strings = buildPatterns(this);
266
+ if (this.strings.error) {
267
+ plugin._super.setError(this.strings.error);
268
+ plugin.inError = true;
269
+ return;
270
+ }
271
+ // second position pattern per string
272
+ this.secondPos = buildSecond(this);
273
+ }
274
+
275
+
276
+
277
+ module.exports = StringPatterns;
@@ -0,0 +1,56 @@
1
+
2
+ /**
3
+ * Layout tablature informations for draw
4
+ * @param {*} numLines
5
+ * @param {*} lineSpace
6
+ */
7
+
8
+ function StringTablature(numLines, lineSpace) {
9
+ this.numLines = numLines;
10
+ this.lineSpace = lineSpace;
11
+ this.verticalSize = this.numLines * this.lineSpace;
12
+ var pitch = 3;
13
+ this.bar = {
14
+ pitch: pitch,
15
+ pitch2: lineSpace * numLines,
16
+ height: 5,
17
+ };
18
+ }
19
+
20
+ /**
21
+ * return true if current line should not produce a tab
22
+ * @param {} line
23
+ */
24
+ StringTablature.prototype.bypass = function (line) {
25
+ var voices = line.staffGroup.voices;
26
+ if (voices.length > 0) {
27
+ if (voices[0].isPercussion) return true;
28
+ }
29
+ return false;
30
+ };
31
+
32
+
33
+ StringTablature.prototype.setRelative = function (child, relative, first) {
34
+ switch (child.type) {
35
+ case 'bar':
36
+ relative.pitch = this.bar.pitch;
37
+ relative.pitch2 = this.bar.pitch2;
38
+ relative.height = this.height;
39
+ break;
40
+ case 'symbol':
41
+ var top = this.bar.pitch2 / 2;
42
+ if (child.name == 'dots.dot') {
43
+ if (first) {
44
+ relative.pitch = top;
45
+ return false;
46
+ } else {
47
+ relative.pitch = top + this.lineSpace;
48
+ return true;
49
+ }
50
+ }
51
+ break;
52
+ }
53
+ return first;
54
+ };
55
+
56
+ module.exports = StringTablature;