abcjs 6.0.0-beta.7 → 6.0.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.
- package/.github/workflows/tests.yml +29 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/CONTRIBUTING.md +1 -0
- package/LICENSE.md +1 -1
- package/README.md +92 -3
- package/RELEASE.md +957 -1
- package/abcjs-audio.css +14 -5
- package/dist/.gitignore +1 -2
- package/dist/abcjs-basic-min.js +3 -0
- package/dist/abcjs-basic-min.js.LICENSE +23 -0
- package/dist/abcjs-basic.js +28231 -0
- package/dist/abcjs-basic.js.map +1 -0
- package/dist/abcjs-plugin-min.js +3 -0
- package/dist/abcjs-plugin-min.js.LICENSE +23 -0
- 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 +27 -2
- package/{static-wrappers/license.js → license.js} +1 -1
- package/package.json +26 -29
- package/{src/plugin/abc_plugin.js → plugin.js} +31 -19
- package/src/api/abc_animation.js +1 -17
- package/src/api/abc_tablatures.js +144 -0
- package/src/api/abc_timing_callbacks.js +234 -116
- package/src/api/abc_tunebook.js +18 -67
- package/src/api/abc_tunebook_svg.js +38 -46
- package/src/data/abc_tune.js +232 -972
- package/src/data/deline-tune.js +199 -0
- package/src/edit/abc_editarea.js +112 -0
- package/src/edit/abc_editor.js +95 -221
- package/src/midi/abc_midi_create.js +48 -50
- package/src/parse/abc_common.js +0 -14
- package/src/parse/abc_parse.js +167 -1321
- package/src/parse/abc_parse_book.js +62 -0
- package/src/parse/abc_parse_directive.js +164 -41
- package/src/parse/abc_parse_header.js +116 -145
- package/src/parse/abc_parse_key_voice.js +26 -20
- package/src/parse/abc_parse_music.js +1337 -0
- package/src/parse/abc_tokenizer.js +21 -15
- package/src/parse/abc_transpose.js +3 -15
- package/src/parse/tune-builder.js +896 -0
- package/src/parse/wrap_lines.js +205 -453
- package/src/synth/abc_midi_flattener.js +1292 -0
- package/src/{midi → synth}/abc_midi_renderer.js +44 -17
- package/src/synth/abc_midi_sequencer.js +648 -0
- package/src/synth/active-audio-context.js +3 -14
- package/src/synth/cents-to-factor.js +10 -0
- package/src/synth/create-note-map.js +21 -32
- package/src/synth/create-synth-control.js +20 -103
- package/src/synth/create-synth.js +185 -77
- package/src/synth/download-buffer.js +7 -21
- package/src/synth/get-midi-file.js +13 -20
- package/src/synth/images/{loading.svg → loading.svg.js} +4 -0
- package/src/synth/images/loop.svg.js +65 -0
- package/src/synth/images/pause.svg.js +10 -0
- package/src/synth/images/play.svg.js +9 -0
- package/src/synth/images/{reset.svg → reset.svg.js} +5 -1
- package/src/synth/instrument-index-to-name.js +1 -16
- package/src/synth/load-note.js +37 -76
- package/src/synth/pitch-to-note-name.js +0 -15
- package/src/synth/pitches-to-perc.js +64 -0
- package/src/synth/place-note.js +78 -68
- package/src/synth/play-event.js +17 -18
- package/src/synth/register-audio-context.js +11 -23
- package/src/synth/sounds-cache.js +0 -15
- package/src/synth/supports-audio.js +9 -23
- package/src/synth/synth-controller.js +80 -49
- package/src/synth/synth-sequence.js +20 -34
- 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_midi_lint.js +5 -22
- package/src/test/abc_midi_sequencer_lint.js +11 -14
- package/src/test/abc_parser_lint.js +136 -32
- package/src/test/abc_vertical_lint.js +94 -32
- package/src/test/rendering-lint.js +38 -5
- package/src/write/abc_absolute_element.js +112 -120
- package/src/write/abc_abstract_engraver.js +102 -253
- package/src/write/abc_beam_element.js +30 -290
- package/src/write/abc_brace_element.js +12 -121
- package/src/write/abc_create_clef.js +21 -32
- package/src/write/abc_create_key_signature.js +8 -26
- package/src/write/abc_create_note_head.js +107 -0
- package/src/write/abc_create_time_signature.js +2 -21
- package/src/write/abc_crescendo_element.js +3 -50
- package/src/write/abc_decoration.js +7 -30
- package/src/write/abc_dynamic_decoration.js +3 -37
- package/src/write/abc_ending_element.js +1 -57
- package/src/write/abc_engraver_controller.js +111 -234
- package/src/write/abc_glyphs.js +9 -19
- package/src/write/abc_relative_element.js +57 -97
- package/src/write/abc_renderer.js +10 -832
- package/src/write/abc_spacing.js +0 -15
- package/src/write/abc_staff_group_element.js +14 -349
- package/src/write/abc_tempo_element.js +9 -117
- package/src/write/abc_tie_element.js +5 -68
- package/src/write/abc_triplet_element.js +6 -124
- package/src/write/abc_voice_element.js +7 -222
- package/src/write/add-chord.js +103 -0
- package/src/write/add-text-if.js +33 -0
- package/src/write/bottom-text.js +79 -0
- package/src/write/calcHeight.js +17 -0
- package/src/write/classes.js +100 -0
- package/src/write/draw/absolute.js +68 -0
- package/src/write/draw/beam.js +56 -0
- package/src/write/draw/brace.js +106 -0
- package/src/write/draw/crescendo.js +38 -0
- package/src/write/draw/debug-box.js +8 -0
- package/src/write/draw/draw.js +56 -0
- package/src/write/draw/dynamics.js +20 -0
- package/src/write/draw/ending.js +46 -0
- package/src/write/draw/group-elements.js +66 -0
- package/src/write/draw/horizontal-line.js +25 -0
- package/src/write/draw/non-music.js +50 -0
- package/src/write/draw/print-line.js +24 -0
- package/src/write/draw/print-path.js +7 -0
- package/src/write/draw/print-stem.js +30 -0
- package/src/write/draw/print-symbol.js +59 -0
- package/src/write/draw/print-vertical-line.js +18 -0
- package/src/write/draw/relative.js +77 -0
- package/src/write/draw/round-number.js +5 -0
- package/src/write/draw/selectables.js +59 -0
- package/src/write/draw/separator.js +16 -0
- package/src/write/draw/set-paper-size.js +45 -0
- package/src/write/{sprintf.js → draw/sprintf.js} +0 -0
- package/src/write/draw/staff-group.js +226 -0
- package/src/write/draw/staff-line.js +9 -0
- package/src/write/draw/staff.js +33 -0
- package/src/write/draw/tab-line.js +40 -0
- package/src/write/draw/tempo.js +45 -0
- package/src/write/draw/text.js +71 -0
- package/src/write/draw/tie.js +97 -0
- package/src/write/draw/triplet.js +46 -0
- package/src/write/draw/voice.js +102 -0
- package/src/write/format-jazz-chord.js +15 -0
- package/src/write/free-text.js +41 -0
- package/src/write/get-font-and-attr.js +37 -0
- package/src/write/get-text-size.js +56 -0
- package/src/write/highlight.js +11 -0
- package/src/write/layout/VoiceElements.js +121 -0
- package/src/write/layout/beam.js +213 -0
- package/src/write/layout/get-left-edge-of-staff.js +56 -0
- package/src/write/layout/getBarYAt.js +6 -0
- package/src/write/layout/layout.js +94 -0
- package/src/write/layout/setUpperAndLowerElements.js +232 -0
- package/src/write/layout/staffGroup.js +146 -0
- package/src/write/layout/triplet.js +75 -0
- package/src/write/layout/voice.js +137 -0
- package/src/write/selection.js +188 -70
- package/src/write/separator.js +10 -0
- package/src/write/set-class.js +21 -0
- package/src/write/subtitle.js +12 -0
- package/src/write/svg.js +95 -43
- package/src/write/top-text.js +54 -0
- package/src/write/unhighlight.js +11 -0
- package/temp.txt +17 -0
- package/test.js +27 -64
- package/types/index.d.ts +1095 -0
- package/version.js +1 -1
- package/.babelrc +0 -5
- package/.eslintrc +0 -3
- package/.gitmodules +0 -3
- package/abcjs-midi.css +0 -166
- package/build-utils/loadPresets.js +0 -14
- package/build-utils/presets/webpack.analyze.js +0 -6
- package/build-utils/presets/webpack.optimize.js +0 -30
- package/build-utils/webpack.development.js +0 -14
- package/build-utils/webpack.production.js +0 -35
- package/deploy-docs.sh +0 -25
- package/docs/README.md +0 -33
- package/fix-versions.sh +0 -23
- package/mei.js +0 -43
- package/midi.js +0 -62
- package/src/api/abc_tunebook_midi.js +0 -116
- package/src/midi/abc_midi_controls.js +0 -701
- package/src/midi/abc_midi_flattener.js +0 -1119
- package/src/midi/abc_midi_js_preparer.js +0 -243
- package/src/midi/abc_midi_sequencer.js +0 -401
- package/src/midi/abc_midi_ui_generator.js +0 -86
- package/src/plugin/abc_plugin_midi.js +0 -220
- package/src/synth/images/loop.svg +0 -61
- package/src/synth/images/pause.svg +0 -6
- package/src/synth/images/play.svg +0 -5
- package/src/transform/abc2abc_write.js +0 -395
- package/static-wrappers/basic.js +0 -2
- package/static-wrappers/midi.js +0 -2
- package/static-wrappers/plugin-midi.js +0 -6
- package/static-wrappers/plugin.js +0 -6
- package/webpack.config.js +0 -29
|
@@ -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;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tablature Absolute elements factory
|
|
3
|
+
*/
|
|
4
|
+
var AbsoluteElement = require('../write/abc_absolute_element');
|
|
5
|
+
var RelativeElement = require('../write/abc_relative_element');
|
|
6
|
+
var Transposer = require('./transposer');
|
|
7
|
+
|
|
8
|
+
function isObject(a) { return a != null && a.constructor === Object; }
|
|
9
|
+
function cloneObject(dest, src) {
|
|
10
|
+
for (var prop in src) {
|
|
11
|
+
if (src.hasOwnProperty(prop)) {
|
|
12
|
+
if (!(Array.isArray(src[prop]) || isObject(src[prop]))) {
|
|
13
|
+
dest[prop] = src[prop];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function cloneAbsolute(absSrc) {
|
|
20
|
+
var returned = new AbsoluteElement('', 0, 0, '', 0);
|
|
21
|
+
cloneObject(returned, absSrc);
|
|
22
|
+
returned.top = 0;
|
|
23
|
+
returned.bottom = -1;
|
|
24
|
+
if (absSrc.abcelem) {
|
|
25
|
+
returned.abcelem = {};
|
|
26
|
+
cloneObject(returned.abcelem, absSrc.abcelem);
|
|
27
|
+
if (returned.abcelem.el_type === "note")
|
|
28
|
+
returned.abcelem.el_type = 'tabNumber';
|
|
29
|
+
}
|
|
30
|
+
return returned;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cloneAbsoluteAndRelatives(absSrc, plugin) {
|
|
34
|
+
var returned = cloneAbsolute(absSrc);
|
|
35
|
+
if (plugin) {
|
|
36
|
+
var children = absSrc.children;
|
|
37
|
+
// proceed with relative as well
|
|
38
|
+
var first = true;
|
|
39
|
+
for (var ii = 0; ii < children.length; ii++) {
|
|
40
|
+
var child = children[ii];
|
|
41
|
+
var relative = new RelativeElement('', 0, 0, 0, '');
|
|
42
|
+
cloneObject(relative, child);
|
|
43
|
+
first = plugin.tablature.setRelative(child, relative, first);
|
|
44
|
+
returned.children.push(relative);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return returned;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildTabAbsolute(plugin, absX, relX) {
|
|
51
|
+
var tabIcon = 'tab.tiny';
|
|
52
|
+
var tabYPos = 7.5;
|
|
53
|
+
if (plugin.isTabBig) {
|
|
54
|
+
tabIcon = 'tab.big';
|
|
55
|
+
tabYPos = 10;
|
|
56
|
+
}
|
|
57
|
+
var element = {
|
|
58
|
+
el_type: "tab",
|
|
59
|
+
icon: tabIcon,
|
|
60
|
+
Ypos: tabYPos
|
|
61
|
+
};
|
|
62
|
+
var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
|
|
63
|
+
tabAbsolute.x = absX;
|
|
64
|
+
var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
|
|
65
|
+
tabRelative.x = relX;
|
|
66
|
+
tabAbsolute.children.push(tabRelative);
|
|
67
|
+
if (tabAbsolute.abcelem.el_type == 'tab') {
|
|
68
|
+
tabRelative.pitch = tabYPos;
|
|
69
|
+
}
|
|
70
|
+
return tabAbsolute;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function lyricsDim(abs) {
|
|
74
|
+
if (abs.extra) {
|
|
75
|
+
for (var ii = 0; ii < abs.extra.length; ii++) {
|
|
76
|
+
var extra = abs.extra[ii];
|
|
77
|
+
if (extra.type == 'lyric') {
|
|
78
|
+
return {
|
|
79
|
+
bottom: extra.bottom,
|
|
80
|
+
height: extra.height
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
function TabAbsoluteElements() {
|
|
88
|
+
this.accidentals = null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getInitialStaffSize(staffGroup) {
|
|
92
|
+
var returned = 0;
|
|
93
|
+
for (var ii = 0; ii < staffGroup.length; ii++) {
|
|
94
|
+
if (!staffGroup[ii].tabNameInfos) returned++;
|
|
95
|
+
}
|
|
96
|
+
return returned;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function buildRelativeTabNote(plugin, relX, def, curNote, isGrace) {
|
|
100
|
+
var strNote = curNote.num;
|
|
101
|
+
if (curNote.note.quarter != null) {
|
|
102
|
+
// add tab quarter => needs to string conversion then
|
|
103
|
+
strNote = strNote.toString();
|
|
104
|
+
strNote += curNote.note.quarter;
|
|
105
|
+
}
|
|
106
|
+
var pitch = plugin.semantics.stringToPitch(curNote.str);
|
|
107
|
+
def.notes.push({ num: strNote, str: curNote.str, pitch: curNote.note.emit() });
|
|
108
|
+
var opt = {
|
|
109
|
+
type: 'tabNumber'
|
|
110
|
+
};
|
|
111
|
+
var tabNoteRelative = new RelativeElement(
|
|
112
|
+
strNote, 0, 0, pitch+0.3, opt);
|
|
113
|
+
tabNoteRelative.x = relX;
|
|
114
|
+
tabNoteRelative.isGrace = isGrace;
|
|
115
|
+
tabNoteRelative.isAltered = curNote.note.isAltered;
|
|
116
|
+
return tabNoteRelative;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getXGrace(abs, index) {
|
|
120
|
+
var found = 0;
|
|
121
|
+
if (abs.extra) {
|
|
122
|
+
for (var ii = 0; ii < abs.extra.length; ii++) {
|
|
123
|
+
if (abs.extra[ii].c.indexOf('noteheads') >= 0) {
|
|
124
|
+
if (found === index) {
|
|
125
|
+
return abs.extra[ii].x + abs.extra[ii].w/2;
|
|
126
|
+
} else {
|
|
127
|
+
found++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return -1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function graceInRest( absElem ) {
|
|
136
|
+
if (absElem.abcelem) {
|
|
137
|
+
var elem = absElem.abcelem;
|
|
138
|
+
if (elem.rest) {
|
|
139
|
+
return elem.gracenotes;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function checkTransposition(plugin, transposer ,pitches, graceNotes) {
|
|
146
|
+
if (plugin.transpose) {
|
|
147
|
+
//transposer.transpose(plugin.transpose);
|
|
148
|
+
for (var jj = 0; jj < pitches.length; jj++) {
|
|
149
|
+
pitches[jj] = transposer.transposeNote(pitches[jj]);
|
|
150
|
+
}
|
|
151
|
+
if (graceNotes) {
|
|
152
|
+
for (var kk = 0; kk < graceNotes.length; kk++) {
|
|
153
|
+
graceNotes[kk] = transposer.transposeNote(graceNotes[kk]);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function convertToNumber(plugin, pitches, graceNotes) {
|
|
160
|
+
var tabPos = plugin.semantics.notesToNumber(pitches, graceNotes);
|
|
161
|
+
if (tabPos.error) {
|
|
162
|
+
plugin._super.setError(tabPos.error);
|
|
163
|
+
return tabPos; // give up on error here
|
|
164
|
+
}
|
|
165
|
+
if (tabPos.graces && tabPos.notes) {
|
|
166
|
+
// add graces to last note in notes
|
|
167
|
+
var posNote = tabPos.notes.length - 1;
|
|
168
|
+
tabPos.notes[posNote].graces = tabPos.graces;
|
|
169
|
+
}
|
|
170
|
+
return tabPos;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function buildGraceRelativesForRest(plugin,abs,absChild,graceNotes,tabVoice) {
|
|
174
|
+
for (var mm = 0; mm < graceNotes.length; mm++) {
|
|
175
|
+
var defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true };
|
|
176
|
+
var graceX = getXGrace(absChild, mm);
|
|
177
|
+
var curGrace = graceNotes[mm];
|
|
178
|
+
var tabGraceRelative = buildRelativeTabNote(plugin, graceX, defGrace, curGrace, true);
|
|
179
|
+
abs.children.push(tabGraceRelative);
|
|
180
|
+
tabVoice.push(defGrace);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Build tab absolutes by scanning current staff line absolute array
|
|
186
|
+
* @param {*} staffAbsolute
|
|
187
|
+
*/
|
|
188
|
+
TabAbsoluteElements.prototype.build = function (plugin,
|
|
189
|
+
staffAbsolute,
|
|
190
|
+
tabVoice,
|
|
191
|
+
voiceIndex,
|
|
192
|
+
staffIndex,
|
|
193
|
+
keySig ) {
|
|
194
|
+
var staffSize = getInitialStaffSize(staffAbsolute);
|
|
195
|
+
var source = staffAbsolute[staffIndex+voiceIndex];
|
|
196
|
+
var dest = staffAbsolute[staffSize+staffIndex+voiceIndex];
|
|
197
|
+
var transposer = null;
|
|
198
|
+
var tabPos = null;
|
|
199
|
+
var defNote = null;
|
|
200
|
+
if (source.children[0].abcelem.el_type != 'clef') {
|
|
201
|
+
// keysig missing => provide one for tabs
|
|
202
|
+
if (keySig != 'none') {
|
|
203
|
+
source.children.splice(0, 0, keySig);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for (var ii = 0; ii < source.children.length; ii++) {
|
|
207
|
+
var absChild = source.children[ii];
|
|
208
|
+
var absX = absChild.x;
|
|
209
|
+
var relX = absX;
|
|
210
|
+
// if (absChild.children.length > 0) {
|
|
211
|
+
// relX = absChild.children[0].x;
|
|
212
|
+
// }
|
|
213
|
+
if ( (absChild.isClef) ) {
|
|
214
|
+
dest.children.push(buildTabAbsolute(plugin, absX, relX));
|
|
215
|
+
}
|
|
216
|
+
switch (absChild.type) {
|
|
217
|
+
case 'staff-extra key-signature':
|
|
218
|
+
// refresh key accidentals
|
|
219
|
+
this.accidentals = absChild.abcelem.accidentals;
|
|
220
|
+
plugin.semantics.strings.accidentals = this.accidentals;
|
|
221
|
+
if (plugin.transpose) {
|
|
222
|
+
transposer = new Transposer(
|
|
223
|
+
absChild.abcelem.accidentals,
|
|
224
|
+
plugin.transpose
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
case 'bar':
|
|
229
|
+
var lastBar = false;
|
|
230
|
+
if (ii === source.children.length-1) {
|
|
231
|
+
// used for final line bar drawing
|
|
232
|
+
// for multi tabs / multi staves
|
|
233
|
+
lastBar = true;
|
|
234
|
+
}
|
|
235
|
+
var cloned = cloneAbsoluteAndRelatives(absChild, plugin);
|
|
236
|
+
if (cloned.abcelem.barNumber) {
|
|
237
|
+
delete cloned.abcelem.barNumber;
|
|
238
|
+
for (var bn = 0; bn < cloned.children.length; bn++) {
|
|
239
|
+
if (cloned.children[bn].type === "barNumber" ) {
|
|
240
|
+
cloned.children.splice(bn, 1);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
cloned.abcelem.lastBar = lastBar;
|
|
246
|
+
dest.children.push(cloned);
|
|
247
|
+
tabVoice.push({
|
|
248
|
+
el_type: absChild.abcelem.el_type,
|
|
249
|
+
type: absChild.abcelem.type,
|
|
250
|
+
endChar: absChild.abcelem.endChar,
|
|
251
|
+
startChar: absChild.abcelem.startChar,
|
|
252
|
+
abselem: cloned
|
|
253
|
+
});
|
|
254
|
+
break;
|
|
255
|
+
case 'rest':
|
|
256
|
+
var restGraces = graceInRest(absChild);
|
|
257
|
+
if (restGraces) {
|
|
258
|
+
// check transpose
|
|
259
|
+
checkTransposition(plugin, transposer, null, restGraces);
|
|
260
|
+
// to number conversion
|
|
261
|
+
tabPos = convertToNumber(plugin, null, restGraces);
|
|
262
|
+
if (tabPos.error) return;
|
|
263
|
+
// build relative for grace
|
|
264
|
+
defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true };
|
|
265
|
+
buildGraceRelativesForRest(plugin, abs, absChild, tabPos.graces, tabVoice);
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
case 'note':
|
|
269
|
+
var abs = cloneAbsolute(absChild);
|
|
270
|
+
abs.x = absChild.heads[0].x + absChild.heads[0].w / 2; // center the number
|
|
271
|
+
abs.lyricDim = lyricsDim(absChild);
|
|
272
|
+
var pitches = absChild.abcelem.pitches;
|
|
273
|
+
var graceNotes = absChild.abcelem.gracenotes;
|
|
274
|
+
// check transpose
|
|
275
|
+
abs.type = 'tabNumber';
|
|
276
|
+
checkTransposition(plugin, transposer, pitches, graceNotes);
|
|
277
|
+
// to number conversion
|
|
278
|
+
tabPos = convertToNumber(plugin, pitches, graceNotes);
|
|
279
|
+
if (tabPos.error) return;
|
|
280
|
+
if (tabPos.graces) {
|
|
281
|
+
// add graces to last note in notes
|
|
282
|
+
var posNote = tabPos.notes.length - 1;
|
|
283
|
+
tabPos.notes[posNote].graces = tabPos.graces;
|
|
284
|
+
}
|
|
285
|
+
// build relative
|
|
286
|
+
defNote = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [] };
|
|
287
|
+
for (var ll = 0; ll < tabPos.notes.length; ll++) {
|
|
288
|
+
var curNote = tabPos.notes[ll];
|
|
289
|
+
if (curNote.graces) {
|
|
290
|
+
for (var mm = 0; mm < curNote.graces.length; mm++) {
|
|
291
|
+
var defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true };
|
|
292
|
+
var graceX = getXGrace(absChild , mm);
|
|
293
|
+
var curGrace = curNote.graces[mm];
|
|
294
|
+
var tabGraceRelative = buildRelativeTabNote(plugin, graceX, defGrace, curGrace, true);
|
|
295
|
+
abs.children.push(tabGraceRelative);
|
|
296
|
+
tabVoice.push(defGrace);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
var tabNoteRelative = buildRelativeTabNote(plugin, abs.x+absChild.heads[ll].dx, defNote, curNote, false);
|
|
300
|
+
abs.children.push(tabNoteRelative);
|
|
301
|
+
}
|
|
302
|
+
defNote.abselem = abs;
|
|
303
|
+
tabVoice.push(defNote);
|
|
304
|
+
dest.children.push(abs);
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
module.exports = TabAbsoluteElements;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Common Class/Method available for all instruments
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function TabCommon(abcTune, tuneNumber, params) {
|
|
8
|
+
this.tune = abcTune;
|
|
9
|
+
this.params = params;
|
|
10
|
+
this.tuneNumber = tuneNumber;
|
|
11
|
+
this.inError = false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
TabCommon.prototype.setError = function (error) {
|
|
16
|
+
var tune = this.tune;
|
|
17
|
+
if (error) {
|
|
18
|
+
this.error = error;
|
|
19
|
+
this.inError = true;
|
|
20
|
+
if (tune.warnings) {
|
|
21
|
+
tune.warnings.push(error);
|
|
22
|
+
} else {
|
|
23
|
+
tune.warnings = [error];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
module.exports = TabCommon;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/* eslint-disable no-debugger */
|
|
2
|
+
var VoiceElement = require('../write/abc_voice_element');
|
|
3
|
+
var TabAbsoluteElements = require('./tab-absolute-elements');
|
|
4
|
+
var spacing = require('../write/abc_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(self, dest) {
|
|
36
|
+
var stringSemantics = self.plugin.semantics.strings;
|
|
37
|
+
var controller = self.renderer.controller;
|
|
38
|
+
var textSize = controller.getTextSize;
|
|
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;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Laying out tabs
|
|
50
|
+
* @param {*} renderer
|
|
51
|
+
* @param {*} line
|
|
52
|
+
* @param {*} staffIndex
|
|
53
|
+
* @param {*} tablatureLayout
|
|
54
|
+
*/
|
|
55
|
+
function TabRenderer(plugin, renderer, line, staffIndex) {
|
|
56
|
+
this.renderer = renderer;
|
|
57
|
+
this.plugin = plugin;
|
|
58
|
+
this.line = line;
|
|
59
|
+
this.absolutes = new TabAbsoluteElements();
|
|
60
|
+
this.staffIndex = staffIndex ;
|
|
61
|
+
this.tabStaff = {
|
|
62
|
+
clef: {
|
|
63
|
+
type: 'TAB'
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
this.tabSize = (plugin.linePitch * plugin.nbLines);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function islastTabInStaff(index, staffGroup) {
|
|
70
|
+
if (staffGroup[index].isTabStaff) {
|
|
71
|
+
if (index === staffGroup.length - 1) return true;
|
|
72
|
+
if (staffGroup[index + 1].isTabStaff) {
|
|
73
|
+
return false;
|
|
74
|
+
} else {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getStaffNumbers(staffs) {
|
|
82
|
+
var nbStaffs = 0;
|
|
83
|
+
for (var ii = 0; ii < staffs.length; ii++) {
|
|
84
|
+
if (!staffs[ii].isTabStaff) {
|
|
85
|
+
nbStaffs++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return nbStaffs;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getParentStaffIndex(staffs, index) {
|
|
92
|
+
for (var ii = index; ii >= 0; ii--) {
|
|
93
|
+
if (!staffs[ii].isTabStaff) {
|
|
94
|
+
return ii;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return -1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
function linkStaffAndTabs(staffs) {
|
|
102
|
+
for (var ii = 0; ii < staffs.length; ii++) {
|
|
103
|
+
if (staffs[ii].isTabStaff) {
|
|
104
|
+
// link to parent staff
|
|
105
|
+
var parentIndex = getParentStaffIndex(staffs, ii);
|
|
106
|
+
staffs[ii].hasStaff = staffs[parentIndex];
|
|
107
|
+
if (!staffs[parentIndex].hasTab) staffs[parentIndex].hasTab = [];
|
|
108
|
+
staffs[parentIndex].hasTab.push(staffs[ii]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isMultiVoiceSingleStaff(staffs , parent) {
|
|
114
|
+
if ( getStaffNumbers(staffs) === 1) {
|
|
115
|
+
if (parent.voices.length > 1) return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
function getNextTabPos(self,staffGroup) {
|
|
122
|
+
var tabIndex = self.staffIndex;
|
|
123
|
+
var startIndex = 0;
|
|
124
|
+
var handledVoices = 0;
|
|
125
|
+
var inProgress = true;
|
|
126
|
+
var nbVoices = 0;
|
|
127
|
+
while (inProgress) {
|
|
128
|
+
//for (var ii = 0; ii < staffGroup.length; ii++) {
|
|
129
|
+
if (!staffGroup[startIndex])
|
|
130
|
+
return -1;
|
|
131
|
+
if (!staffGroup[startIndex].isTabStaff) {
|
|
132
|
+
nbVoices = staffGroup[startIndex].voices.length; // get number of staff voices
|
|
133
|
+
}
|
|
134
|
+
if (staffGroup[startIndex].isTabStaff) {
|
|
135
|
+
handledVoices++;
|
|
136
|
+
if (islastTabInStaff(startIndex, staffGroup)) {
|
|
137
|
+
if (handledVoices < nbVoices) return startIndex + 1;
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
handledVoices = 0;
|
|
141
|
+
if (startIndex >= tabIndex) {
|
|
142
|
+
if (startIndex+1 == staffGroup.length) return startIndex +1;
|
|
143
|
+
if (!staffGroup[startIndex + 1].isTabStaff) return startIndex + 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
startIndex++;
|
|
147
|
+
// out of space case
|
|
148
|
+
if (startIndex > staffGroup.length) return -1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function getLastStaff(staffs, lastTab) {
|
|
153
|
+
for (var ii = lastTab; ii >= 0 ; ii-- ) {
|
|
154
|
+
if (!staffs[ii].isTabStaff) {
|
|
155
|
+
return staffs[ii];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function checkVoiceKeySig(voices, ii) {
|
|
162
|
+
var curVoice = voices[ii];
|
|
163
|
+
// on multivoice multistaff only the first voice has key signature
|
|
164
|
+
// folling consecutive do not have one => we should provide the first voice key sig back then
|
|
165
|
+
var elem0 = curVoice.children[0].abcelem;
|
|
166
|
+
if (elem0.el_type === 'clef') return null;
|
|
167
|
+
if (ii == 0) {
|
|
168
|
+
// not found => clef=none case
|
|
169
|
+
return 'none';
|
|
170
|
+
}
|
|
171
|
+
return voices[ii-1].children[0];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
TabRenderer.prototype.doLayout = function () {
|
|
175
|
+
var staffs = this.line.staff;
|
|
176
|
+
if (staffs) {
|
|
177
|
+
// give up on staffline=0 in key
|
|
178
|
+
var firstStaff = staffs[0];
|
|
179
|
+
if (firstStaff) {
|
|
180
|
+
if (firstStaff.clef) {
|
|
181
|
+
if (firstStaff.clef.stafflines == 0) {
|
|
182
|
+
this.plugin._super.setError("No tablatures when stafflines=0");
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
staffs.splice(
|
|
188
|
+
staffs.length, 0,
|
|
189
|
+
this.tabStaff
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
var staffGroup = this.line.staffGroup;
|
|
193
|
+
|
|
194
|
+
var voices = staffGroup.voices;
|
|
195
|
+
var firstVoice = voices[0];
|
|
196
|
+
// take lyrics into account if any
|
|
197
|
+
var lyricsHeight = getLyricHeight(firstVoice);
|
|
198
|
+
var padd = 3;
|
|
199
|
+
var prevIndex = this.staffIndex;
|
|
200
|
+
var previousStaff = staffGroup.staffs[prevIndex];
|
|
201
|
+
var tabTop = previousStaff.top + padd + lyricsHeight;
|
|
202
|
+
if (previousStaff.isTabStaff) {
|
|
203
|
+
tabTop = previousStaff.top;
|
|
204
|
+
}
|
|
205
|
+
var staffGroupInfos = {
|
|
206
|
+
bottom: -1,
|
|
207
|
+
isTabStaff: true,
|
|
208
|
+
specialY: initSpecialY(),
|
|
209
|
+
lines: this.plugin.nbLines,
|
|
210
|
+
linePitch: this.plugin.linePitch,
|
|
211
|
+
dy: 0.15,
|
|
212
|
+
top: tabTop,
|
|
213
|
+
};
|
|
214
|
+
var nextTabPos = getNextTabPos(this,staffGroup.staffs);
|
|
215
|
+
if (nextTabPos === -1)
|
|
216
|
+
return;
|
|
217
|
+
staffGroupInfos.parentIndex = nextTabPos - 1;
|
|
218
|
+
staffGroup.staffs.splice(nextTabPos, 0, staffGroupInfos);
|
|
219
|
+
// staffGroup.staffs.push(staffGroupInfos);
|
|
220
|
+
staffGroup.height += this.tabSize + padd;
|
|
221
|
+
var parentStaff = getLastStaff(staffGroup.staffs, nextTabPos);
|
|
222
|
+
var nbVoices = 1;
|
|
223
|
+
if (isMultiVoiceSingleStaff(staffGroup.staffs,parentStaff)) {
|
|
224
|
+
nbVoices = parentStaff.voices.length;
|
|
225
|
+
}
|
|
226
|
+
// build from staff
|
|
227
|
+
this.tabStaff.voices = [];
|
|
228
|
+
for (var ii = 0; ii < nbVoices; ii++) {
|
|
229
|
+
var tabVoice = new VoiceElement(0, 0);
|
|
230
|
+
var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
|
|
231
|
+
staffGroup.staffs[this.staffIndex].top += nameHeight;
|
|
232
|
+
staffGroup.height += nameHeight * spacing.STEP;
|
|
233
|
+
tabVoice.staff = staffGroupInfos;
|
|
234
|
+
voices.splice(voices.length, 0, tabVoice);
|
|
235
|
+
var keySig = checkVoiceKeySig(voices, ii + this.staffIndex);
|
|
236
|
+
this.tabStaff.voices[ii] = [];
|
|
237
|
+
this.absolutes.build(this.plugin, voices, this.tabStaff.voices[ii], ii , this.staffIndex ,keySig);
|
|
238
|
+
}
|
|
239
|
+
linkStaffAndTabs(staffGroup.staffs); // crossreference tabs and staff
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
module.exports = TabRenderer;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* generic transposer
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
var TabNote = require('./instruments/tab-note');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function buildAccEquiv(acc,note) {
|
|
11
|
+
var equiv = note.getAccidentalEquiv();
|
|
12
|
+
if (acc.note.toUpperCase() == equiv.name.toUpperCase()) {
|
|
13
|
+
equiv.isSharp = false;
|
|
14
|
+
equiv.isFlat = false;
|
|
15
|
+
return equiv;
|
|
16
|
+
}
|
|
17
|
+
return note;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function adjustNoteToKey(acc, note) {
|
|
21
|
+
if (acc.acc == 'sharp') {
|
|
22
|
+
if (note.isFlat) {
|
|
23
|
+
return buildAccEquiv(acc, note);
|
|
24
|
+
} else if (note.isSharp) {
|
|
25
|
+
if (acc.note.toUpperCase() == note.name.toUpperCase()) {
|
|
26
|
+
note.isSharp = false;
|
|
27
|
+
note.isKeySharp = true;
|
|
28
|
+
} else {
|
|
29
|
+
if (acc.note.toUpperCase() == note.name.toUpperCase()) {
|
|
30
|
+
note.natural = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else if (acc.acc == 'flat') {
|
|
35
|
+
if (note.isSharp) {
|
|
36
|
+
return buildAccEquiv(acc, note);
|
|
37
|
+
} else if (note.isFlat) {
|
|
38
|
+
if (acc.note.toUpperCase() == note.name.toUpperCase()) {
|
|
39
|
+
note.isFlat = false;
|
|
40
|
+
note.isKeyFlat = true;
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
if (acc.note.toUpperCase() == note.name.toUpperCase()) {
|
|
44
|
+
note.natural = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return note;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
function replaceNote(self, newNote, start, end) {
|
|
53
|
+
if (self.lastEnd) {
|
|
54
|
+
while (start > self.lastEnd) {
|
|
55
|
+
self.updatedSrc.push(self.abcSrc[self.lastEnd]);
|
|
56
|
+
self.lastEnd++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var nNote = newNote.split('');
|
|
60
|
+
for (var ii = 0; ii < nNote.length; ii++) {
|
|
61
|
+
self.updatedSrc.push(nNote[ii]);
|
|
62
|
+
}
|
|
63
|
+
var curPos = start + ii;
|
|
64
|
+
while (end >= curPos) {
|
|
65
|
+
self.updatedSrc.push(nNote[curPos]);
|
|
66
|
+
curPos++;
|
|
67
|
+
}
|
|
68
|
+
self.lastEnd = end;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
function checkKeys(self, note) {
|
|
73
|
+
var accs = self.transposedKey;
|
|
74
|
+
for (var ii = 0; ii < accs.length; ii++) {
|
|
75
|
+
note = adjustNoteToKey(accs[ii], note);
|
|
76
|
+
}
|
|
77
|
+
return note;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Transposer.prototype.transposeNote = function (note) {
|
|
81
|
+
var returned = note;
|
|
82
|
+
var curNote = new TabNote.TabNote(returned.name);
|
|
83
|
+
if (this.transposeBy > 0) {
|
|
84
|
+
for (var ii = 0; ii < this.transposeBy; ii++) {
|
|
85
|
+
curNote = checkKeys(this,curNote.nextNote());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
} else if ( this.transposeBy < 0) {
|
|
89
|
+
for (var jj = this.transposeBy; jj < 0; jj++) {
|
|
90
|
+
curNote = checkKeys(this,curNote.prevNote());
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
returned.name = curNote.emit();
|
|
94
|
+
return returned;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
Transposer.prototype.upgradeSource = function (note, startChar, endChar ) {
|
|
98
|
+
var n = new TabNote.TabNote(note.name);
|
|
99
|
+
var newNote = n.emit();
|
|
100
|
+
replaceNote(this, newNote, startChar, endChar-1);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function Transposer(transposedKey , transposeBy ) {
|
|
104
|
+
this.transposeBy = transposeBy;
|
|
105
|
+
this.transposedKey = transposedKey;
|
|
106
|
+
this.lastEnd = this.kEnd+1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
module.exports = Transposer;
|