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.
- package/LICENSE.md +1 -1
- package/README.md +15 -6
- package/RELEASE.md +74 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic-min.js.LICENSE +2 -2
- package/dist/abcjs-basic.js +2599 -328
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/dist/abcjs-plugin-min.js.LICENSE +2 -2
- package/dist/report-basic.html +37 -0
- package/dist/report-before-glyph-compress.html +37 -0
- package/dist/report-brown-ts-target-es5.html +37 -0
- package/dist/report-dev-orig-no-babel.html +37 -0
- package/dist/report-synth.html +37 -0
- package/docker-build.sh +1 -0
- package/glyphs.json +1 -0
- package/index.js +23 -1
- package/license.js +1 -1
- package/package.json +2 -1
- package/plugin.js +23 -1
- package/src/api/abc_tablatures.js +144 -0
- package/src/api/abc_tunebook.js +10 -1
- package/src/api/abc_tunebook_svg.js +18 -6
- package/src/data/abc_tune.js +26 -24
- package/src/edit/abc_editor.js +29 -11
- package/src/parse/abc_parse.js +4 -2
- package/src/parse/abc_parse_directive.js +12 -6
- package/src/parse/tune-builder.js +1 -1
- package/src/synth/abc_midi_flattener.js +11 -2
- package/src/synth/abc_midi_sequencer.js +4 -1
- package/src/synth/create-synth.js +105 -34
- package/src/synth/load-note.js +34 -65
- package/src/synth/place-note.js +63 -59
- package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
- package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
- package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
- package/src/tablatures/instruments/string-patterns.js +277 -0
- package/src/tablatures/instruments/string-tablature.js +56 -0
- package/src/tablatures/instruments/tab-note.js +282 -0
- package/src/tablatures/instruments/tab-notes.js +41 -0
- package/src/tablatures/instruments/violin/tab-violin.js +47 -0
- package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
- package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
- package/src/tablatures/tab-absolute-elements.js +310 -0
- package/src/tablatures/tab-common.js +29 -0
- package/src/tablatures/tab-renderer.js +243 -0
- package/src/tablatures/transposer.js +110 -0
- package/src/test/abc_parser_lint.js +3 -0
- package/src/write/abc_absolute_element.js +2 -2
- package/src/write/abc_engraver_controller.js +19 -11
- package/src/write/abc_glyphs.js +2 -0
- package/src/write/abc_relative_element.js +5 -3
- package/src/write/abc_renderer.js +5 -1
- package/src/write/draw/absolute.js +5 -1
- package/src/write/draw/draw.js +5 -6
- package/src/write/draw/non-music.js +3 -1
- package/src/write/draw/print-line.js +24 -0
- package/src/write/draw/relative.js +14 -2
- package/src/write/draw/selectables.js +9 -6
- package/src/write/draw/staff-group.js +44 -8
- package/src/write/draw/staff-line.js +3 -19
- package/src/write/draw/staff.js +15 -2
- package/src/write/draw/tab-line.js +40 -0
- package/src/write/draw/text.js +3 -0
- package/src/write/draw/voice.js +9 -1
- package/src/write/format-jazz-chord.js +2 -2
- package/src/write/layout/staffGroup.js +23 -1
- package/src/write/layout/voice.js +2 -1
- package/src/write/svg.js +2 -1
- package/temp.txt +0 -0
- package/test.js +23 -0
- package/types/index.d.ts +73 -25
- package/version.js +1 -1
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Note structure for Tabs
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
var notes = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
function TabNote(note) {
|
|
10
|
+
var isFlat = false;
|
|
11
|
+
var newNote = note;
|
|
12
|
+
var isSharp = false;
|
|
13
|
+
var isAltered = false;
|
|
14
|
+
var natural = null;
|
|
15
|
+
var quarter = null;
|
|
16
|
+
var isDouble = false;
|
|
17
|
+
var acc = 0;
|
|
18
|
+
|
|
19
|
+
if (note.startsWith('_')) {
|
|
20
|
+
isFlat = true;
|
|
21
|
+
acc = -1;
|
|
22
|
+
// check quarter flat
|
|
23
|
+
if (note[1] == '/') {
|
|
24
|
+
isFlat = false;
|
|
25
|
+
quarter = "v";
|
|
26
|
+
acc = 0;
|
|
27
|
+
} else if (note[1] == '_') {
|
|
28
|
+
// double flat
|
|
29
|
+
isDouble = true;
|
|
30
|
+
acc -= 1;
|
|
31
|
+
}
|
|
32
|
+
} else if (note.startsWith('^')) {
|
|
33
|
+
isSharp = true;
|
|
34
|
+
acc = +1;
|
|
35
|
+
// check quarter sharp
|
|
36
|
+
if (note[1] == '/') {
|
|
37
|
+
isSharp = false;
|
|
38
|
+
quarter = "^";
|
|
39
|
+
acc = 0;
|
|
40
|
+
} else if (note[1] == '^') {
|
|
41
|
+
// double sharp
|
|
42
|
+
isDouble = true;
|
|
43
|
+
acc += 1;
|
|
44
|
+
}
|
|
45
|
+
} else if (note.startsWith('=')) {
|
|
46
|
+
natural = true;
|
|
47
|
+
acc = 0;
|
|
48
|
+
}
|
|
49
|
+
isAltered = isFlat || isSharp || (quarter != null);
|
|
50
|
+
if (isAltered || natural) {
|
|
51
|
+
if ((quarter != null) || (isDouble)) {
|
|
52
|
+
newNote = note.slice(2);
|
|
53
|
+
} else {
|
|
54
|
+
newNote = note.slice(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var hasComma = (note.match(/,/g) || []).length;
|
|
58
|
+
var hasQuote = (note.match(/'/g) || []).length;
|
|
59
|
+
|
|
60
|
+
this.name = newNote;
|
|
61
|
+
this.acc = acc;
|
|
62
|
+
this.isSharp = isSharp;
|
|
63
|
+
this.isKeySharp = false;
|
|
64
|
+
this.isDouble = isDouble;
|
|
65
|
+
this.isAltered = isAltered;
|
|
66
|
+
this.isFlat = isFlat;
|
|
67
|
+
this.isKeyFlat = false;
|
|
68
|
+
this.natural = natural;
|
|
69
|
+
this.quarter = quarter;
|
|
70
|
+
this.isLower = (this.name == this.name.toLowerCase());
|
|
71
|
+
this.name = this.name[0].toUpperCase();
|
|
72
|
+
this.hasComma = hasComma;
|
|
73
|
+
this.isQuoted = hasQuote;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function cloneNote(self) {
|
|
77
|
+
var newNote = self.name;
|
|
78
|
+
var newTabNote = new TabNote(newNote);
|
|
79
|
+
newTabNote.hasComma = self.hasComma;
|
|
80
|
+
newTabNote.isLower = self.isLower;
|
|
81
|
+
newTabNote.isQuoted = self.isQuoted;
|
|
82
|
+
newTabNote.isSharp = self.isSharp;
|
|
83
|
+
newTabNote.isKeySharp = self.isKeySharp;
|
|
84
|
+
newTabNote.isFlat = self.isFlat;
|
|
85
|
+
newTabNote.isKeyFlat = self.isKeyFlat;
|
|
86
|
+
return newTabNote;
|
|
87
|
+
}
|
|
88
|
+
TabNote.prototype.sameNoteAs = function (note) {
|
|
89
|
+
if ((this.name == note.name) &&
|
|
90
|
+
(this.hasComma == note.hasComma) &&
|
|
91
|
+
(this.isLower == note.isLower) &&
|
|
92
|
+
(this.isQuoted == note.isQuoted) &&
|
|
93
|
+
(this.isSharp == note.isSharp) &&
|
|
94
|
+
(this.isFlat == note.isFlat)) {
|
|
95
|
+
return true;
|
|
96
|
+
} else {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
TabNote.prototype.isLowerThan = function (note) {
|
|
102
|
+
var noteComparator = ['C','D','E','F','G','A','B'];
|
|
103
|
+
if (this.hasComma > note.hasComma) return true;
|
|
104
|
+
if (note.hasComma > this.hasComma) return false;
|
|
105
|
+
if (this.isQuoted > note.isQuoted) return false;
|
|
106
|
+
if (note.isQuoted > this.isQuoted) return true;
|
|
107
|
+
if (this.isLower) {
|
|
108
|
+
if (!note.isLower) return false;
|
|
109
|
+
} else {
|
|
110
|
+
if (note.isLower) return true;
|
|
111
|
+
}
|
|
112
|
+
var noteName = note.name[0].toUpperCase();
|
|
113
|
+
var thisName = this.name[0].toUpperCase();
|
|
114
|
+
if (noteComparator.indexOf(thisName) < noteComparator.indexOf(noteName)) return true;
|
|
115
|
+
return false;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
TabNote.prototype.checkKeyAccidentals = function(accidentals) {
|
|
119
|
+
if (accidentals) {
|
|
120
|
+
var curNote = this.name;
|
|
121
|
+
for (var iii = 0; iii < accidentals.length; iii++) {
|
|
122
|
+
var curAccidentals = accidentals[iii];
|
|
123
|
+
if (curNote == curAccidentals.note.toUpperCase()) {
|
|
124
|
+
if (curAccidentals.acc == 'flat') {
|
|
125
|
+
this.acc = -1;
|
|
126
|
+
this.isKeyFlat = true;
|
|
127
|
+
}
|
|
128
|
+
if (curAccidentals.acc == 'sharp') {
|
|
129
|
+
this.acc = +1;
|
|
130
|
+
this.isKeySharp = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
TabNote.prototype.getAccidentalEquiv = function () {
|
|
138
|
+
var cloned = cloneNote(this);
|
|
139
|
+
if (cloned.isSharp || cloned.isKeySharp ) {
|
|
140
|
+
cloned = cloned.nextNote();
|
|
141
|
+
cloned.isFlat = true;
|
|
142
|
+
cloned.isSharp = false;
|
|
143
|
+
cloned.isKeySharp = false;
|
|
144
|
+
} else if (cloned.isFlat || cloned.isKeyFlat ) {
|
|
145
|
+
cloned = cloned.prevNote();
|
|
146
|
+
cloned.isSharp = true;
|
|
147
|
+
cloned.isFlat = false;
|
|
148
|
+
cloned.isKeyFlat = false;
|
|
149
|
+
}
|
|
150
|
+
return cloned;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
TabNote.prototype.nextNote = function () {
|
|
155
|
+
var newTabNote = cloneNote(this);
|
|
156
|
+
|
|
157
|
+
if (!this.isSharp && !this.isKeySharp ) {
|
|
158
|
+
if (this.name != 'E' && this.name != 'B') {
|
|
159
|
+
newTabNote.isSharp = true;
|
|
160
|
+
return newTabNote;
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
// cleanup
|
|
164
|
+
newTabNote.isSharp = false;
|
|
165
|
+
newTabNote.isKeySharp = false;
|
|
166
|
+
}
|
|
167
|
+
var noteIndex = notes.indexOf(this.name);
|
|
168
|
+
if (noteIndex == notes.length - 1) {
|
|
169
|
+
noteIndex = 0;
|
|
170
|
+
} else {
|
|
171
|
+
noteIndex++;
|
|
172
|
+
}
|
|
173
|
+
newTabNote.name = notes[noteIndex];
|
|
174
|
+
if (newTabNote.name == 'C') {
|
|
175
|
+
if (newTabNote.hasComma > 0) {
|
|
176
|
+
newTabNote.hasComma--;
|
|
177
|
+
} else {
|
|
178
|
+
if (!newTabNote.isLower) {
|
|
179
|
+
newTabNote.isLower = true;
|
|
180
|
+
} else {
|
|
181
|
+
newTabNote.isQuoted = true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return newTabNote;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
TabNote.prototype.prevNote = function () {
|
|
189
|
+
var newTabNote = cloneNote(this);
|
|
190
|
+
|
|
191
|
+
if (this.isSharp) {
|
|
192
|
+
newTabNote.isSharp = false;
|
|
193
|
+
return newTabNote;
|
|
194
|
+
}
|
|
195
|
+
var noteIndex = notes.indexOf(this.name);
|
|
196
|
+
if (noteIndex == 0) {
|
|
197
|
+
noteIndex = notes.length - 1;
|
|
198
|
+
} else {
|
|
199
|
+
noteIndex--;
|
|
200
|
+
}
|
|
201
|
+
newTabNote.name = notes[noteIndex];
|
|
202
|
+
if (newTabNote.name == 'B') {
|
|
203
|
+
if (newTabNote.isLower) {
|
|
204
|
+
newTabNote.hasComma = 1;
|
|
205
|
+
} else {
|
|
206
|
+
if (newTabNote.hasComma > 0) {
|
|
207
|
+
newTabNote.hasComma++;
|
|
208
|
+
} else {
|
|
209
|
+
if (newTabNote.isQuoted > 0) {
|
|
210
|
+
newTabNote.isQuoted -= 1;
|
|
211
|
+
} else {
|
|
212
|
+
newTabNote.isLower = true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (this.isFlat) {
|
|
218
|
+
newTabNote.isFlat = false;
|
|
219
|
+
return newTabNote;
|
|
220
|
+
} else {
|
|
221
|
+
if (this.name != 'E' && this.name != 'B') {
|
|
222
|
+
newTabNote.isSharp = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return newTabNote;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
TabNote.prototype.emitNoAccidentals = function ( ) {
|
|
229
|
+
var returned = this.name;
|
|
230
|
+
if (this.isLower) {
|
|
231
|
+
returned = returned.toLowerCase();
|
|
232
|
+
}
|
|
233
|
+
for (var ii = 0; ii < this.isQuoted; ii++) {
|
|
234
|
+
returned += "'";
|
|
235
|
+
}
|
|
236
|
+
for (var jj = 0; jj < this.hasComma; jj++) {
|
|
237
|
+
returned += ",";
|
|
238
|
+
}
|
|
239
|
+
return returned;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
TabNote.prototype.emit = function () {
|
|
243
|
+
var returned = this.name;
|
|
244
|
+
if (this.isSharp || this.isKeySharp ) {
|
|
245
|
+
returned = '^' + returned;
|
|
246
|
+
if (this.isDouble) {
|
|
247
|
+
returned = '^' + returned;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (this.isFlat || this.isKeyFlat) {
|
|
251
|
+
returned = '_' + returned;
|
|
252
|
+
if (this.isDouble) {
|
|
253
|
+
returned = '_' + returned;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (this.quarter) {
|
|
257
|
+
if (this.quarter == "^") {
|
|
258
|
+
returned = "^/" + returned;
|
|
259
|
+
} else {
|
|
260
|
+
returned = "_/" + returned;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (this.natural) {
|
|
264
|
+
returned = '=' + returned;
|
|
265
|
+
}
|
|
266
|
+
for (var ii = 1; ii <= this.hasComma; ii++) {
|
|
267
|
+
returned += ',';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (this.isLower) {
|
|
271
|
+
returned = returned.toLowerCase();
|
|
272
|
+
for (var jj = 1; jj <= this.isQuoted; jj++) {
|
|
273
|
+
returned += "'";
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return returned;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
module.exports = {
|
|
280
|
+
'TabNote': TabNote,
|
|
281
|
+
'notes': notes
|
|
282
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
|
|
2
|
+
var TabNote = require('./tab-note');
|
|
3
|
+
|
|
4
|
+
var notes = TabNote.notes;
|
|
5
|
+
|
|
6
|
+
function TabNotes(fromNote, toNote) {
|
|
7
|
+
this.fromN = new TabNote.TabNote(fromNote);
|
|
8
|
+
this.toN = new TabNote.TabNote(toNote);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
TabNotes.prototype.build = function () {
|
|
13
|
+
var fromN = this.fromN;
|
|
14
|
+
var toN = this.toN;
|
|
15
|
+
// check that toN is not lower than fromN
|
|
16
|
+
if (toN.isLowerThan(fromN)) {
|
|
17
|
+
var from = fromN.emit();
|
|
18
|
+
var tn = toN.emit();
|
|
19
|
+
return {
|
|
20
|
+
error: 'Invalid string Instrument tuning : ' +
|
|
21
|
+
tn + ' string lower than ' + from + ' string'
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
var buildReturned = [];
|
|
25
|
+
var startIndex = notes.indexOf(fromN.name);
|
|
26
|
+
var toIndex = notes.indexOf(toN.name);
|
|
27
|
+
if ((startIndex == -1) || (toIndex == -1)) {
|
|
28
|
+
return buildReturned;
|
|
29
|
+
}
|
|
30
|
+
var finished = false;
|
|
31
|
+
while (!finished) {
|
|
32
|
+
buildReturned.push(fromN.emit());
|
|
33
|
+
fromN = fromN.nextNote();
|
|
34
|
+
if (fromN.sameNoteAs(toN)) {
|
|
35
|
+
finished = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return buildReturned;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
module.exports = TabNotes;
|
|
@@ -0,0 +1,47 @@
|
|
|
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');
|
|
6
|
+
var setViolinFonts = require('./violin-fonts');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* upon init mainly store provided instances for later usage
|
|
11
|
+
* @param {*} abcTune the parsed tune AST tree
|
|
12
|
+
* @param {*} tuneNumber the parsed tune AST tree
|
|
13
|
+
* @param {*} params complementary args provided to Tablature Plugin
|
|
14
|
+
*/
|
|
15
|
+
Plugin.prototype.init = function (abcTune, tuneNumber, params) {
|
|
16
|
+
var _super = new TabCommon(abcTune, tuneNumber, params);
|
|
17
|
+
this.abcTune = abcTune;
|
|
18
|
+
this._super = _super;
|
|
19
|
+
this.linePitch = 3;
|
|
20
|
+
this.nbLines = 4;
|
|
21
|
+
this.isTabBig = false;
|
|
22
|
+
this.capo = params.capo;
|
|
23
|
+
this.transpose = params.visualTranspose;
|
|
24
|
+
this.tablature = new StringTablature(this.nbLines,
|
|
25
|
+
this.linePitch);
|
|
26
|
+
var semantics = new ViolinPatterns(this);
|
|
27
|
+
this.semantics = semantics;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
Plugin.prototype.render = function (renderer, line, staffIndex) {
|
|
31
|
+
if (this._super.inError) return;
|
|
32
|
+
if (this.tablature.bypass(line)) return;
|
|
33
|
+
setViolinFonts(this.abcTune);
|
|
34
|
+
var rndrer = new TabRenderer(this, renderer, line, staffIndex);
|
|
35
|
+
rndrer.doLayout();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function Plugin() {}
|
|
39
|
+
|
|
40
|
+
//
|
|
41
|
+
// Tablature plugin definition
|
|
42
|
+
//
|
|
43
|
+
var AbcViolinTab = function () {
|
|
44
|
+
return { name: 'ViolinTab', tablature: Plugin };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
module.exports = AbcViolinTab;
|
|
@@ -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 setViolinFonts(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 = setViolinFonts;
|
|
19
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
var StringPatterns = require('../string-patterns');
|
|
2
|
+
|
|
3
|
+
function ViolinPatterns(plugin) {
|
|
4
|
+
this.tuning = plugin._super.params.tuning;
|
|
5
|
+
if (!this.tuning) {
|
|
6
|
+
this.tuning = ['G,', 'D', 'A', 'e'];
|
|
7
|
+
}
|
|
8
|
+
plugin.tuning = this.tuning;
|
|
9
|
+
this.strings = new StringPatterns(plugin);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
ViolinPatterns.prototype.notesToNumber = function (notes, graces) {
|
|
13
|
+
var converter = this.strings;
|
|
14
|
+
return converter.notesToNumber(notes, graces);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
ViolinPatterns.prototype.stringToPitch = function (stringNumber) {
|
|
18
|
+
var converter = this.strings;
|
|
19
|
+
return converter.stringToPitch(stringNumber);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
module.exports = ViolinPatterns;
|