@goplayerjuggler/abc-tools 1.0.21 → 1.0.23
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/package.json +1 -1
- package/src/incipit.js +2 -1
- package/src/manipulator.js +12 -6
- package/src/parse/decode-abc-text.js +201 -0
- package/src/parse/getMetadata.js +45 -41
- package/src/parse/header-parser.js +33 -15
- package/src/sort/contour-sort.js +14 -4
package/package.json
CHANGED
package/src/incipit.js
CHANGED
|
@@ -288,7 +288,8 @@ function getIncipit(data) {
|
|
|
288
288
|
numBars = 2;
|
|
289
289
|
const currentMeter = getMeter(abc);
|
|
290
290
|
const unitLength = getUnitLength(abc);
|
|
291
|
-
if (
|
|
291
|
+
if (!currentMeter) numBars = 2;
|
|
292
|
+
else if (
|
|
292
293
|
(currentMeter[0] === 4 &&
|
|
293
294
|
currentMeter[1] === 4 &&
|
|
294
295
|
unitLength.den === 16) ||
|
package/src/manipulator.js
CHANGED
|
@@ -175,6 +175,8 @@ function hasAnacrucis(abc) {
|
|
|
175
175
|
* @throws {Error} If the current meter doesn't match either smallMeter or largeMeter
|
|
176
176
|
*/
|
|
177
177
|
function toggleMeterDoubling(abc, smallMeter, largeMeter, currentMeter) {
|
|
178
|
+
//if no L: header at all: add one
|
|
179
|
+
if (!/\nL:\s*1\/8/.test(abc)) abc = abc.replace("\nK:", "\nL:1/8\nK:");
|
|
178
180
|
if (!currentMeter) currentMeter = getMeter(abc);
|
|
179
181
|
|
|
180
182
|
const isSmall =
|
|
@@ -649,7 +651,7 @@ function convertStandardReel(
|
|
|
649
651
|
}
|
|
650
652
|
|
|
651
653
|
let result = //toggleMeter_4_4_to_4_2(reel, meter);
|
|
652
|
-
toggleMeterDoubling(reel,
|
|
654
|
+
toggleMeterDoubling(reel, meter, [4, 2], meter);
|
|
653
655
|
if (comment) {
|
|
654
656
|
result = result.replace(/(\nK:)/, `\nN:${comment}$1`);
|
|
655
657
|
}
|
|
@@ -875,10 +877,13 @@ function getFirstBars(
|
|
|
875
877
|
|
|
876
878
|
// Parse ABC
|
|
877
879
|
const parsed = parseAbc(abc, { maxBars: estimatedMaxBars });
|
|
878
|
-
const { bars, headerLines, barLines, musicText
|
|
880
|
+
const { bars, headerLines, barLines, musicText } = parsed;
|
|
881
|
+
let { meter } = parsed;
|
|
882
|
+
if (!meter) meter = [10000, 1]; //hack(?) to handle incipits in meterless music
|
|
879
883
|
|
|
884
|
+
let stopAfterDuration = null;
|
|
880
885
|
if (bars.length === 0 || barLines.length === 0) {
|
|
881
|
-
|
|
886
|
+
stopAfterDuration = new Fraction(3, 2);
|
|
882
887
|
}
|
|
883
888
|
|
|
884
889
|
// Determine which bar number to stop after
|
|
@@ -910,7 +915,8 @@ function getFirstBars(
|
|
|
910
915
|
|
|
911
916
|
// Calculate the expected duration per musical bar
|
|
912
917
|
const expectedBarDuration = new Fraction(meter[0], meter[1]);
|
|
913
|
-
const targetDuration =
|
|
918
|
+
const targetDuration =
|
|
919
|
+
stopAfterDuration ?? expectedBarDuration.multiply(numBarsFraction);
|
|
914
920
|
|
|
915
921
|
// Determine starting position and how much duration we need to accumulate
|
|
916
922
|
let startPos = 0;
|
|
@@ -1070,8 +1076,8 @@ function canDoubleBarLength(abc) {
|
|
|
1070
1076
|
!abc.match(/\[L:/) &&
|
|
1071
1077
|
(((rhythm === "reel" || rhythm === "hornpipe") &&
|
|
1072
1078
|
l.equals(new Fraction(1, 8)) &&
|
|
1073
|
-
meter[0] === 4 &&
|
|
1074
|
-
|
|
1079
|
+
((meter[0] === 4 && meter[1] === 4) ||
|
|
1080
|
+
(meter[0] === 2 && meter[1] === 2))) ||
|
|
1075
1081
|
(rhythm === "jig" && meter[0] === 6 && meter[1] === 8))) ||
|
|
1076
1082
|
(rhythm === "polka" && meter[0] === 2 && meter[1] === 4)
|
|
1077
1083
|
);
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps 2-char ABC mnemonic sequences (ABC 2.1 §2.3) to Unicode characters.
|
|
3
|
+
* Keys are the two characters following the backslash.
|
|
4
|
+
*/
|
|
5
|
+
const MNEMONIC_MAP = {
|
|
6
|
+
// Grave: \`X
|
|
7
|
+
"`A": "À",
|
|
8
|
+
"`a": "à",
|
|
9
|
+
"`E": "È",
|
|
10
|
+
"`e": "è",
|
|
11
|
+
"`I": "Ì",
|
|
12
|
+
"`i": "ì",
|
|
13
|
+
"`O": "Ò",
|
|
14
|
+
"`o": "ò",
|
|
15
|
+
"`U": "Ù",
|
|
16
|
+
"`u": "ù",
|
|
17
|
+
// Acute: \'X
|
|
18
|
+
"'A": "Á",
|
|
19
|
+
"'a": "á",
|
|
20
|
+
"'E": "É",
|
|
21
|
+
"'e": "é",
|
|
22
|
+
"'I": "Í",
|
|
23
|
+
"'i": "í",
|
|
24
|
+
"'O": "Ó",
|
|
25
|
+
"'o": "ó",
|
|
26
|
+
"'U": "Ú",
|
|
27
|
+
"'u": "ú",
|
|
28
|
+
"'Y": "Ý",
|
|
29
|
+
"'y": "ý",
|
|
30
|
+
// Circumflex: \^X
|
|
31
|
+
"^A": "Â",
|
|
32
|
+
"^a": "â",
|
|
33
|
+
"^E": "Ê",
|
|
34
|
+
"^e": "ê",
|
|
35
|
+
"^I": "Î",
|
|
36
|
+
"^i": "î",
|
|
37
|
+
"^O": "Ô",
|
|
38
|
+
"^o": "ô",
|
|
39
|
+
"^U": "Û",
|
|
40
|
+
"^u": "û",
|
|
41
|
+
// Tilde: \~X
|
|
42
|
+
"~A": "Ã",
|
|
43
|
+
"~a": "ã",
|
|
44
|
+
"~N": "Ñ",
|
|
45
|
+
"~n": "ñ",
|
|
46
|
+
"~O": "Õ",
|
|
47
|
+
"~o": "õ",
|
|
48
|
+
// Umlaut: \"X
|
|
49
|
+
'"A': "Ä",
|
|
50
|
+
'"a': "ä",
|
|
51
|
+
'"E': "Ë",
|
|
52
|
+
'"e': "ë",
|
|
53
|
+
'"I': "Ï",
|
|
54
|
+
'"i': "ï",
|
|
55
|
+
'"O': "Ö",
|
|
56
|
+
'"o': "ö",
|
|
57
|
+
'"U': "Ü",
|
|
58
|
+
'"u': "ü",
|
|
59
|
+
'"Y': "Ÿ",
|
|
60
|
+
'"y': "ÿ",
|
|
61
|
+
// Cedilla, ring, slash
|
|
62
|
+
cC: "Ç",
|
|
63
|
+
cc: "ç",
|
|
64
|
+
AA: "Å",
|
|
65
|
+
aa: "å",
|
|
66
|
+
"/O": "Ø",
|
|
67
|
+
"/o": "ø",
|
|
68
|
+
// Breve: \uX (note: \uXXXX hex escapes are resolved before this map is applied)
|
|
69
|
+
uA: "Ă",
|
|
70
|
+
ua: "ă",
|
|
71
|
+
uE: "Ĕ",
|
|
72
|
+
ue: "ĕ",
|
|
73
|
+
// Caron, double acute
|
|
74
|
+
vS: "Š",
|
|
75
|
+
vs: "š",
|
|
76
|
+
vZ: "Ž",
|
|
77
|
+
vz: "ž",
|
|
78
|
+
HO: "Ő",
|
|
79
|
+
Ho: "ő",
|
|
80
|
+
HU: "Ű",
|
|
81
|
+
Hu: "ű",
|
|
82
|
+
// Ligatures
|
|
83
|
+
ss: "ß",
|
|
84
|
+
AE: "Æ",
|
|
85
|
+
ae: "æ",
|
|
86
|
+
oe: "œ"
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** Named HTML entities for common European characters. */
|
|
90
|
+
const HTML_ENTITY_MAP = {
|
|
91
|
+
amp: "&",
|
|
92
|
+
lt: "<",
|
|
93
|
+
gt: ">",
|
|
94
|
+
quot: '"',
|
|
95
|
+
apos: "'",
|
|
96
|
+
nbsp: "\u00A0",
|
|
97
|
+
Agrave: "À",
|
|
98
|
+
agrave: "à",
|
|
99
|
+
Aacute: "Á",
|
|
100
|
+
aacute: "á",
|
|
101
|
+
Acirc: "Â",
|
|
102
|
+
acirc: "â",
|
|
103
|
+
Atilde: "Ã",
|
|
104
|
+
atilde: "ã",
|
|
105
|
+
Auml: "Ä",
|
|
106
|
+
auml: "ä",
|
|
107
|
+
Aring: "Å",
|
|
108
|
+
aring: "å",
|
|
109
|
+
AElig: "Æ",
|
|
110
|
+
aelig: "æ",
|
|
111
|
+
Ccedil: "Ç",
|
|
112
|
+
ccedil: "ç",
|
|
113
|
+
Egrave: "È",
|
|
114
|
+
egrave: "è",
|
|
115
|
+
Eacute: "É",
|
|
116
|
+
eacute: "é",
|
|
117
|
+
Ecirc: "Ê",
|
|
118
|
+
ecirc: "ê",
|
|
119
|
+
Euml: "Ë",
|
|
120
|
+
euml: "ë",
|
|
121
|
+
Igrave: "Ì",
|
|
122
|
+
igrave: "ì",
|
|
123
|
+
Iacute: "Í",
|
|
124
|
+
iacute: "í",
|
|
125
|
+
Icirc: "Î",
|
|
126
|
+
icirc: "î",
|
|
127
|
+
Iuml: "Ï",
|
|
128
|
+
iuml: "ï",
|
|
129
|
+
Ntilde: "Ñ",
|
|
130
|
+
ntilde: "ñ",
|
|
131
|
+
Ograve: "Ò",
|
|
132
|
+
ograve: "ò",
|
|
133
|
+
Oacute: "Ó",
|
|
134
|
+
oacute: "ó",
|
|
135
|
+
Ocirc: "Ô",
|
|
136
|
+
ocirc: "ô",
|
|
137
|
+
Otilde: "Õ",
|
|
138
|
+
otilde: "õ",
|
|
139
|
+
Ouml: "Ö",
|
|
140
|
+
ouml: "ö",
|
|
141
|
+
Oslash: "Ø",
|
|
142
|
+
oslash: "ø",
|
|
143
|
+
Ugrave: "Ù",
|
|
144
|
+
ugrave: "ù",
|
|
145
|
+
Uacute: "Ú",
|
|
146
|
+
uacute: "ú",
|
|
147
|
+
Ucirc: "Û",
|
|
148
|
+
ucirc: "û",
|
|
149
|
+
Uuml: "Ü",
|
|
150
|
+
uuml: "ü",
|
|
151
|
+
Yacute: "Ý",
|
|
152
|
+
yacute: "ý",
|
|
153
|
+
Yuml: "Ÿ",
|
|
154
|
+
szlig: "ß",
|
|
155
|
+
OElig: "Œ",
|
|
156
|
+
oelig: "œ"
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Strips an ABC inline % comment (a % not preceded by \) and trims trailing whitespace.
|
|
161
|
+
* @param {string} str
|
|
162
|
+
* @returns {string}
|
|
163
|
+
*/
|
|
164
|
+
function stripComment(str) {
|
|
165
|
+
return str.replace(/(?<!\\)%.*/, "").trimEnd();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Decodes ABC 2.1 text-string escapes (§2.3) into Unicode.
|
|
170
|
+
* Processing order:
|
|
171
|
+
* 1. Protect \\ with a placeholder
|
|
172
|
+
* 2. \uXXXX fixed-width unicode (must precede breve \uX mnemonic)
|
|
173
|
+
* 3. Protect \% and \& control escapes
|
|
174
|
+
* 4–6. HTML named / decimal / hex entities
|
|
175
|
+
* 7. Curly-brace mnemonic variants: {mnem} or {\mnem}
|
|
176
|
+
* 8. Backslash mnemonics: \XX
|
|
177
|
+
* 9. Restore placeholders
|
|
178
|
+
* @param {string} str - Header value, already comment-stripped
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
function decodeABCText(str) {
|
|
182
|
+
return str
|
|
183
|
+
.replace(/\\\\/g, "\x00")
|
|
184
|
+
.replace(/\\u([0-9a-fA-F]{4})/g, (_, h) =>
|
|
185
|
+
String.fromCodePoint(parseInt(h, 16))
|
|
186
|
+
)
|
|
187
|
+
.replace(/\\%/g, "\x01")
|
|
188
|
+
.replace(/\\&/g, "\x02")
|
|
189
|
+
.replace(/&([a-zA-Z]+);/g, (m, n) => HTML_ENTITY_MAP[n] ?? m)
|
|
190
|
+
.replace(/&#x([0-9a-fA-F]+);/gi, (_, h) =>
|
|
191
|
+
String.fromCodePoint(parseInt(h, 16))
|
|
192
|
+
)
|
|
193
|
+
.replace(/&#([0-9]+);/g, (_, n) => String.fromCodePoint(+n))
|
|
194
|
+
.replace(/\{\\?([^}{]{2})\}/g, (m, k) => MNEMONIC_MAP[k] ?? m)
|
|
195
|
+
.replace(/\\(..)/g, (m, k) => MNEMONIC_MAP[k] ?? m)
|
|
196
|
+
.replaceAll("\x00", "\\")
|
|
197
|
+
.replaceAll("\x01", "%")
|
|
198
|
+
.replaceAll("\x02", "&");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = { decodeABCText, stripComment };
|
package/src/parse/getMetadata.js
CHANGED
|
@@ -1,71 +1,75 @@
|
|
|
1
1
|
const { normaliseKey } = require("../manipulator");
|
|
2
|
+
const { decodeABCText, stripComment } = require("./decode-abc-text");
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
* Extracts data in the ABC _header_ T R C M K S F D N H fields
|
|
5
|
-
* and returns it in
|
|
6
|
-
* source, url, recording, comments, and hComments.
|
|
7
|
-
*
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @param {
|
|
15
|
-
* @returns {object}
|
|
5
|
+
* Extracts data in the ABC _header_ T R C M K S O F D N H fields
|
|
6
|
+
* and returns it in an object with properties: title, rhythm, composer, meter, key,
|
|
7
|
+
* source, origin, url, recording, comments, and hComments.
|
|
8
|
+
* - Only the first T title is stored in `title`; subsequent ones go in `titles`
|
|
9
|
+
* - The key is normalised: C, Cmaj, C maj, C major all map to "C major"
|
|
10
|
+
* - N: lines accumulate in a `comments` array
|
|
11
|
+
* - H: lines (and +: continuations) are joined with spaces into `hComments`
|
|
12
|
+
* - ABC text escapes (mnemonics, entities, etc.) are decoded by default
|
|
13
|
+
* @param {string} abc
|
|
14
|
+
* @param {object} [options]
|
|
15
|
+
* @param {boolean} [options.decode=true] - Decode ABC text escapes; pass false for raw speed
|
|
16
|
+
* @returns {object}
|
|
16
17
|
*/
|
|
17
|
-
function getMetadata(abc) {
|
|
18
|
+
function getMetadata(abc, { decode = true } = {}) {
|
|
18
19
|
const lines = abc.split("\n"),
|
|
19
20
|
metadata = {},
|
|
20
21
|
comments = [],
|
|
21
22
|
hComments = [],
|
|
22
23
|
titles = [];
|
|
23
24
|
|
|
25
|
+
const process = decode
|
|
26
|
+
? (raw) => decodeABCText(stripComment(raw))
|
|
27
|
+
: (raw) => stripComment(raw);
|
|
28
|
+
|
|
24
29
|
let currentHeader = "";
|
|
25
30
|
|
|
26
31
|
for (const line of lines) {
|
|
27
32
|
const trimmed = line.trim();
|
|
28
|
-
|
|
33
|
+
if (!trimmed || trimmed[0] === "%") continue;
|
|
34
|
+
|
|
35
|
+
if (trimmed.startsWith("K:")) {
|
|
36
|
+
metadata.key = normaliseKey(
|
|
37
|
+
stripComment(trimmed.substring(2).trim())
|
|
38
|
+
).join(" ");
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const val = process(trimmed.substring(2).trim());
|
|
43
|
+
|
|
29
44
|
if (trimmed.startsWith("T:")) {
|
|
30
|
-
if (!metadata.title) metadata.title =
|
|
31
|
-
else titles.push(
|
|
45
|
+
if (!metadata.title) metadata.title = val;
|
|
46
|
+
else titles.push(val);
|
|
32
47
|
} else if (trimmed.startsWith("R:")) {
|
|
33
|
-
metadata.rhythm =
|
|
48
|
+
metadata.rhythm = val.toLowerCase();
|
|
34
49
|
} else if (trimmed.startsWith("C:")) {
|
|
35
|
-
metadata.composer =
|
|
50
|
+
metadata.composer = val;
|
|
36
51
|
} else if (trimmed.startsWith("M:")) {
|
|
37
|
-
metadata.meter =
|
|
38
|
-
} else if (trimmed.startsWith("K:")) {
|
|
39
|
-
metadata.key = normaliseKey(trimmed2).join(" ");
|
|
40
|
-
// metadata.indexOfKey = i
|
|
41
|
-
break;
|
|
52
|
+
metadata.meter = val;
|
|
42
53
|
} else if (trimmed.startsWith("S:")) {
|
|
43
|
-
metadata.source =
|
|
54
|
+
metadata.source = val;
|
|
44
55
|
} else if (trimmed.startsWith("O:")) {
|
|
45
|
-
metadata.origin =
|
|
56
|
+
metadata.origin = val;
|
|
46
57
|
} else if (trimmed.startsWith("F:")) {
|
|
47
|
-
metadata.url =
|
|
58
|
+
metadata.url = val;
|
|
48
59
|
} else if (trimmed.startsWith("D:")) {
|
|
49
|
-
metadata.recording =
|
|
60
|
+
metadata.recording = val;
|
|
50
61
|
} else if (trimmed.startsWith("N:")) {
|
|
51
|
-
comments.push(
|
|
62
|
+
comments.push(val);
|
|
52
63
|
} else if (trimmed.startsWith("H:")) {
|
|
53
64
|
currentHeader = "H";
|
|
54
|
-
hComments.push(
|
|
55
|
-
} else if (trimmed.startsWith("+:")) {
|
|
56
|
-
|
|
57
|
-
case "H":
|
|
58
|
-
hComments.push(trimmed2);
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
65
|
+
hComments.push(val);
|
|
66
|
+
} else if (trimmed.startsWith("+:") && currentHeader === "H") {
|
|
67
|
+
hComments.push(val);
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (hComments.length > 0) {
|
|
67
|
-
metadata.hComments = hComments.join(" ");
|
|
68
|
-
}
|
|
70
|
+
|
|
71
|
+
if (comments.length > 0) metadata.comments = comments;
|
|
72
|
+
if (hComments.length > 0) metadata.hComments = hComments.join(" ");
|
|
69
73
|
if (titles.length > 0) metadata.titles = titles;
|
|
70
74
|
|
|
71
75
|
return metadata;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { Fraction } = require("../math.js");
|
|
2
|
+
const { decodeABCText, stripComment } = require("./decode-abc-text");
|
|
2
3
|
|
|
3
4
|
// ============================================================================
|
|
4
5
|
// ABC HEADER PARSING
|
|
@@ -12,6 +13,37 @@ const { Fraction } = require("../math.js");
|
|
|
12
13
|
//
|
|
13
14
|
// ============================================================================
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Returns all values for a given header letter in the ABC string, in document order.
|
|
18
|
+
* @param {string} abc
|
|
19
|
+
* @param {string} header - Single header letter, e.g. 'T'
|
|
20
|
+
* @param {object} [options]
|
|
21
|
+
* @param {boolean} [options.decode=true] - Decode ABC text escapes; pass false for raw values
|
|
22
|
+
* @returns {string[]}
|
|
23
|
+
*/
|
|
24
|
+
function getHeaderValues(abc, header, { decode = true } = {}) {
|
|
25
|
+
const re = new RegExp(`^${header}:[ \\t]*(.*)$`, "gm");
|
|
26
|
+
const results = [];
|
|
27
|
+
for (const m of abc.matchAll(re)) {
|
|
28
|
+
const val = decode ? decodeABCText(stripComment(m[1])) : stripComment(m[1]);
|
|
29
|
+
if (val) results.push(val);
|
|
30
|
+
}
|
|
31
|
+
return results;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** @deprecated Use `getHeaderValues(abc, header)[0] ?? null` instead. */
|
|
35
|
+
function getHeaderValue(abc, header, options) {
|
|
36
|
+
return getHeaderValues(abc, header, options)[0] ?? null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @deprecated Use `getHeaderValues(abc, 'T')` instead.
|
|
41
|
+
* Note: unlike the previous implementation, this now returns decoded strings, not match objects.
|
|
42
|
+
*/
|
|
43
|
+
function getTitles(abc) {
|
|
44
|
+
return getHeaderValues(abc, "T");
|
|
45
|
+
}
|
|
46
|
+
|
|
15
47
|
/**
|
|
16
48
|
* Extract base note of key signature from ABC header
|
|
17
49
|
*
|
|
@@ -70,21 +102,6 @@ function getUnitLength(abc) {
|
|
|
70
102
|
}
|
|
71
103
|
return new Fraction(1, 8); // Default to 1/8
|
|
72
104
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Extract titles - there may be 0..N titles
|
|
75
|
-
*
|
|
76
|
-
* @param {string} abc - ABC notation string
|
|
77
|
-
* @returns {[string]} - array of titles
|
|
78
|
-
*/
|
|
79
|
-
function getTitles(abc) {
|
|
80
|
-
return [...abc.matchAll(/^(?:T:\s*(.+)\n)/gm)];
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function getHeaderValue(abc, header) {
|
|
84
|
-
const r = new RegExp(String.raw`(?:${header}:\s*(.+)\n)`, "m"),
|
|
85
|
-
m = abc.match(r);
|
|
86
|
-
return m ? m[1]?.trim() : null;
|
|
87
|
-
}
|
|
88
105
|
|
|
89
106
|
/**
|
|
90
107
|
* Process ABC lines: extract music lines with metadata
|
|
@@ -167,6 +184,7 @@ function getMusicLines(abc) {
|
|
|
167
184
|
|
|
168
185
|
module.exports = {
|
|
169
186
|
getHeaderValue,
|
|
187
|
+
getHeaderValues,
|
|
170
188
|
getKey,
|
|
171
189
|
getMeter,
|
|
172
190
|
getMusicLines,
|
package/src/sort/contour-sort.js
CHANGED
|
@@ -204,8 +204,10 @@ function sort(arr, options = {}) {
|
|
|
204
204
|
],
|
|
205
205
|
applySwingTransform = ["hornpipe", "barndance", "fling", "mazurka"],
|
|
206
206
|
getAbc: getAbcForContour = getAbcForContour_default,
|
|
207
|
-
getContourOptions = {
|
|
208
|
-
|
|
207
|
+
getContourOptions = () => {
|
|
208
|
+
return {
|
|
209
|
+
withSvg: true
|
|
210
|
+
};
|
|
209
211
|
}
|
|
210
212
|
} = options;
|
|
211
213
|
|
|
@@ -223,13 +225,21 @@ function sort(arr, options = {}) {
|
|
|
223
225
|
for (const tune of arr) {
|
|
224
226
|
if (!tune.contour) {
|
|
225
227
|
try {
|
|
228
|
+
const contourOptions = getContourOptions();
|
|
229
|
+
// if (
|
|
230
|
+
// tune.title?.indexOf("oldrick") >= 0 ||
|
|
231
|
+
// ) {
|
|
232
|
+
// console.log("debug");
|
|
233
|
+
// }
|
|
226
234
|
const withSwingTransform =
|
|
227
235
|
applySwingTransform.indexOf(tune.rhythm) >= 0;
|
|
228
236
|
const shortAbc = getAbcForContour(tune);
|
|
229
237
|
|
|
230
238
|
if (shortAbc) {
|
|
231
|
-
|
|
232
|
-
|
|
239
|
+
contourOptions.withSwingTransform = withSwingTransform;
|
|
240
|
+
if (Object.hasOwn(tune, "contourShift"))
|
|
241
|
+
contourOptions.contourShift = tune.contourShift;
|
|
242
|
+
tune.contour = getContour(shortAbc, contourOptions);
|
|
233
243
|
}
|
|
234
244
|
} catch (error) {
|
|
235
245
|
console.log(error);
|